鉴于以下表格:
table A (id, latitude, longitude)
table B (id, latitude, longitude)
如何构建一个高效的T-SQL查询,将A中的每一行与B中最近的一行相关联?
ResultSet应该包含A中的所有行,并将它们与1和B中的1个元素相关联。我正在寻找的格式如下:
(A.id, B.id, distanceAB)
我有一个函数来计算给定2对纬度和经度的距离。我尝试了使用order by ... limit 1
和/或rank() over (partition by ...) as rowCount ... where rowCount = 1
的内容,但结果要么不是我需要的,要么返回时间太长。
我错过了什么吗?
答案 0 :(得分:5)
没有办法解决这样一个事实:你必须将A中的每个记录与B中的每个记录进行比较,如果A和B都包含大量记录,这显然会很难扩展。
话虽如此,这将返回正确的结果:
SELECT aid, bid, distanceAB
FROM (
SELECT aid, bid, distanceAB,
dense_rank() over (partition by aid order by distanceAB) as n
FROM (
SELECT a.id as aid, B.id as bid,
acos(sin(radians(A.lat)) * sin(radians(B.lat)) +
cos(radians(A.lat)) * cos(radians(B.lat)) *
cos(radians(A.lon - B.lon))) * 6372.8 as distanceAB
FROM A cross join B
) C
) D
WHERE n = 1
如果您的套装不是太大,这将在合理的时间内返回。 A中有3个位置,B中有130,000个左右,我的机器上需要大约一秒钟。每篇1000条记录大约需要40秒。就像我说的那样,它的扩展性很差。
应该注意的是,Sparky的答案在某些情况下可能会返回错误的结果。假设您的A位置为+ 40,+ 100。 + 40,+ 111不会被退回,即使它比+ 49,+ 109更接近。
答案 1 :(得分:1)
这是一种应该具有牺牲性能的方法,但一个很大的警告是它可能找不到任何结果
select top 1 a.id,b.id,dbo.yourFunction() as DistanceAB
from a
join b on b.latitude between a.latitude-10 and a.latitude+10 and
b.longititude between a.longitude-10 and b.longittude+10
order by 3
你基本上在做的是在A的大约20个半径范围内寻找任何B行,然后用你的函数对它进行排序以确定最接近的行。您可以根据需要调整单位半径。虽然它不准确,但它应该减小结果集的大小,并且应该给你不错的性能结果。
答案 2 :(得分:0)
两个子查询的连接是可能的。第一个包含A和B位置之间的所有距离,第二个包含A位置的B位置的最小距离。
SELECT x.aid, x.bid, x.distance
FROM
(SELECT A.ID AS aid,
B.ID AS bid,
SQRT(A.Latitude * A.Latitude + B.Longitude * B.Longitude) AS Distance
FROM LocationsA AS A
CROSS JOIN LocationsB AS B) x JOIN
(SELECT A.ID AS aid,
MIN(SQRT(A.Latitude * A.Latitude + B.Longitude * B.Longitude)) AS Distance
FROM LocationsA AS A
CROSS JOIN LocationsB AS B
GROUP BY A.ID) y ON x.aid = y.aid AND x.Distance = y.Distance