简体   繁体   中英

Limit by count OR condition in MySQL

I'm making a web app that uses geolocation, and I'm whipping up a "nearby places" view. It's pretty simple logic: it shows the closest 5 places that are stored in the database. Perfect.

The trick is that I want it to return the closest 5 places OR all of the places within two miles, whichever is greater. In other words, I want the user to be able to see at least all the places within two miles, but if there aren't 5 places in within that radius, I want them to show the nearest 5.

Let's use these for sample data sets. Set 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       |
+----+----------------------+-----------+

Set 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       |
+----+----------------------+-----------+

In the first set, I'd want to return rows 3, 4, 22, 5, and 25. In the second set, I'd want to show 3, 4, 5, 6, 9, 2, and 22.

I know I could just always limit the query to, say, 100 places, and go through the result set in PHP to filter... but I'm wondering if there's a more efficient way to do it right in SQL.

In summary, the way to do this is to run both the queries and take a UNION of the sets. That's it. There really is very little performance loss doing this because the first set (that can produce >5 rows) is already required if there really are more than 5 rows in the result.

In detail - original answer below

For illustration, instead of using 2 data sets, I just use 2 columns in this same sample table, for which the query is shown further below:

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);

And the queries and results:

/* 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

The performance of this query is as good as it gets.
The first UNION part picks out the ones <2km. This is necessary since you want all the matches.
The next part selects the top 5 and assuming you have an index, this is trivial to collect. The combination (UNION) of both parts very quick.

If it was me, I would have your query return all of the locations ordered by distance. Then in PHP you can jump to position 5 in the array (this will be the 5th furthest away place because you have ordered the query by distance) and check if it is within 2 miles. If its then return the first 5 and if not then return all of them.

So your query would simply be:

SELECT id, name, distance FROM places ORDER BY distance ASC
SELECT * FROM places ORDER BY distance ASC LIMIT 5

Nearest places will be pulled first anyway (order by asc), so it doesn't matter.

LIMIT is the maximum results to be pulled from the database, so the maximum of places which the user will get is 5, might be less but not more.

You can do definitely use mysql to limit (and is encouraged) with order:

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

the "ASC" tells the query to order the rows in ascending fashion (smallest to highest).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM