Ограничение по количеству ИЛИ условие в MySQL - PullRequest
4 голосов
/ 03 марта 2012

Я делаю веб-приложение, которое использует геолокацию, и я поднимаю вид «близлежащих мест». Это довольно простая логика: она показывает 5 ближайших мест, которые хранятся в базе данных. Идеально подходит.

Хитрость в том, что я хочу, чтобы он возвращал ближайшие 5 мест ИЛИ все места в пределах двух миль, в зависимости от того, что больше. Другими словами, я хочу, чтобы пользователь мог видеть по крайней мере все места в двух милях, но если в этом радиусе нет 5 мест, я хочу, чтобы они показали ближайшие 5.

Давайте использовать их для примеров наборов данных. Комплект 1:

| id | name                 | distance  |
+----+----------------------+-----------+
| 3  | Earl of Sandwich     | 0.3       |
| 4  | Nails 'n More        | 0.8       |
| 22 | City Hotel           | 1.7       |
| 5  | Mighty Medicine      | 2.1       |
| 25 | Wonder Wings         | 2.5       |
| 6  | Jean Warehouse       | 2.7       |
| 9  | Ship Safe & Speedy   | 2.9       |
| 2  | Bagel Bonus          | 4.1       |
+----+----------------------+-----------+

Набор 2:

| id | name                 | distance  |
+----+----------------------+-----------+
| 3  | Earl of Sandwich     | 0.1       |
| 4  | Nails 'n More        | 0.2       |
| 5  | Mighty Medicine      | 0.5       |
| 6  | Jean Warehouse       | 0.7       |
| 9  | Ship Safe & Speedy   | 0.9       |
| 2  | Bagel Bonus          | 1.2       |
| 22 | City Hotel           | 1.7       |
| 25 | Wonder Wings         | 2.1       |
+----+----------------------+-----------+

В первом наборе я хотел бы вернуть строки 3, 4, 22, 5 и 25. Во втором наборе я хотел бы показать 3, 4, 5, 6, 9, 2 и 22.

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

Ответы [ 4 ]

3 голосов
/ 03 марта 2012

Таким образом, способ сделать это состоит в том, чтобы выполнить оба запроса и взять UNION наборов. Вот и все. Это действительно очень мало снижает производительность, потому что первый набор (который может создать> 5 строк) уже требуется, если в результате действительно более 5 строк.

Подробно - оригинальный ответ ниже

Для иллюстрации, вместо использования двух наборов данных, я просто использую 2 столбца в той же самой таблице примеров, для которых запрос показан ниже:

drop table if exists tbl1;
create table tbl1 (
id int,
name varchar(100),
distance1 float,
distance2 float
);
insert tbl1 values
( 3  , 'Earl of Sandwich   ', 0.3, 0.1),
( 4  , 'Nails ''n More     ', 0.8, 0.2),
( 22 , 'City Hotel         ', 1.7, 1.7),
( 5  , 'Mighty Medicine    ', 2.1, 0.5),
( 25 , 'Wonder Wings       ', 2.5, 2.1),
( 6  , 'Jean Warehouse     ', 2.7, 0.7),
( 9  , 'Ship Safe & Speedy ', 2.9, 0.9),
( 2  , 'Bagel Bonus        ', 4.1, 1.2);

А запросы и результаты:

/* query 1 */
select id, name, distance1
from (
    select *
    from tbl1
    where distance1 <= 2.0
    order by distance1) a
union
select id, name, distance1
from (
    select *
     from tbl1
    order by distance1
     limit 5) b;

/* result 1 */
id;name;distance1
3;Earl of Sandwich   ;0.3
4;Nails 'n More     ;0.8
22;City Hotel         ;1.7
5;Mighty Medicine    ;2.1
25;Wonder Wings       ;2.5

/* query 2 */
select id, name, distance2
from (
    select *
    from tbl1
    where distance2 <= 2.0
    order by distance2) a
union
select id, name, distance2
from (
    select *
     from tbl1
    order by distance2
     limit 5) b;

/* result 2 */
id;name;distance2
3;Earl of Sandwich   ;0.1
4;Nails 'n More     ;0.2
5;Mighty Medicine    ;0.5
6;Jean Warehouse     ;0.7
9;Ship Safe & Speedy ;0.9
2;Bagel Bonus        ;1.2
22;City Hotel         ;1.7

Производительность этого запроса так же высока, как и у него.
Первая часть UNION выбирает те, что <2 км. Это необходимо, так как вы хотите все совпадения. <br> Следующая часть выбирает топ-5 и, если у вас есть индекс, собирать его тривиально. Сочетание (UNION) обеих частей очень быстрое.

0 голосов
/ 03 марта 2012
SELECT * FROM places ORDER BY distance ASC LIMIT 5

Ближайшие места будут вытянуты первыми в любом случае (по заказу asc), так что это не имеет значения.

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

0 голосов
/ 03 марта 2012

Вы действительно можете использовать mysql для ограничения (и приветствуется) с помощью порядка:

SELECT * FROM `table` ORDER BY `distance` ASC LIMIT 5;

«ASC» указывает запросу упорядочить строки в порядке возрастания (от наименьшего к наибольшему).

0 голосов
/ 03 марта 2012

Если бы это был я, ваш запрос вернул бы все местоположения, упорядоченные по расстоянию. Затем в PHP вы можете перейти на позицию 5 в массиве (это будет 5-е самое дальнее место, потому что вы заказали запрос по расстоянию) и проверить, находится ли он в пределах 2 миль. Если это так, верните первые 5, а если нет, то верните все из них.

Таким образом, ваш запрос будет просто:

SELECT id, name, distance FROM places ORDER BY distance ASC
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...