与这个问题有关。
其实免得说我们要解决mysql中的分组排序问题。我们有一个表,每行代表一个实体,属于一个组。我们希望根据每个组单独的属性为每个实体分配一个等级。稍后我们可以对排名进行各种操作,例如要求每个组的前 10 个实体也满足另一个条件,等等。
例如,实体可以是根据他们最喜欢的编程语言属于不同“组”的程序员。然后每个程序员都有一个声誉(比如说在论坛上)。我们想添加一个额外的字段,该字段将是基于下降声誉的程序员排名。我们希望为每个组独立地执行此操作。
gid | repu | name |
1 1 john
1 3 anna
2 2 scot
2 1 leni
成为
gid | repu | name | rank
1 3 anna 1
1 1 john 2
2 2 scot 1
2 1 leni 2
现在让我们也要求我们不想使用基于会话变量的解决方案。是的,它们工作得很好,但它们显然违反了 mysql 的要求,即不在同一语句上读写会话变量。(见这里)
现在这篇文章中提出的解决方案说
-- SOL #1 (SELF-JOIN)
SELECT a.*, count(*) as row_number FROM test a
JOIN test b ON a.gid = b.gid AND a.repu <= b.repu
GROUP BY a.gid, a.repu
这几乎可以做到这一点。我的一些问题是,这是合法的 SQL 还是违反了任何标准或 mysql 怪癖?是否保证它可以在 mysql 上运行?
我在这里读到的另一个解决方案是,这对我来说更像是一种黑魔法,但看起来更优雅
-- SOL #2 (SUBQUERY)
SELECT t.* ,
( SELECT COUNT(*) + 1
FROM test
WHERE repu > t.repu AND gid = t.gid
) AS rank
FROM test AS t
ORDER BY gid ASC, rank ASC
这使用了一个引用外部表的子查询,并且也可以做到这一点。有人能解释一下这个是如何工作的吗?
此外,这里的问题与解决方案 #1 相同。
加上对评估两个提议的解决方案的性能/兼容性的任何评论。
编辑:其他方法,供参考
从这篇文章中,会话变量方法的一种变体。警告:这是我要避免的。请注意,在单个语句中,@rand 和 @partition 会话变量被读取(在 WHEN 和 THEN 之后的情况下)和写入(在 THEN AND ELSE 之后的情况下以及在初始化变量的下一个子查询中)。
-- SOL #3 (SESSION VARIABLES / ANTIPATTERN)
SELECT t.*, ( CASE gid
WHEN @partition THEN @rank := @rank + 1
ELSE @rank := 1 AND @partition := gid ) AS rank
FROM test t,
(SELECT @rank := 0, @partition := '') tmp
ORDER BY gid ASC, repu DESC
这里还有一个基于集合的解决方案,相当复杂,由下面的一位同事发布。
-- SOL #4 (SET BASED)
SELECT x.*, FIND_IN_SET(CONCAT(x.gid,':',x.repu), y.c) rank
FROM test x
JOIN (
SELECT GROUP_CONCAT(DISTINCT CONCAT(gid,':',repu) ORDER BY gid, repu DESC) c
FROM test GROUP BY gid
) y ON FIND_IN_SET(CONCAT(x.gid,':',x.repu), y.c)