J'ai une requête qui prend une chaîne json comme paramètre. Le json est un tableau de paires de latitude et longitude. Un exemple d'entrée peut être le suivant.
declare @json nvarchar(max)= N'[[40.7592024,-73.9771259],[40.7126492,-74.0120867]
,[41.8662374,-87.6908788],[37.784873,-122.4056546]]';
Il appelle un TVF qui calcule le nombre de POI autour d'un point géographique, à des distances de 1,3,5,10 mile.
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
L'intention de la requête json est d'appeler cette fonction en bloc. Si je l'appelle comme ça, la performance est très mauvaise en prenant près de 10 secondes pour seulement 4 points:
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
Cependant, le déplacement de la construction de la géographie à l'intérieur d'une table dérivée entraîne une amélioration spectaculaire des performances, ce qui termine la requête en environ 1 seconde.
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
Les plans semblent pratiquement identiques. Aucun n'utilise le parallélisme et les deux utilisent l'index spatial. Il y a une bobine paresseuse supplémentaire sur le plan lent que je peux éliminer avec l'indice option(no_performance_spool)
. Mais les performances de la requête ne changent pas. Il reste encore beaucoup plus lent.
L'exécution des deux avec l'indication ajoutée dans un lot pèsera les deux requêtes de manière égale.
Version du serveur SQL = Microsoft SQL Server 2016 (SP1-CU7-GDR) (KB4057119) - 13.0.4466.4 (X64)
Donc ma question est pourquoi est-ce important? Comment savoir si je dois calculer des valeurs dans une table dérivée ou non?
point_of_interest
table, analysent l'index 4602 fois et génèrent une table de travail et un fichier de travail. L'estimateur estime que ces plans sont identiques, mais le rendement dit le contraire.
|LatLong.Lat - @geo.Lat| + |LatLong.Long - @geo.Long| < n
avant de faire le plus compliqué sqrt((LatLong.Lat - @geo.Lat)^2 + (LatLong.Long - @geo.Long)^2)
. Et encore mieux, calculez d'abord les limites supérieures et inférieures LatLong.Lat > @geoLatLowerBound && LatLong.Lat < @geoLatUpperBound && LatLong.Long > @geoLongLowerBound && LatLong.Long < @geoLongUpperBound
. (Ceci est un pseudocode, adaptez