0
.

Сначала я должен объявить, что я крайний новичок. Я только что использовал PHP и MySQL около 4 недель. Примите мои извинения заранее, если я не правильно отформатировал этот вопрос или не использовал надлежащие условия.MySQL. Выбор неправильного значения столбца в группе. По запросу для локатора хранилища. Приложение Google Maps.

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

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

Мое приложение требует, чтобы было только ближайшее местонахождение каждой сети ресторанов в пределах указанного пользователем расстояния, которое будет возвращено и отображено. Я добавил оператор GROUP BY, чтобы выполнить это. Правильное количество записей возвращается с правильным именем пользователя и расстоянием от пользователя. Однако все остальные поля никогда не верны. Они кажутся случайным образом выбранными из других записей, которые находятся за пределами значения MIN. Например, первая запись возвращается для DAIRY QUEEN на расстоянии 4,38 мили - это правильно. Тем не менее, адрес, штат, город и т. Д. Для DAIRY QUEEN на 4.38 миль неверны.

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

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

Заметки о моем коде, который не так страшно, как она выглядит, и не нужно понимать, чтобы решить мою проблему:

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

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

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

Благодарим за вас за помощь. Я действительно всего 4 недели в mySQL и PHP, и, как вы можете видеть, в моей голове?


Мой вопрос сводится к этому. Как этот SELECT должен быть изменен, чтобы возвращать только одну запись таблицы местоположений с соответствующими полями для каждого 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 

ответ

0

Вы прошли долгий путь за четыре недели. Это помогает включать минимальные заявления 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 выражение на название места и расстояния, чтобы получить другие столбцы вам нужно.

Я завернул арифметику в функции с именем «расстояние», то

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, но это было предложение pre-caffeine, поэтому я удалил его.

Возможно, вам понадобится указатель на loc_name, потому что он используется в GROUP BY. См. Документы MySQL для EXPLAIN syntax.