Wir haben eine einfache SQL Server-Tabelle mit Geodaten, die folgendermaßen aussieht:
CREATE TABLE [dbo].[Factors](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StateCode] [nvarchar](2) NOT NULL,
[GeoLocation] [geography] NULL,
[Factor] [decimal](18, 6) NOT NULL,
CONSTRAINT [PK_dbo.Factors] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
Wir haben momentan mehr als 100.000 Zeilen darin, aber das wird voraussichtlich auf Millionen anwachsen.
Wir führen darauf Abfragen aus, die folgendermaßen aussehen:
declare @state nvarchar(2) = 'AL'
declare @point geography = geography::STGeomFromText('POINT(-86.19146040 32.38225770)', 4326)
select top 3
Lat,
Lon,
Factor,
GeoLocation.STDistance(@point) as Distance
from dbo.Factors
where StateCode = @state and GeoLocation.STDistance(@point) is not null
order by Distance
Hier ist das bisschen komisch. Die Daten in dieser Tabelle sind unvollständig: Wir haben sie zum Beispiel für die südlichen Teile eines Bundesstaates, aber nicht für den gesamten Bundesstaat. Wenn der Punkt, nach dem wir suchen, innerhalb einiger hundert Meter von Punkten liegt, für die wir Daten haben (z. B. aus dem südlichen Teil des Bundesstaates), gibt die Abfrage eine Untersekunde zurück. Wenn es jedoch beispielsweise 100 Kilometer vom nächsten Datenpunkt entfernt ist (z. B. wenn der Zielpunkt aus dem nördlichen Teil des Bundesstaates stammt), dauert die Rückgabe der Abfrage etwa 3 Minuten. In beiden Fällen geben Abfragepläne an, dass sie mit einem Scan des Geodatenindex beginnen. Daher ist es nicht das Problem, das manchmal auftritt, dass SQL Server nicht herausfinden kann, dass der betreffende Index verwendet werden soll.
Ich gehe davon aus, dass dies etwas mit der Darstellung des Geoindex zu tun hat.
CREATE SPATIAL INDEX IX_Factors_Spatial
ON [dbo].[Factors] (GeoLocation)
USING GEOGRAPHY_AUTO_GRID
WITH (
CELLS_PER_OBJECT = 16,
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON);
Aber ich weiß nicht, dass ich die Details gut genug verstehe, um das Problem in den Griff zu bekommen.
Irgendwelche Vorschläge, wie Sie dieses Problem beheben können?
quelle
dbcc freeproccache
führen Sie eine Abfrage für den nördlichen Teil eines Bundesstaates aus, um festzustellen, ob dies schnell geht.Antworten:
Kurze Antwort: Vergleichen Sie die tatsächlichen Ausführungspläne für die schnellen und langsamen Varianten und Sie werden sich selbst sehen.
Wenn das Angegebene
@point
nahe an den Punkten in der Tabelle liegt, helfen die im räumlichen Index verwendeten Tessellationen tatsächlich dabei, die meisten Zeilen zu schließen, und es sind nur wenige Suchvorgänge des Index erforderlich.Wenn das Gegebene
@point
weit von einem Punkt in der Tabelle entfernt ist, muss die Engine effektiv alle Zeilen lesen. Es wird 100K-mal nach einem Index gesucht, was langsam ist.Wenn Sie den räumlichen Index deaktivieren, wird die Leistung der Abfrage für alle gegeben
@point
. Es ist langsamer als Ihre schnelle Variante, wenn der Index nützlich ist, aber es ist schneller als Ihre langsame Variante, wenn der Index schädlich ist.Weitere Informationen zu den internen Strukturen eines solchen Index finden Sie unter Übersicht über räumliche Indizes, falls Sie dies noch nicht getan haben.
Beispieltestdaten
Generieren Sie ~ 100.000 Zeilen in einem Bereich von ~ 20 km x ~ 20 km.
Erstellen Sie einen räumlichen Standardindex
Testabfragen
@point1
liegt in der Nähe anderer Punkte in der Tabelle.@point2
ist weit weg von anderen Punkten in der Tabelle.Ausführungspläne und IO
Index aktiviert
IO. Das beste Ergebnis ist schnell (7 ms, 171 Lesevorgänge). Das untere Ergebnis ist langsam (5.693 ms, 234.662 Lesevorgänge).
Schnell.
Langsam.
Index deaktiviert
IO. Beide Abfragen haben die gleiche Anzahl von Lesevorgängen (601) und die gleiche Dauer (~ 1700 ms).
Der Plan ist für beide Abfragen gleich:
Das Scannen von 100.000 Zeilen ist schneller als das Suchen von 100.000 Zeilen.
Ich weiß nicht, wie ich das Problem lösen soll, ob es eine Möglichkeit gibt, das Beste aus beiden Welten herauszuholen und irgendwie automatisch zu entscheiden, ob ich den Index verwenden soll oder nicht.
Sie können versuchen, den Begrenzungsrahmen (min / max lat / lon) zu berechnen und die Logik basierend darauf zu ändern, ob sich der angegebene Punkt innerhalb des Begrenzungsrahmens befindet.
Das Interessanteste passiert in dieser integrierten Funktion mit Tabellenwert für die geodätische Tesselation, und ich sehe keine Feinabstimmung.
Bei räumlichen Indizes hängt vieles von Ihrer Datenverteilung ab.
In einigen Fällen sind Sie möglicherweise mit zwei separaten einfachen Standardindizes für Längen- und Breitengrade besser dran, wenn Sie wissen, dass Ihre Daten dicht sind und Sie die Suche auf einen schmalen Streifen oder einen kleinen Bereich beschränken können (gegebener Punkt + - wenige km).
quelle