获得每组最高/最小的logging
怎么做?
这个问题以前的标题是“ 在使用子查询的复杂查询中使用rank(@Rank:= @Rank + 1) – 是否可以工作? ”因为我正在寻找使用等级的解决scheme,但是现在我看到Bill发布的解决scheme是好多了。
原始问题:
我试图编写一个查询,将从每个组中给出一些定义的顺序最后一个logging:
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
expression式@Rank := @Rank + 1
通常用于排名,但对于我来说,在2 @Rank := @Rank + 1
查询中使用时看起来很可疑,但只初始化一次。 它会这样工作吗?
第二,它是否会与多次评估的子查询一起工作? 像子查询中的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! (note by Tomas)
( 由Tomas编辑:如果在同一组中有相同OrderField的更多logging,并且您只需要其中的一个,则可能需要扩展条件:
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时,表示左边的外部连接没有find这样的匹配,因此t1
中组的OrderField
值最大。
没有行列,没有子查询。 如果你在(GroupId, OrderField)
上有一个复合索引,这应该运行得很快,并且通过“使用索引”来优化对t2的访问。
关于performance,请参阅我的回答检索每个组中的最后一个logging 。 我尝试了一个子查询方法和使用堆栈溢出数据转储的联接方法。 差异是显着的:在我的testing中,连接方法跑了278倍。
重要的是你有正确的索引,以获得最好的结果!
关于使用@Rankvariables的方法,它不会像你写的那样工作,因为@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 | +---------+------------+------+------+
但是这个查询的优化是可怕的。 它不能使用任何索引,它会创build两个临时表,对它们进行艰难的sorting,甚至使用联接缓冲区,因为联接临时表时也不能使用索引。 这是来自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 | +----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
而我使用左外连接的解决scheme优化得更好。 它不使用临时表,甚至报告"Using index"
,这意味着它可以仅"Using index"
来parsing连接,而不会触及数据。
+----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+ | 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 | +----+-------------+-------+------+---------------+---------+---------+-----------------+------+--------------------------+
你可能会读博客上的人声称“joinSQL慢”,但这是无稽之谈。 糟糕的优化使SQL变慢。