性能调整大圆距离计算

Ste*_*eve 6 performance sql-server tuning functions

我有一个查询,用于查找距离已知点 1 公里以内的位置。

为此,我使用了余弦球面定律公式和我的纬度和经度。

目前,该查询在大约 5 分钟内运行了大约 5000 条记录。这是可以接受的,但我想尽快完成。

我使用的主要查询:

select loc.*, base.key
from locations loc left outer join baseline base
on base.key in
(select key
from baseline
where [dbo].CIRCLEDISTANCE(loc.LAT, base.LATITUDE, loc.LONG, base.LONGITUDE) <= 1)
Run Code Online (Sandbox Code Playgroud)

圆距离标量值函数:

ALTER FUNCTION CIRCLEDISTANCE 
(
    -- Add the parameters for the function here
    @LAT1 varchar(250), 
    @LAT2 varchar(250),
    @LNG1 varchar(250), 
    @LNG2 varchar(250)
)
RETURNS float
AS
BEGIN

    DECLARE @Distance float
    DECLARE @LAT1_FLOAT float
    DECLARE @LNG1_FLOAT float
    DECLARE @LAT2_FLOAT float
    DECLARE @LNG2_FLOAT float

    select @LAT1_FLOAT = cast(@LAT1 as float)
    select @LNG1_FLOAT = cast(@LNG1 as float)
    select @LAT2_FLOAT = cast(@LAT2  as float)
    select @LNG2_FLOAT = cast(@LNG2 as float)

    SELECT @Distance = acos(sin(radians(@LAT1_FLOAT))*
            sin(radians(@LAT2_FLOAT))+
            cos(radians(@LAT1_FLOAT))*
            cos(radians(@LAT2_FLOAT))*
            cos(radians(@LNG2_FLOAT)-radians(@LNG1_FLOAT ))
            )*6371;

    RETURN @Distance

END
GO
Run Code Online (Sandbox Code Playgroud)

执行计划(有一些匿名)

我已经索引了查询和函数中命名的所有列。

一些注意事项:

  • 我正在函数中转换为浮点数,因为我得到的数据是作为字符串到达​​的,在此之前我无法更改它
  • 需要左外连接,因为我需要知道没有已知点的位置
  • 我使用的是余弦定律而不是半正弦,因为它很简单,而且我不需要很高的精度,因为我只对 1 公里内的事物感兴趣。!

我自己不是 DBA,只是 .net 开发人员,所以我对提高索引以外的查询性能并不真正了解,因此感谢任何帮助。

编辑:

滚动 Rob 和 Mark 的答案使我的查询执行时间缩短到 30 秒。如果有任何进一步的收益,现在是查询:

select <columns>
from locations loc 
left outer join baseline base  
on base.key in
(select key
from baseline
where base.location_geo.STDistance(loc.location_geo) <= 0.00015678559)
Run Code Online (Sandbox Code Playgroud)

0.00015678559 是 1000/6378137(地球半径),所以当查询运行时我不需要做任何数学运算。

location_geo 是一个使用以下公式的计算列:

([geography]::STGeomFromText(((('POINT('+[Longitude])+' ')+[Latitude])+')',(104001)))
Run Code Online (Sandbox Code Playgroud)

104001 是球形地球模型的空间 ID,因为我不是在寻找很远的距离,这似乎使查询更快地返回。

我在 location_geo 列上也有一个空间索引,如下所示:http : //social.technet.microsoft.com/wiki/contents/articles/9694.tuning-spatial-point-data-queries-in-sql-server- 2012.aspx

Rob*_*ley 6

创建一个计算列,使用 Point 构造函数将您的 lat 和 long 列转换为地理类型。然后在这个计算列上放置一个空间索引。

然后您的查询可以从您的圆心创建一个地理点,并比较距离。应该非常快。


Mik*_*son 5

标量值函数需要时间来调用,并且从它的外观来看,您正在对CIRCLEDISTANCE. 您可以将函数重写为内联表值函数。

create function CIRCLEDISTANCE 
(
  @LAT1 varchar(250), 
  @LAT2 varchar(250),
  @LNG1 varchar(250), 
  @LNG2 varchar(250)
)
returns table as return
(
  select acos(sin(radians(cast(@LAT1 as float)))*
         sin(radians(cast(@LAT2 as float)))+
         cos(radians(cast(@LAT1 as float)))*
         cos(radians(cast(@LAT2 as float)))*
         cos(radians(cast(@LNG2 as float))-radians(cast(@LNG1 as float)))
         )*6371 as Distance
)
Run Code Online (Sandbox Code Playgroud)

这需要稍微重写您的主查询,因为该函数现在返回一个表而不是标量值。

select loc.*,
       B.[key]
from locations as loc
  outer apply (
              select base.[key]
              from baseline as base
                cross apply dbo.CIRCLEDISTANCE2(loc.LAT, base.LATITUDE, loc.LONG, base.LONGITUDE) as C
              where C.Distance <= 1
              group by base.[key]
              ) as B
Run Code Online (Sandbox Code Playgroud)