MySQL Выбор неверного значения столбца в запросе «Группировка по» для приложения «Локатор магазина» - Google Maps App - PullRequest
0 голосов
/ 30 января 2011

Сначала я должен объявить, что я новичок в жизни. Только что использовал PHP и MySQL в течение 4 недель. Пожалуйста, примите мои извинения заранее, если я не отформатировал этот вопрос правильно или не использую надлежащие условия использования.

Я создаю приложение для поиска магазинов. Для тестирования у меня есть таблица с названием 'location', которая содержит данные об имени, адресе и широте / долготе для 5 различных сетей ресторанов с 1500 записями (местоположения).

Я запустил приложение в качестве стандартного локатора магазина, где пользователь вводит свой адрес и расстояние в милях для поиска. Приведенный ниже код правильно возвращает эти результаты при удалении оператора GROUP BY. Например, когда пользователь вводит свой адрес и расстояние для поиска, оператор SELECT возвращает ВСЕ рестораны в пределах этого расстояния.

Мое приложение требует, чтобы было возвращено и отображено только ближайшее местоположение каждой сети ресторанов в пределах указанного пользователем расстояния. Я добавил оператор GROUP BY для достижения этой цели. Правильное количество записей возвращается с правильным именем и расстоянием от пользователя. Однако все остальные поля никогда не бывают правильными. Они кажутся случайно выбранными из других записей, которые находятся за пределами значения MIN. Например, первая возвращенная запись предназначена для МОЛОЧНОЙ КОРОЛЕВЫ на расстоянии 4,38 мили - это правильно . Однако адрес, штат, город и т. Д. Для МОЛОЧНОЙ КОРОЛЕВЫ на 4,38 миль неверны .

Я много читал о проблемах с GROUP BY и требовании использовать INNER JOIN, возможно, для решения моей проблемы? Недавние вопросы и ответы в stackoverflow касаются этого очень конкретно, см. MySQL Выбор неправильного значения столбца в запросе Group By . Во всех решениях, которые я читал до сих пор, я использовал вычисленное расстояние в качестве ключа для выполнения JOIN, и я не понимаю, как это возможно.

Вопрос 1: Как мне создать оператор SELECT, чтобы получить желаемый результат: полный ряд полей данных только для одной из каждой сети ресторанов в таблице местоположений?

Заметки о моем коде, который не так страшен , как он выглядит и не является необходимым для решения моей проблемы:

Формула триггера в MIN () вычисляет расстояние в милях между адресом пользователя (переведенным в широту / долготу) и широтой / долготой каждой записи местоположения. Поверьте мне, это работает хорошо.

Оператор ORDER BY 13: означает ЗАКАЗАТЬ по 13-му полю, указанному в SELECT, в данном случае это псевдоним «расстояние». Я упоминаю об этом, потому что я заметил, что этот синтаксис не очень известен.

Код, следующий за оператором WHERE, проверяет, находится ли адрес пользователя (в широтах / долготах) в поле с широтно-угловыми углами, которые являются указанным пользователем расстоянием для поиска. Это называется «ограничивающей рамкой». Используется для оптимизации времени поиска. Можно просто проверить, является ли «расстояние» <=, чем расстояние, введенное пользователем, но для этого потребуется прочитать весь файл местоположений. Производственная версия будет содержать около миллиона записей. Для таблицы местоположений существует индекс: (loc_lat, loc_lon, loc_id). Насколько я понимаю, использование ограничивающего прямоугольника в операторе WHERE ограничит диапазон индекса, который необходимо прочитать. Вопрос 2: Это правда, как я это реализовал, будет ли это обрабатываться, как я описал? Сохранит ли решение вопроса 1 оптимизацию? </p>

Заранее благодарю всех за помощь. Я действительно только 4 недели на MySQL и PHP и, как вы можете видеть, по моей голове?


Мой вопрос сводится к этому. Как изменить этот SELECT так, чтобы он возвращал только 1 запись таблицы местоположений с соответствующими полями для каждого loc_name, которое является минимальным расстоянием от входного адреса пользователя ??

SELECT loc_id,loc_name,loc_address_1,loc_address_2,loc_city,
       loc_state,loc_postal_code,loc_phone,loc_fax,
       loc_lat,loc_lon,loc_geocoded_status,
       MIN( ((ACOS( SIN( $lat * PI( ) /180 ) * SIN( loc_lat * PI( ) /180 ) + 
               COS( $lat * PI( ) /180 ) * COS( loc_lat * PI( ) /180 ) *
               COS( ($long - loc_lon) * PI( ) /180 ) ) *180 / PI( )) *60 * 1.1515) )
       AS distance  
FROM locations WHERE (loc_lat between $lat1 and $lat2
                  AND loc_lon between $lon1 and $lon2)
                  AND loc_geocoded_status = 1
GROUP BY loc_name
ORDER BY 13

1 Ответ

1 голос
/ 28 февраля 2011

Вы прошли долгий путь за четыре недели.Это помогает включать минимальные операторы DDL и INSERT, чтобы побудить больше людей отвечать.

Я добавил оператор GROUP BY, чтобы выполнить это.Правильное количество записей возвращается с правильным именем и расстоянием от пользователя.Однако все остальные поля никогда не бывают правильными.Они кажутся случайно выбранными из других записей, которые находятся за пределами значения MIN.

Да, это нормально для MySQL.Статья MySQL Standard Group By объясняет это поведение.

Неопределенный результирующий набор возвращается, когда один или несколько неагрегированных столбцов в предложении SELECT не перечислены в GROUP.Пункт BY.Столбцы, перечисленные в предложении SELECT, но исключенные из предложения GROUP BY, возвращают бессмысленные значения, поскольку они являются значениями столбцов, выбранными неопределенно из всех предварительно агрегированных строк.

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

SELECT loc_name, MIN( ((ACOS( . . . ) AS distance  
FROM locations
GROUP BY loc_name

И вы должны иметь возможность использовать этот оператор и выражение JOIN для имени местоположения и расстояния, чтобы получить другоенеобходимые вам столбцы.

Я обернул арифметику в функцию с именем "distance", затем

SELECT L1.*, C.*
FROM locations L1
INNER JOIN (SELECT L2.loc_name, 
                   MIN(distance($lat, $lon, 
                                L2.loc_lat, L2.loc_lon)) AS distance
            FROM locations L2
            GROUP BY L2.loc_name) C
ON   L1.loc_name = C.loc_name
 AND C.distance  = distance($lat, $lon, 
                          L1.loc_lat, L1.loc_lon)

Вам нужно будет добавить информацию о вашем ограничивающем прямоугольнике.Я оставил это, пока пытался убедиться, что JOIN работает правильно.У меня был ненужный ORDER BY во внутреннем предложении SELECT, но это было предварительное предложение кофеина, поэтому я удалил его.

Вероятно, вам также понадобится индекс для loc_name, потому что он используется в GROUP BY.См. Документы MySQL для ОБЪЯСНИТЕ синтаксис .

...