Skip to content

Commit 702b14a

Browse files
committed
Use extents table for ms sql layers for faster loading
- Also move feature count logic
1 parent df398a1 commit 702b14a

File tree

1 file changed

+82
-57
lines changed

1 file changed

+82
-57
lines changed

src/providers/mssql/qgsmssqlprovider.cpp

Lines changed: 82 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -568,52 +568,55 @@ void QgsMssqlProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, i
568568
}
569569

570570

571-
// update the extent, feature count, wkb type and srid for this layer
571+
// update the extent, wkb type and srid for this layer
572572
void QgsMssqlProvider::UpdateStatistics( bool estimate )
573573
{
574-
mNumberFeatures = 0;
575574
// get features to calculate the statistics
576575
QString statement;
577576

578577
QSqlQuery query = QSqlQuery( mDatabase );
579578
query.setForwardOnly( true );
580579

581-
// Get the extents from the geometry_columns table to speed up load times.
582-
statement = QString( "SELECT min_x, min_y, max_x, max_y from geometry_columns where f_table_schema = '%1' and f_table_name = '%2'" ).arg( mSchemaName ).arg( mTableName );
580+
// Get the extents from the spatial index table to speed up load times.
581+
// We have to use max() and min() because you can have more then one index but the biggest area is what we want to use.
582+
QString sql = "SELECT min(bounding_box_xmin), min(bounding_box_ymin), max(bounding_box_xmax), max(bounding_box_ymax)"
583+
" FROM sys.spatial_index_tessellations WHERE object_id = OBJECT_ID('[%1].[%2]')";
584+
585+
statement = QString (sql).arg( mSchemaName )
586+
.arg( mTableName );
583587

584588
if ( query.exec( statement ) )
585589
{
590+
QgsDebugMsg("Found extents in spatial index");
586591
if ( query.next() )
587592
{
588-
if ( !query.value( 0 ).isNull() || !query.value( 1 ).isNull() ||
589-
!query.value( 2 ).isNull() || !query.value( 3 ).isNull() )
590-
{
591593
mExtent.setXMinimum( query.value( 0 ).toDouble() );
592594
mExtent.setYMinimum( query.value( 1 ).toDouble() );
593595
mExtent.setXMaximum( query.value( 2 ).toDouble() );
594596
mExtent.setYMaximum( query.value( 3 ).toDouble() );
595597
return;
596-
}
597598
}
598599
}
599600

600-
// If we can't find the extents in the geometry_columns table just do what we normally do.
601-
bool readAll = false;
601+
QgsDebugMsg(query.lastError().text());
602+
603+
// If we can't find the extents in the spatial index table just do what we normally do.
604+
bool readAllGeography = false;
602605
if ( estimate )
603606
{
604607
if ( mGeometryColType == "geometry" )
605-
statement = QString( "select min([%1].STPointN(1).STX), min([%1].STPointN(1).STY), max([%1].STPointN(1).STX), max([%1].STPointN(1).STY), COUNT([%1])" ).arg( mGeometryColName );
608+
statement = QString( "select min([%1].STPointN(1).STX), min([%1].STPointN(1).STY), max([%1].STPointN(1).STX), max([%1].STPointN(1).STY)" ).arg( mGeometryColName );
606609
else
607-
statement = QString( "select min([%1].STPointN(1).Long), min([%1].STPointN(1).Lat), max([%1].STPointN(1).Long), max([%1].STPointN(1).Lat), COUNT([%1])" ).arg( mGeometryColName );
610+
statement = QString( "select min([%1].STPointN(1).Long), min([%1].STPointN(1).Lat), max([%1].STPointN(1).Long), max([%1].STPointN(1).Lat)" ).arg( mGeometryColName );
608611
}
609612
else
610613
{
611614
if ( mGeometryColType == "geometry" )
612-
statement = QString( "select min([%1].STEnvelope().STPointN(1).STX), min([%1].STEnvelope().STPointN(1).STY), max([%1].STEnvelope().STPointN(3).STX), max([%1].STEnvelope().STPointN(3).STY), count([%1])" ).arg( mGeometryColName );
615+
statement = QString( "select min([%1].STEnvelope().STPointN(1).STX), min([%1].STEnvelope().STPointN(1).STY), max([%1].STEnvelope().STPointN(3).STX), max([%1].STEnvelope().STPointN(3).STY)" ).arg( mGeometryColName );
613616
else
614617
{
615618
statement = QString( "select [%1]" ).arg( mGeometryColName );
616-
readAll = true;
619+
readAllGeography = true;
617620
}
618621
}
619622

@@ -633,52 +636,45 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate )
633636
QgsDebugMsg( msg );
634637
}
635638

636-
if ( query.isActive() )
639+
if ( !query.isActive() )
640+
{
641+
return;
642+
}
643+
644+
QgsGeometry geom;
645+
if ( !readAllGeography )
637646
{
638-
QgsGeometry geom;
639-
if ( !readAll )
647+
if ( query.next() )
640648
{
641-
if ( query.next() )
642-
{
643-
mExtent.setXMinimum( query.value( 0 ).toDouble() );
644-
mExtent.setYMinimum( query.value( 1 ).toDouble() );
645-
mExtent.setXMaximum( query.value( 2 ).toDouble() );
646-
mExtent.setYMaximum( query.value( 3 ).toDouble() );
647-
mNumberFeatures = query.value( 4 ).toInt();
648-
}
649+
mExtent.setXMinimum( query.value( 0 ).toDouble() );
650+
mExtent.setYMinimum( query.value( 1 ).toDouble() );
651+
mExtent.setXMaximum( query.value( 2 ).toDouble() );
652+
mExtent.setYMaximum( query.value( 3 ).toDouble() );
653+
return;
649654
}
650-
else
651-
{
652-
// read all features
653-
while ( query.next() )
654-
{
655-
QByteArray ar = query.value( 0 ).toByteArray();
656-
unsigned char* wkb = parser.ParseSqlGeometry(( unsigned char* )ar.data(), ar.size() );
657-
if ( wkb )
658-
{
659-
geom.fromWkb( wkb, parser.GetWkbLen() );
660-
QgsRectangle rect = geom.boundingBox();
655+
}
661656

662-
if ( mNumberFeatures > 0 )
663-
{
664-
if ( rect.xMinimum() < mExtent.xMinimum() )
665-
mExtent.setXMinimum( rect.xMinimum() );
666-
if ( rect.yMinimum() < mExtent.yMinimum() )
667-
mExtent.setYMinimum( rect.yMinimum() );
668-
if ( rect.xMaximum() > mExtent.xMaximum() )
669-
mExtent.setXMaximum( rect.xMaximum() );
670-
if ( rect.yMaximum() > mExtent.yMaximum() )
671-
mExtent.setYMaximum( rect.yMaximum() );
672-
}
673-
else
674-
{
675-
mExtent = rect;
676-
mWkbType = geom.wkbType();
677-
mSRId = parser.GetSRSId();
678-
}
679-
++mNumberFeatures;
680-
}
681-
}
657+
// We have to read all the geometry if readAllGeography is true.
658+
while ( query.next() )
659+
{
660+
QByteArray ar = query.value( 0 ).toByteArray();
661+
unsigned char* wkb = parser.ParseSqlGeometry(( unsigned char* )ar.data(), ar.size() );
662+
if ( wkb )
663+
{
664+
geom.fromWkb( wkb, parser.GetWkbLen() );
665+
QgsRectangle rect = geom.boundingBox();
666+
667+
if ( rect.xMinimum() < mExtent.xMinimum() )
668+
mExtent.setXMinimum( rect.xMinimum() );
669+
if ( rect.yMinimum() < mExtent.yMinimum() )
670+
mExtent.setYMinimum( rect.yMinimum() );
671+
if ( rect.xMaximum() > mExtent.xMaximum() )
672+
mExtent.setXMaximum( rect.xMaximum() );
673+
if ( rect.yMaximum() > mExtent.yMaximum() )
674+
mExtent.setYMaximum( rect.yMaximum() );
675+
676+
mWkbType = geom.wkbType();
677+
mSRId = parser.GetSRSId();
682678
}
683679
}
684680
}
@@ -704,7 +700,36 @@ QGis::WkbType QgsMssqlProvider::geometryType() const
704700
*/
705701
long QgsMssqlProvider::featureCount() const
706702
{
707-
return mNumberFeatures;
703+
// Return the count that we get from the subset.
704+
if ( !mSqlWhereClause.isEmpty() )
705+
return mNumberFeatures;
706+
707+
// If there is no subset set we can get the count from the system tables.
708+
// Which is faster then doing select count(*)
709+
QSqlQuery query = QSqlQuery( mDatabase );
710+
query.setForwardOnly( true );
711+
712+
QString sql = "SELECT rows"
713+
" FROM sys.tables t"
714+
" JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1)"
715+
" WHERE SCHEMA_NAME(t.schema_id) = '%1' AND OBJECT_NAME(t.OBJECT_ID) = '%2'";
716+
717+
QString statement = QString (sql).arg( mSchemaName )
718+
.arg( mTableName );
719+
720+
if ( query.exec( statement ) )
721+
{
722+
if ( query.next() )
723+
{
724+
return query.value(0).toInt();
725+
}
726+
}
727+
else
728+
{
729+
// We couldn't get the rows from the sys tables. Can that ever happen?
730+
// Should just do a select count(*) here.
731+
return -1;
732+
}
708733
}
709734

710735
const QgsFields & QgsMssqlProvider::fields() const

0 commit comments

Comments
 (0)