Skip to content

Commit 62af54e

Browse files
committed
[mssql] Use Filter instead of STIntersects to improve query performance
...and refine validity test from 57dc3c7 Using Filter is more performant since it does a bounding box only check when an appropriate spatial index is available. This matches the behavior with other providers, where the provider filter only does a bounding box check and callers must perform the actual intersection check if required. While Filter also avoids SQL server closing the iterator upon encountering invalid geometries, we can't rely on this because SQL server will transparently fall back to STIntersects if it decides there's no suitable spatial indexes available, and then throw an exception on invalid geometries. So we still require the STISValid check when using Filter. The extent calculation from 57dc3c7 has been refined to avoid the very expensive STMakeValid call. Instead we use a (ugly!) workaround to skip invalid geometries using STIsValid only inside the min/max x/y aggregates. Note that we can't just dump a WHERE STIsValid clause in to the statement because SQL server still throws the exception. Gotta love it! Fix #15752, #10947
1 parent 039866d commit 62af54e

3 files changed

Lines changed: 12 additions & 4 deletions

File tree

‎src/providers/mssql/qgsmssqlfeatureiterator.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ void QgsMssqlFeatureIterator::BuildStatement( const QgsFeatureRequest& request )
127127
<< qgsDoubleToString( request.filterRect().xMinimum() ) << ' ' << qgsDoubleToString( request.filterRect().yMaximum() ) << ", "
128128
<< qgsDoubleToString( request.filterRect().xMinimum() ) << ' ' << qgsDoubleToString( request.filterRect().yMinimum() );
129129

130-
mStatement += QString( " where [%1].STIsValid() = 1 AND [%1].STIntersects([%2]::STGeomFromText('POLYGON((%3))',%4)) = 1" ).arg(
130+
mStatement += QString( " where [%1].STIsValid() = 1 AND [%1].Filter([%2]::STGeomFromText('POLYGON((%3))',%4)) = 1" ).arg(
131131
mSource->mGeometryColName, mSource->mGeometryColType, r, QString::number( mSource->mSRId ) );
132132
filterAdded = true;
133133
}

‎src/providers/mssql/qgsmssqlprovider.cpp‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -690,14 +690,14 @@ void QgsMssqlProvider::UpdateStatistics( bool estimate )
690690
if ( estimate )
691691
{
692692
if ( mGeometryColType == "geometry" )
693-
statement = QString( "select min([%1].MakeValid().STPointN(1).STX), min([%1].MakeValid().STPointN(1).STY), max([%1].MakeValid().STPointN(1).STX), max([%1].MakeValid().STPointN(1).STY)" ).arg( mGeometryColName );
693+
statement = QString( "select min(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).STX else NULL end), min(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).STY else NULL end), max(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).STX else NULL end), max(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).STY else NULL end)" ).arg( mGeometryColName );
694694
else
695-
statement = QString( "select min([%1].MakeValid().STPointN(1).Long), min([%1].MakeValid().STPointN(1).Lat), max([%1].MakeValid().STPointN(1).Long), max([%1].MakeValid().STPointN(1).Lat)" ).arg( mGeometryColName );
695+
statement = QString( "select min(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).Long else NULL end), min(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).Lat else NULL end), max(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).Long else NULL end), max(case when ([%1].STIsValid() = 1) THEN [%1].STPointN(1).Lat else NULL end)" ).arg( mGeometryColName );
696696
}
697697
else
698698
{
699699
if ( mGeometryColType == "geometry" )
700-
statement = QString( "select min([%1].MakeValid().STEnvelope().STPointN(1).STX), min([%1].MakeValid().STEnvelope().STPointN(1).STY), max([%1].MakeValid().STEnvelope().STPointN(3).STX), max([%1].MakeValid().STEnvelope().STPointN(3).STY)" ).arg( mGeometryColName );
700+
statement = QString( "select min(case when ([%1].STIsValid() = 1) THEN [%1].STEnvelope().STPointN(1).STX else NULL end), min(case when ([%1].STIsValid() = 1) THEN [%1].STEnvelope().STPointN(1).STY else NULL end), max(case when ([%1].STIsValid() = 1) THEN [%1].STEnvelope().STPointN(3).STX else NULL end), max(case when ([%1].STIsValid() = 1) THEN [%1].STEnvelope().STPointN(3).STY else NULL end)" ).arg( mGeometryColName );
701701
else
702702
{
703703
statement = QString( "select [%1]" ).arg( mGeometryColName );

‎tests/src/python/test_provider_mssql.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ def testInvalidGeometries(self):
133133
(self.dbconn), "testinvalid", "mssql")
134134
assert(vl.isValid())
135135

136+
self.assertEqual(vl.dataProvider().extent().toString(1), QgsRectangle(173.953, -41.513, 173.967, -41.502).toString(1))
137+
136138
#burn through features - don't want SQL server to trip up on the invalid ones
137139
count = 0
138140
for f in vl.dataProvider().getFeatures():
@@ -147,6 +149,12 @@ def testInvalidGeometries(self):
147149
self.assertEqual(count, 37)
148150
# sorry... you get NO chance to see these features exist and repair them... because SQL server. Use PostGIS instead and live a happier life!
149151

152+
# with estimated metadata
153+
vl = QgsVectorLayer('%s srid=4167 type=POLYGON estimatedmetadata=true table="qgis_test"."invalid_polys" (ogr_geometry) sql=' %
154+
(self.dbconn), "testinvalid", "mssql")
155+
assert(vl.isValid())
156+
self.assertEqual(vl.dataProvider().extent().toString(1), QgsRectangle(173.954, -41.513, 173.967, -41.502).toString(1))
157+
150158

151159
if __name__ == '__main__':
152160
unittest.main()

0 commit comments

Comments
 (0)