gpt4 book ai didi

mysql - 获取每组最高/最小的记录

转载 作者:行者123 更新时间:2023-11-29 18:43:03 26 4
gpt4 key购买 nike

如何获得每组最高/最小的记录?

这个问题的前标题是“在带有子查询的复杂查询中使用排名(@Rank := @Rank + 1) - 它会起作用吗?”因为我正在寻找使用排名的解决方案,但是现在我发现 Bill 发布的解决方案要好得多。

原始问题:

我正在尝试编写一个查询,该查询将按照指定的顺序从每个组中获取最后一条记录:

SET @Rank=0;

select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from Table
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from Table
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField

表达式@Rank := @Rank + 1通常用于排名,但对我来说,当在2个子查询中使用时它看起来很可疑,但只初始化一次。会这样吗?

第二,它是否适用于多次评估的一个子查询?就像 where(或having)子句中的子查询(上面的另一种编写方式):

SET @Rank=0;

select Table.*, @Rank := @Rank + 1 AS Rank
from Table
having Rank = (select max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from Table as t0
order by OrderField
) as t
where t.GroupId = table.GroupId
)
order by OrderField

提前致谢!

最佳答案

要获取每组 OrderField 最高的行:

SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId AND t1.OrderField < t2.OrderField
WHERE t2.GroupId IS NULL
ORDER BY t1.OrderField; // not needed

如果同一组中有多个具有相同 OrderField 的记录,而您恰好需要其中一个,则可能需要扩展条件:

SELECT t1.*
FROM `Table` AS t1
LEFT OUTER JOIN `Table` AS t2
ON t1.GroupId = t2.GroupId
AND (t1.OrderField < t2.OrderField
OR (t1.OrderField = t2.OrderField AND t1.Id < t2.Id))
WHERE t2.GroupId IS NULL

换句话说,返回不存在具有相同 GroupId 和更大 OrderField 的其他行 t2 的行 t1 。当t2.*为NULL时,表示左外连接没有找到这样的匹配,因此t1具有OrderField中的最大值。组。

没有排名,没有子查询。如果您在 (GroupId, OrderField) 上有复合索引,这应该可以快速运行并通过“使用索引”优化对 t2 的访问。

<小时/>

关于性能,请参阅我对 Retrieving the last record in each group 的回答。我尝试了使用 Stack Overflow 数据转储的子查询方法和连接方法。差异非常显着:在我的测试中,join 方法的运行速度快了 278 倍。

拥有正确的索引以获得最佳结果非常重要!

关于使用 @Rank 变量的方法,它不会像您编写的那样工作,因为在查询处理第一个表后,@Rank 的值不会重置为零。我将向您展示一个示例。

我插入了一些虚拟数据,其中有一个额外的字段为空,除了我们知道每组最大的行之外:

select * from `Table`;

+---------+------------+------+
| GroupId | OrderField | foo |
+---------+------------+------+
| 10 | 10 | NULL |
| 10 | 20 | NULL |
| 10 | 30 | foo |
| 20 | 40 | NULL |
| 20 | 50 | NULL |
| 20 | 60 | foo |
+---------+------------+------+

我们可以证明第一组的排名增加到三,第二组的排名增加到六,并且内部查询正确返回这些:

select GroupId, max(Rank) AS MaxRank
from (
select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField) as t
group by GroupId

+---------+---------+
| GroupId | MaxRank |
+---------+---------+
| 10 | 3 |
| 20 | 6 |
+---------+---------+

现在运行没有连接条件的查询,以强制对所有行进行笛卡尔积,并且我们还获取所有列:

select s.*, t.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
-- on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;

+---------+---------+---------+------------+------+------+
| GroupId | MaxRank | GroupId | OrderField | foo | Rank |
+---------+---------+---------+------------+------+------+
| 10 | 3 | 10 | 10 | NULL | 7 |
| 20 | 6 | 10 | 10 | NULL | 7 |
| 10 | 3 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 20 | NULL | 8 |
| 20 | 6 | 10 | 30 | foo | 9 |
| 10 | 3 | 10 | 30 | foo | 9 |
| 10 | 3 | 20 | 40 | NULL | 10 |
| 20 | 6 | 20 | 40 | NULL | 10 |
| 10 | 3 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 50 | NULL | 11 |
| 20 | 6 | 20 | 60 | foo | 12 |
| 10 | 3 | 20 | 60 | foo | 12 |
+---------+---------+---------+------------+------+------+

从上面我们可以看出,每组的最大排名是正确的,但是随着处理第二个派生表,@Rank 继续增加,达到 7 或更高。因此,第二个派生表中的排名永远不会与第一个派生表中的排名重叠。

您必须添加另一个派生表,以强制 @Rank 在处理两个表之间重置为零(并希望优化器不会更改其评估表的顺序,或者使用 STRAIGHT_JOIN 来防止这种情况发生) ):

select s.*
from (select GroupId, max(Rank) AS MaxRank
from (select GroupId, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as t
group by GroupId) as t
join (select @Rank := 0) r -- RESET @Rank TO ZERO HERE
join (
select *, @Rank := @Rank + 1 AS Rank
from `Table`
order by OrderField
) as s
on t.GroupId = s.GroupId and t.MaxRank = s.Rank
order by OrderField;

+---------+------------+------+------+
| GroupId | OrderField | foo | Rank |
+---------+------------+------+------+
| 10 | 30 | foo | 3 |
| 20 | 60 | foo | 6 |
+---------+------------+------+------+

但是这个查询的优化很糟糕。它不能使用任何索引,它创建两个临时表,以困难的方式对它们进行排序,甚至使用连接缓冲区,因为它在连接临时表时也不能使用索引。这是 EXPLAIN 的示例输出:

+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| 1 | PRIMARY | <derived4> | system | NULL | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | |
| 1 | PRIMARY | <derived5> | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using join buffer |
| 5 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 4 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary; Using filesort |
| 3 | DERIVED | Table | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+

而我使用左外连接的解决方案优化得更好。它不使用临时表,甚至报告“使用索引”,这意味着它可以仅使用索引来解析连接,而无需接触数据。

+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 6 | Using filesort |
| 1 | SIMPLE | t2 | ref | GroupId | GroupId | 5 | test.t1.GroupId | 1 | Using where; Using index |
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+

您可能会在博客上看到人们声称“联接使 SQL 变慢”,但这是无稽之谈。优化不当会导致 SQL 变慢。

关于mysql - 获取每组最高/最小<whatever>的记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44823703/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com