最有效的选择最近点的方法

时间:2013-11-25 15:03:19

标签: sql sql-server performance tsql geospatial

我有两个(非常大)相同结构的表,包含两种类型的位置:

LOCA

  • Id - INT
  • X - FLOAT(纬度)
  • Y - FLOAT(经度)

LocB

  • Id - INT
  • X - FLOAT(纬度)
  • Y - FLOAT(经度)

每个人都有几百万行。我需要在LocA中选择所有位置,并为每个位置选择LocB中最近的位置。

执行此操作的最有效查询是什么?

EDIT1:距离算法很愚蠢:SQRT(POWER(LocB.X - LocA.X,2)+ POWER(LocB.Y - LocA.Y,2))

EDIT2:我已经完成的实现,但我真的不确定它是否是最佳的(我非常怀疑),将是:

SELECT  A.Id    AS AId,
(   SELECT TOP 1 B.Id
    FROM    B
    ORDER BY SQRT(POWER(B.X - A.X, 2) + POWER(B.Y - A.Y, 2)) ASC
)               AS BId
FROM    A

EDIT3:在表LocB中有“重复”是很常见的,但我希望为LocA中的某个位置返回任何匹配的“最接近”,而不是全部。

5 个答案:

答案 0 :(得分:2)

这可能效率不高,但目前我看不到更好的方法:

SELECT  a.ID, a.X, a.Y, b.ID, b.X, b.Y, b.Distance
FROM    LocA a
        CROSS APPLY
        (   SELECT  TOP 1 WITH TIES
                    b.ID, 
                    b.X, 
                    b.Y, 
                    Distance = SQRT(POWER(b.X - a.X, 2) + POWER(b.Y - a.Y, 2))
            FROM    LocB b 
            ORDER BY Distance
        ) B;

答案 1 :(得分:2)

您是否考虑过考虑geography::PointSTDistance方法,并在这些点列上创建spatial index

如果您的数据库结构已修复,则可以添加新的持久计算列。

答案 2 :(得分:1)

SQRT不会改变ORDER - 它只是开销

SELECT  A.Id AS AId,
(   SELECT TOP 1 B.Id
    FROM    B
    ORDER BY POWER(B.X - A.X, 2) + POWER(B.Y - A.Y, 2) ASC
)               AS BId
FROM    A

我在想有两种方法可以进行两次传球 您知道距离是< = delta X + delta Y
并且该近似中的最大误差是SQRT(2)-1

这不涉及重复或关系

我怀疑额外的IO不能弥补POWER计算数量的减少但是值得一试 如果您在SSD上有#temp

,那么值得一试
create #temp1
IDa
IDb
Xa
Ya
Xb 
Yb 
distSum
distAct 

insert into #temp (IDa, IDb, Xa, Ya, Xb ,Yb, distSum)
select a.ID, b.ID, a.x, a.y, b.x, b.y, abs(a.X-b.X) + abs(a.Y-b.Y)
table as a 
join table as b 
on a.ID < b.ID 

delete #temp 
from #temp 
join 
(select IDa, min(distSum) as minDistSum from #temp group by IDa) as aMin 
on #temp.IDa = aMin.IDa 
and #temp.distSum > 1.414*(minDistSum) 

update #temp 
set distAct = POWER(Xa - Xb, 2) + POWER(Ya - Yb, 2)

答案 3 :(得分:0)

这是代码:

WITH S AS (
SELECT *
FROM LOCA CROSS APPLY( select locb.id as ID_B, (POWER(LocB.X - LocA.X, 2) + POWER(LocB.Y - LocA.Y, 2)) D  FROM LOCB ) S
)
SELECT DISTINCT ID,X,Y,d,ID_B
FROM S
where d=(select min(d) from s s1 where s1.ID=s.id)

答案 4 :(得分:0)

怎么样:

SELECT id as Aid,x,y,m % 100 as bId
FROM (
SELECT A.id,A.x,A.y,MIN(CAST(((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)) AS BIGINT)*100+B.id)     as m 
FROM A
CROSS JOIN B
GROUP BY A.id,A.x,A.y) j;