Tôi có một truy vấn lấy một chuỗi json làm tham số. Json là một mảng các cặp kinh độ, vĩ độ. Một ví dụ đầu vào có thể là như sau.
declare @json nvarchar(max)= N'[[40.7592024,-73.9771259],[40.7126492,-74.0120867]
,[41.8662374,-87.6908788],[37.784873,-122.4056546]]';
Nó gọi một TVF tính toán số POI xung quanh một điểm địa lý, ở khoảng cách 1,3,5,10 dặm.
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
Mục đích của truy vấn json là gọi hàng loạt chức năng này. Nếu tôi gọi nó như thế này thì hiệu suất rất kém chỉ mất gần 10 giây cho chỉ 4 điểm:
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))
kế hoạch = https://www.brentozar.com/pastetheplan/?id=HJDCYd_o4
Tuy nhiên, việc di chuyển cấu trúc địa lý bên trong bảng dẫn xuất khiến hiệu suất được cải thiện đáng kể, hoàn thành truy vấn trong khoảng 1 giây.
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)
kế hoạch = https://www.brentozar.com/pastetheplan/?id=HkSS5_OoE
Các kế hoạch trông gần như giống hệt nhau. Không sử dụng song song và cả hai đều sử dụng chỉ số không gian. Có một spool lười biếng bổ sung vào kế hoạch chậm mà tôi có thể loại bỏ với gợi ý option(no_performance_spool)
. Nhưng hiệu suất truy vấn không thay đổi. Nó vẫn còn chậm hơn nhiều.
Chạy cả hai với gợi ý được thêm vào trong một đợt sẽ cân nhắc cả hai truy vấn như nhau.
Phiên bản máy chủ Sql = Microsoft SQL Server 2016 (SP1-CU7-GDR) (KB4057119) - 13.0.4466.4 (X64)
Vì vậy, câu hỏi của tôi là tại sao điều này lại quan trọng? Làm thế nào tôi có thể biết khi nào tôi nên tính toán các giá trị bên trong một bảng dẫn xuất hay không?
point_of_interest
bảng, cả hai đều quét chỉ số 4602 lần và cả hai đều tạo ra một bàn làm việc và tệp công việc. Người ước tính tin rằng các kế hoạch này là giống hệt nhau nhưng hiệu suất nói khác.
|LatLong.Lat - @geo.Lat| + |LatLong.Long - @geo.Long| < n
trước khi bạn làm phức tạp hơn sqrt((LatLong.Lat - @geo.Lat)^2 + (LatLong.Long - @geo.Long)^2)
. Và thậm chí tốt hơn, tính toán giới hạn trên và dưới trước, sau đó LatLong.Lat > @geoLatLowerBound && LatLong.Lat < @geoLatUpperBound && LatLong.Long > @geoLongLowerBound && LatLong.Long < @geoLongUpperBound
. (Đây là mã giả, thích nghi thích hợp.)