Дублирование записей в геопоиске MySQL с использованием формулы Cross Join и Hversine

Я пытаюсь завершить модификацию этого учебника Google.

Я написал этот SQL для запроса таблицы местоположений, используя местоположение «имя». Учитывая название местоположения, запрос возвращает пиццерии в непосредственной близости. Для этого я соединил свою таблицу местоположений ресторанов, назвал ее «маркеры» и рассчитал расстояния, используя формулу Хаверсина.

    SELECT m.address,
       m.name,
       m.lat,
       m.lng,
       (3959 * ACOS(COS(RADIANS(poi.lat)) * 
       COS(RADIANS(m.lat)) * 
       COS(RADIANS(m.lng) - RADIANS(poi.lng)) + SIN(RADIANS(poi.lat))*
       SIN(RADIANS(m.lat)))) AS distance
    FROM markers poi
       CROSS JOIN markers m
    WHERE poi.address LIKE "%myrtle beach%"
          AND poi.id <> m.id HAVING distance < 200
   ORDER BY distance LIMIT 0,20

Запрос возвращает ожидаемые результаты, но если точка интереса находится за пределами указанной области, в данном случае «миртл-бич», я получаю повторяющиеся записи для каждого совпадения. Это связано с тем, что CROSS JOIN легко исправить с помощью выбора DISTINCT. Но поля "lng" и "lat" относятся к типам FLOAT, поэтому расчеты расстояния никогда не бывают идентичными, даже для повторяющихся записей.

Вот подмножество возвратов:

3901 North Kings Highway Suite 1, Миртл-Бич, Южная Каролина | Восток Чикаго Pizza Company | 33,716099 -78,855583 | 4.0285562196955125

1706 S Kings Hwy # A, Миртл-Бич, Южная Каролина | Пицца Домино: Миртл-Бич | 33.674881 | -78.905144 | 4.0285562196955125

82 Wentworth St, Чарльстон, Южная Каролина | Андолинис Пицца | 2.782330 | -79,934235 | 85,68177495224947

82 Wentworth St, Чарльстон, Южная Каролина | Андолинис Пицца | 32.782330 | -79,934235 | 89.71000040441085

114 Jungle Rd, остров Эдисто, Южная Каролина | Бакс Пицца Эдисто Бич Инк | 32.503971 -80.297951 | 114.22243529200529

114 Jungle Rd, остров Эдисто, Южная Каролина | Бакс Пицца Эдисто Бич Инк | 32.503971 -80.297951 | 118.2509427998286"

Любые предложения о том, куда идти отсюда?


person Brandon Buster    schedule 22.02.2014    source источник
comment
Можете ли вы опубликовать пример ввода и вывода, показывающий дубликаты? И ради удобочитаемости я предлагаю редактирование, которое предполагает наличие Haversine функции.   -  person bishop    schedule 22.02.2014
comment
@Brandon Buster Можете ли вы предоставить образец ваших данных. Кроме того, что вы подразумеваете под точкой интереса? Единственные введенные вручную данные, которые я вижу, это %myrtle beach% . Что вы пытаетесь сделать? Поскольку этот вопрос немного сложен для понимания и требует разъяснений и более подробной информации.   -  person Menelaos    schedule 23.02.2014
comment
Извините, я использовал термин достопримечательность в нескольких разных контекстах. В этом примере интерес представляет только Миртл-Бич. Данные, которые я хочу вернуть, это пиццерии в радиусе 200 миль от этой точки. Я отредактирую свой пост с некоторыми примерами данных и дополнительной информацией.   -  person Brandon Buster    schedule 23.02.2014


Ответы (3)


Пытаться:

select distinct x.address, x.name, y.lat, y.lng, x.distance
  from (SELECT m.address,
               m.name,
               m.lat,
               m.lng,
               (3959 *
               ACOS(COS(RADIANS(poi.lat)) * COS(RADIANS(m.lat)) *
                     COS(RADIANS(m.lng) - RADIANS(poi.lng)) +
                     SIN(RADIANS(poi.lat)) * SIN(RADIANS(m.lat)))) AS distance
          FROM markers poi
         cross JOIN markers m
         WHERE poi.address LIKE "%myrtle beach%"
           and poi.id <> m.id HAVING distance < 200) x
  join markers y
    on x.address = y.address
   and x.name = y.name
   and x.lat = y.lat
   and x.lng = y.lng
 order by x.distance limit 0, 20
person Brian DeMilia    schedule 22.02.2014
comment
Спасибо. Укажите на то, что выглядит рабочим решением. Однако я бы предпочел не выполнять соединение с третьей таблицей. Если это окажется единственным ответом (что вполне вероятно), я отмечу это как ответ. - person Brandon Buster; 23.02.2014

Вы получаете повторяющиеся результаты, потому что обе точки соответствуют «миртл-бич». Используйте такое условие, как poi.id < m.id, чтобы получить только одно совпадение.

Пример:

poi id    m id    distance
1         2       100
2         1       100

Запрос:

SELECT 
    m.address,
    m.name,
    m.lat,
    m.lng,
    (3959 * ACOS(COS(RADIANS(poi.lat)) * 
    COS(RADIANS(m.lat)) * 
    COS(RADIANS(m.lng) - RADIANS(poi.lng)) + SIN(RADIANS(poi.lat))*
    SIN(RADIANS(m.lat)))) AS distance
FROM markers poi
CROSS JOIN markers m
WHERE 
    (poi.address LIKE "%myrtle beach%" OR m.address LIKE "%myrtle beach%")
    AND poi.id < m.id 
HAVING distance < 200
ORDER BY distance LIMIT 0,20

Или, если у вас действительно есть единственная строка в маркерах в качестве точки интереса, укажите это вместо любого совпадения по адресу. Тогда ваше условие poi.id <> m.id гарантирует отсутствие дубликатов.

SELECT 
    m.address,
    m.name,
    m.lat,
    m.lng,
    (3959 * ACOS(COS(RADIANS(poi.lat)) * 
    COS(RADIANS(m.lat)) * 
    COS(RADIANS(m.lng) - RADIANS(poi.lng)) + SIN(RADIANS(poi.lat))*
    SIN(RADIANS(m.lat)))) AS distance
FROM markers poi
CROSS JOIN markers m
WHERE 
    poi.id = (SELECT TOP(1) id FROM markers WHERE address LIKE "%myrtle beach%")
    AND poi.id <> m.id 
HAVING distance < 200
ORDER BY distance LIMIT 0,20
person Mitch    schedule 22.02.2014
comment
Спасибо за ответ. Он устраняет дубликаты, хотя также предполагает, что все совпадающие записи будут иметь более высокий идентификатор, чем любая из записей, найденных в моей целевой области, что не всегда так. Это дает мне другой взгляд на то, как я писал SQL. - person Brandon Buster; 23.02.2014
comment
@BrandonBuster, это не предполагается, учитывая, что в первом запросе может совпадать строка с более высоким или более низким номером. Во втором запросе мы не используем сравнение >. - person Mitch; 23.02.2014

Анализ ответов каждого заставил меня задуматься. Вместо того, чтобы спрашивать, почему я получаю дублирующиеся результаты, я начал задаваться вопросом, от какого из двух местоположений Миртл-Бич был запрос, вычисляющий расстояния? Ответ был ОБА. И это объясняет, почему я получал два рекорда за матч.

Вот мое решение:

SELECT  m.address, m.name, m.lat, m.lng, (3959 
   * ACOS(COS(RADIANS(poi.lat)) * COS(RADIANS(m.lat)) 
   * COS(RADIANS(m.lng) - RADIANS(poi.lng)) + SIN(RADIANS(poi.lat))
   * SIN(RADIANS(m.lat))))     AS distance
FROM markers m
cross JOIN (
   select  name, lat, lng from markers
   where address like '%myrtle beach %'
   limit 1
) poi
HAVING distance < 200
ORDER BY name
LIMIT 0, 20

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

person Brandon Buster    schedule 23.02.2014