У мене є запит, який бере параметр json як параметр. Json - це масив пар широти, довготи. Приклад введення може бути наступним.
declare @json nvarchar(max)= N'[[40.7592024,-73.9771259],[40.7126492,-74.0120867]
,[41.8662374,-87.6908788],[37.784873,-122.4056546]]';
Він називає TVF, який обчислює кількість точок зору навколо географічної точки на відстані 1,3,5,10 милі.
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
Метою запиту json є масовий виклик цієї функції. Якщо я називаю це так, продуктивність дуже погана, займаючи майже 10 секунд всього за 4 бали:
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))
план = https://www.brentozar.com/pastetheplan/?id=HJDCYd_o4
Однак переміщення побудови географії всередині похідної таблиці змушує продуктивність покращитися, виконавши запит приблизно за 1 секунду.
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)
план = https://www.brentozar.com/pastetheplan/?id=HkSS5_OoE
Плани виглядають практично однаково. Ні паралелізм, ні обидва використовують просторовий індекс. На повільному плані є додаткова ледача котушка, яку я можу усунути за допомогою натяку option(no_performance_spool)
. Але ефективність запиту не змінюється. Це все ще залишається набагато повільніше.
Запуск обох із доданим підказом у партії зважить обидва запити однаково.
Версія сервера Sql = Microsoft SQL Server 2016 (SP1-CU7-GDR) (KB4057119) - 13.0.4466.4 (X64)
Тож моє запитання, чому це має значення? Як я можу знати, коли я повинен обчислювати значення всередині похідної таблиці чи ні?
point_of_interest
столі, обидва сканують індекс 4602 рази, і обидва генерують робочий стіл та робочий файл. Оцінювач вважає, що ці плани однакові, але результативність говорить про інше.
|LatLong.Lat - @geo.Lat| + |LatLong.Long - @geo.Long| < n
перед вами робиться складніше sqrt((LatLong.Lat - @geo.Lat)^2 + (LatLong.Long - @geo.Long)^2)
. А ще краще, обчисліть спочатку верхню та нижню межі, потім LatLong.Lat > @geoLatLowerBound && LatLong.Lat < @geoLatUpperBound && LatLong.Long > @geoLongLowerBound && LatLong.Long < @geoLongUpperBound
. (Це псевдокод,