Skip to content
Permalink
Browse files

Use extents table for ms sql layers for faster loading

- Also move feature count logic
  • Loading branch information
NathanW2 committed Jul 2, 2013
1 parent df398a1 commit 702b14a1beed830c0cedac0dced811076f95f25f
Showing with 82 additions and 57 deletions.
  1. +82 −57 src/providers/mssql/qgsmssqlprovider.cpp
@@ -568,52 +568,55 @@ void QgsMssqlProvider::uniqueValues( int index, QList<QVariant> &uniqueValues, i
}


// update the extent, feature count, wkb type and srid for this layer
// update the extent, wkb type and srid for this layer
void QgsMssqlProvider::UpdateStatistics( bool estimate )
{
mNumberFeatures = 0;
// get features to calculate the statistics
QString statement;

QSqlQuery query = QSqlQuery( mDatabase );
query.setForwardOnly( true );

// Get the extents from the geometry_columns table to speed up load times.
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 );
// Get the extents from the spatial index table to speed up load times.
// 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.
QString sql = "SELECT min(bounding_box_xmin), min(bounding_box_ymin), max(bounding_box_xmax), max(bounding_box_ymax)"
" FROM sys.spatial_index_tessellations WHERE object_id = OBJECT_ID('[%1].[%2]')";

statement = QString (sql).arg( mSchemaName )
.arg( mTableName );

if ( query.exec( statement ) )
{
QgsDebugMsg("Found extents in spatial index");
if ( query.next() )
{
if ( !query.value( 0 ).isNull() || !query.value( 1 ).isNull() ||
!query.value( 2 ).isNull() || !query.value( 3 ).isNull() )
{
mExtent.setXMinimum( query.value( 0 ).toDouble() );
mExtent.setYMinimum( query.value( 1 ).toDouble() );
mExtent.setXMaximum( query.value( 2 ).toDouble() );
mExtent.setYMaximum( query.value( 3 ).toDouble() );
return;
}
}
}

// If we can't find the extents in the geometry_columns table just do what we normally do.
bool readAll = false;
QgsDebugMsg(query.lastError().text());

// If we can't find the extents in the spatial index table just do what we normally do.
bool readAllGeography = false;
if ( estimate )
{
if ( mGeometryColType == "geometry" )
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 );
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 );
else
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 );
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 );
}
else
{
if ( mGeometryColType == "geometry" )
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 );
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 );
else
{
statement = QString( "select [%1]" ).arg( mGeometryColName );
readAll = true;
readAllGeography = true;
}
}

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

if ( query.isActive() )
if ( !query.isActive() )
{
return;
}

QgsGeometry geom;
if ( !readAllGeography )
{
QgsGeometry geom;
if ( !readAll )
if ( query.next() )
{
if ( query.next() )
{
mExtent.setXMinimum( query.value( 0 ).toDouble() );
mExtent.setYMinimum( query.value( 1 ).toDouble() );
mExtent.setXMaximum( query.value( 2 ).toDouble() );
mExtent.setYMaximum( query.value( 3 ).toDouble() );
mNumberFeatures = query.value( 4 ).toInt();
}
mExtent.setXMinimum( query.value( 0 ).toDouble() );
mExtent.setYMinimum( query.value( 1 ).toDouble() );
mExtent.setXMaximum( query.value( 2 ).toDouble() );
mExtent.setYMaximum( query.value( 3 ).toDouble() );
return;
}
else
{
// read all features
while ( query.next() )
{
QByteArray ar = query.value( 0 ).toByteArray();
unsigned char* wkb = parser.ParseSqlGeometry(( unsigned char* )ar.data(), ar.size() );
if ( wkb )
{
geom.fromWkb( wkb, parser.GetWkbLen() );
QgsRectangle rect = geom.boundingBox();
}

if ( mNumberFeatures > 0 )
{
if ( rect.xMinimum() < mExtent.xMinimum() )
mExtent.setXMinimum( rect.xMinimum() );
if ( rect.yMinimum() < mExtent.yMinimum() )
mExtent.setYMinimum( rect.yMinimum() );
if ( rect.xMaximum() > mExtent.xMaximum() )
mExtent.setXMaximum( rect.xMaximum() );
if ( rect.yMaximum() > mExtent.yMaximum() )
mExtent.setYMaximum( rect.yMaximum() );
}
else
{
mExtent = rect;
mWkbType = geom.wkbType();
mSRId = parser.GetSRSId();
}
++mNumberFeatures;
}
}
// We have to read all the geometry if readAllGeography is true.
while ( query.next() )
{
QByteArray ar = query.value( 0 ).toByteArray();
unsigned char* wkb = parser.ParseSqlGeometry(( unsigned char* )ar.data(), ar.size() );
if ( wkb )
{
geom.fromWkb( wkb, parser.GetWkbLen() );
QgsRectangle rect = geom.boundingBox();

if ( rect.xMinimum() < mExtent.xMinimum() )
mExtent.setXMinimum( rect.xMinimum() );
if ( rect.yMinimum() < mExtent.yMinimum() )
mExtent.setYMinimum( rect.yMinimum() );
if ( rect.xMaximum() > mExtent.xMaximum() )
mExtent.setXMaximum( rect.xMaximum() );
if ( rect.yMaximum() > mExtent.yMaximum() )
mExtent.setYMaximum( rect.yMaximum() );

mWkbType = geom.wkbType();
mSRId = parser.GetSRSId();
}
}
}
@@ -704,7 +700,36 @@ QGis::WkbType QgsMssqlProvider::geometryType() const
*/
long QgsMssqlProvider::featureCount() const
{
return mNumberFeatures;
// Return the count that we get from the subset.
if ( !mSqlWhereClause.isEmpty() )
return mNumberFeatures;

// If there is no subset set we can get the count from the system tables.
// Which is faster then doing select count(*)
QSqlQuery query = QSqlQuery( mDatabase );
query.setForwardOnly( true );

QString sql = "SELECT rows"
" FROM sys.tables t"
" JOIN sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0,1)"
" WHERE SCHEMA_NAME(t.schema_id) = '%1' AND OBJECT_NAME(t.OBJECT_ID) = '%2'";

QString statement = QString (sql).arg( mSchemaName )
.arg( mTableName );

if ( query.exec( statement ) )
{
if ( query.next() )
{
return query.value(0).toInt();
}
}
else
{
// We couldn't get the rows from the sys tables. Can that ever happen?
// Should just do a select count(*) here.
return -1;
}
}

const QgsFields & QgsMssqlProvider::fields() const

1 comment on commit 702b14a

@NathanW2

This comment has been minimized.

Copy link
Member Author

@NathanW2 NathanW2 commented on 702b14a Jul 2, 2013

Should have been "Use spatial index table for ms sql layers for faster loading"

Please sign in to comment.
You can’t perform that action at this time.