JOIN на основе расстояния с учетом широты/долготы

Учитывая следующие таблицы:

table A (id, latitude, longitude)
table B (id, latitude, longitude)

как создать эффективный запрос T-SQL, который связывает каждую строку в A с ближайшей строкой в ​​B?

ResultSet должен содержать все строки в A и связывать их с 1 и только 1 элементом в B. Формат, который я ищу, следующий:

(A.id, B.id, distanceAB)

У меня есть функция, которая вычисляет расстояние с учетом двух пар широты и долготы. Я пробовал что-то с использованием order by ... limit 1 и/или rank() over (partition by ...) as rowCount ... where rowCount = 1, но результат либо не совсем то, что мне нужно, либо слишком долго возвращается.

Я что-то упускаю?


person Marsellus Wallace    schedule 20.01.2012    source источник


Ответы (3)


Невозможно обойти тот факт, что вам придется сравнивать каждую запись в 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

Это вернется через разумное время, если ваши наборы не слишком велики. С 3 точками в A и 130 000 или около того в B на моей машине это занимает около одной секунды. 1000 записей в каждой занимает около 40 секунд. Как я уже сказал, он плохо масштабируется.

Следует отметить, что ответ Спарки может возвращать неверные результаты при определенных обстоятельствах. Предположим, ваше местоположение А находится в +40, +100. +40,+111 не будет возвращено, хотя это ближе, чем +49,+109.

person Chad    schedule 21.01.2012

Это один из подходов, который должен иметь достойную производительность, но большое предостережение заключается в том, что он может не дать никаких результатов.

    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

В основном вы ищете любую строку B в радиусе примерно 20 единиц от A, а затем сортируете ее по своей функции, чтобы определить ближайшую. Вы можете отрегулировать радиус единицы по мере необходимости. Хотя это не точно, это должно уменьшить размер результирующего набора и дать вам достойные результаты производительности.

person Sparky    schedule 20.01.2012

Это возможно при объединении двух подзапросов. Первый содержит все расстояния между точками A и B, второй содержит только минимальное расстояние точек B от точек A.

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
person tpolyak    schedule 20.01.2012