Ich habe eine Abfrage, die eine JSON-Zeichenfolge als Parameter nimmt. Der json ist ein Array von Breiten- und Längengradpaaren. Eine Beispieleingabe könnte die folgende sein.
declare @json nvarchar(max)= N'[[40.7592024,-73.9771259],[40.7126492,-74.0120867]
,[41.8662374,-87.6908788],[37.784873,-122.4056546]]';
Es ruft eine TVF auf, die die Anzahl der POIs um einen geografischen Punkt in 1,3,5,10 Meilen Entfernung berechnet.
create or alter function [dbo].[fn_poi_in_dist](@geo geography)
returns table
with schemabinding as
return
select count_1 = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 1,1,0e))
,count_3 = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 3,1,0e))
,count_5 = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 5,1,0e))
,count_10 = count(*)
from dbo.point_of_interest
where LatLong.STDistance(@geo) <= 1609.344e * 10
Die Absicht der json-Abfrage besteht darin, diese Funktion als Massenaufruf aufzurufen. Wenn ich es so nenne, ist die Leistung sehr schlecht und dauert fast 10 Sekunden für nur 4 Punkte:
select row=[key]
,count_1
,count_3
,count_5
,count_10
from openjson(@json)
cross apply dbo.fn_poi_in_dist(
geography::Point(
convert(float,json_value(value,'$[0]'))
,convert(float,json_value(value,'$[1]'))
,4326))
plan = https://www.brentozar.com/pastetheplan/?id=HJDCYd_o4
Durch das Verschieben der Geografiekonstruktion in eine abgeleitete Tabelle wird die Leistung jedoch erheblich verbessert, und die Abfrage wird in etwa 1 Sekunde abgeschlossen.
select row=[key]
,count_1
,count_3
,count_5
,count_10
from (
select [key]
,geo = geography::Point(
convert(float,json_value(value,'$[0]'))
,convert(float,json_value(value,'$[1]'))
,4326)
from openjson(@json)
) a
cross apply dbo.fn_poi_in_dist(geo)
plan = https://www.brentozar.com/pastetheplan/?id=HkSS5_OoE
Die Pläne sehen praktisch identisch aus. Keiner verwendet Parallelität und beide verwenden den räumlichen Index. Es gibt eine zusätzliche träge Spule auf dem langsamen Plan, die ich mit dem Hinweis beseitigen kann option(no_performance_spool)
. Die Abfrageleistung ändert sich jedoch nicht. Es bleibt immer noch viel langsamer.
Wenn Sie beide mit dem hinzugefügten Hinweis in einem Stapel ausführen, werden beide Abfragen gleich gewichtet.
SQL Server-Version = Microsoft SQL Server 2016 (SP1-CU7-GDR) (KB4057119) - 13.0.4466.4 (X64)
Meine Frage ist also, warum das wichtig ist? Wie kann ich wissen, wann ich Werte in einer abgeleiteten Tabelle berechnen soll oder nicht?
point_of_interest
Tabelle durch, scannen den Index 4602-mal und generieren eine Arbeitstabelle und eine Arbeitsdatei. Der Schätzer ist der Ansicht, dass diese Pläne identisch sind, die Leistung jedoch etwas anderes vorgibt.
|LatLong.Lat - @geo.Lat| + |LatLong.Long - @geo.Long| < n
umso komplizierter ist, bevor Sie ihn ausführen sqrt((LatLong.Lat - @geo.Lat)^2 + (LatLong.Long - @geo.Long)^2)
. Und noch besser, berechnen Sie zuerst die obere und untere Grenze LatLong.Lat > @geoLatLowerBound && LatLong.Lat < @geoLatUpperBound && LatLong.Long > @geoLongLowerBound && LatLong.Long < @geoLongUpperBound
. (Dies ist ein Pseudocode, passen Sie ihn entsprechend an.)