简体   繁体   English

MySQL中的计数或条件限制

[英]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. 这是非常简单的逻辑:它显示了存储在数据库中的最近5个位置。 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. 诀窍是我希望它返回最近的5个地方或两英里内的所有地方,以较大者为准。 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. 换句话说,我希望用户能够至少看到两英里内的所有地方,但如果该半径内没有5个地方,我希望他们显示最接近的5个。

Let's use these for sample data sets. 让我们将它们用于样本数据集。 Set 1: 第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: 第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. 在第一组中,我想要返回第3,4,25,5和25行。在第二组中,我想要显示3,4,5,6,9,2和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. 我知道我总是可以将查询限制在100个位置,并通过PHP中的结果集进行过滤...但我想知道是否有更有效的方法在SQL中正确执行。

In summary, the way to do this is to run both the queries and take a UNION of the sets. 总之,执行此操作的方法是运行两个查询并获取集合的UNION。 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. 这样做的确很少有性能损失,因为如果结果中确实有超过5行,则已经需要第一组(可以产生> 5行)。

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: 为了说明,我只使用同一样本表中的2列,而不是使用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);

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. 第一个UNION部分挑出了<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. 下一部分选择前5名并假设你有一个索引,这是很容易收集的。 The combination (UNION) of both parts very quick. 两个部分的组合(UNION)非常快。

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. 然后在PHP中你可以跳到阵列中的第5位(这将是距离第5个最远的地方,因为你已经按距离订购了查询)并检查它是否在2英里内。 If its then return the first 5 and if not then return all of them. 如果它然后返回前5个,如果没有则返回所有它们。

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. 不管怎样,最近的地方都会先拉(按asc排序),所以没关系。

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. LIMIT是从数据库中提取的最大结果,因此用户获得的最大位置是5,可能更少但不是更多。

You can do definitely use mysql to limit (and is encouraged) with order: 你可以肯定使用mysql限制(并鼓励)订单:

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

the "ASC" tells the query to order the rows in ascending fashion (smallest to highest). “ASC”告诉查询以递增的方式(从最小到最高)对行进行排序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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