mysql中的组内排序
我有一个面板数据集:即,mysql中的组内排序,mysql,sql,sorting,rank,percentile,Mysql,Sql,Sorting,Rank,Percentile,我有一个面板数据集:即,时间,ID,和值。我想做一个排名的基础上的价值为每个日期。我只需运行以下命令即可实现排序: select * from tbl order by date, value 我的问题是,一旦表格以这种方式排序,我如何检索每个组的行号(也就是说,对于每个日期,我希望有一个名为ranking的列,从1到N) 例如: 输入: Date, ID, Value d1, id1, 2 d1, id2, 1 d2, id1, 10 d2, id2, 11 输出: Date, I
时间
,ID
,和值
。我想做一个排名的基础上的价值为每个日期。我只需运行以下命令即可实现排序:
select * from tbl order by date, value
我的问题是,一旦表格以这种方式排序,我如何检索每个组的行号(也就是说,对于每个日期,我希望有一个名为ranking的列,从1到N)
例如:
输入:
Date, ID, Value
d1, id1, 2
d1, id2, 1
d2, id1, 10
d2, id2, 11
输出:
Date, ID, Value, Rank
d1, id2, 1, 1
d1, id1, 2, 2
d2, id1, 10, 1
d2, id2, 11, 2
这会奏效吗
select [DATE],ID,Value,
(DENSE_RANK() OVER (
PARTITION BY ID
ORDER BY Date) )AS [DenseRank],
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY [Date] DESC) AS RN
from SomeTable
如果没有窗口函数,您可以自己排序
tbl
,并使用用户变量计算分区的排名(“日期”值):
SELECT "date", -- D) Desired columns
id,
value,
rank
FROM (SELECT "date", -- C) Rank by date
id,
value,
CASE COALESCE(@partition, "date")
WHEN "date" THEN @rank := @rank + 1
ELSE @rank := 1
END AS rank,
@partition := "date" AS dummy
FROM (SELECT @rank := 0 AS rank, -- A) User var init
@partition := NULL AS partition) dummy
STRAIGHT_JOIN
( SELECT "date", -- B) Ordering query
id,
value
FROM tbl
ORDER BY date, value) tbl_ordered;
更新
那么,这个查询在做什么
我们使用用户变量“循环”经过排序的结果集,根据结果集的哪个连续段(在@partition
中跟踪),递增或重置计数器(@rank
)
在查询A中,我们初始化两个用户变量。在查询B中,我们按照需要的顺序获取表的记录:首先按日期,然后按值A和B一起构成一个派生表,tbl_排序
,看起来像这样:
rank | partition | "date" | id | value
---- + --------- + ------ + ---- + -----
0 | NULL | d1 | id2 | 1
0 | NULL | d1 | id1 | 2
0 | NULL | d2 | id1 | 10
0 | NULL | d2 | id2 | 11
请记住,我们并不真正关心列dummy.rank
和dummy.partition
——它们只是我们初始化变量@rank
和@partition
时的意外情况
在queryC中,我们循环遍历派生表的记录。我们所做的或多或少是以下伪代码所做的:
rank = 0
partition = nil
foreach row in fetch_rows(sorted_query):
(date, id, value) = row
if partition is nil or partition == date:
rank += 1
else:
rank = 1
partition = date
stdout.write(date, id, value, rank, partition)
最后,queryD从C投影所有列,除了包含
@partition
的列(我们将其命名为dummy
,不需要显示)。我知道这是一个老问题,但这里有一个简短的答案:
SELECT w.*, if(
@preDate = w.date,
@rank := @rank + 1,
@rank := (@preDate :=w.date) = w.date
) rank
FROM tbl w
JOIN (SELECT @preDate := '' )a
ORDER BY date, value
请不要在SQL保留字后面命名列,例如DATE,即使出于说明目的。不,DATE不是保留字(因为有太多人滥用它,mysql允许DATE为非保留字)@ajreal:@pilcrow可能意味着
DATE
是标准的SQL保留字吗?我的意思是,我不确定是否有这样的东西。即使没有保留,使用date
或time
作为列名表也是不好的做法。与DATE()
函数或DATE
数据类型混淆太多。--MySQL允许将一些关键字用作不带引号的标识符,因为许多人以前使用过它们。示例如下所示:…
对于有关SQL Server的问题,您可以保留这样的答案。(尽管我必须说,您也应该更加注意您所回答的问题:分区应该在日期
之前完成,而不是在ID
之前完成,并且排序更可能是通过值
而不是通过任何其他方式完成的。)谢谢您。我正试图理解这个问题,但它有点复杂。你能对这里发生的事情做一点评论吗?谢谢你的帮助。解释得很清楚。。非常适合没有像我这样丰富SQL经验的人!绝妙的方法!重命名虚拟表中的分区attr,因为MySQL将变得混乱。-“@partition:=NULL作为分区”谢谢,@ninjabber。这种手动分区很快就会变得笨拙——当您想要基于三个或更多标准进行分区时,您会严重错过窗口/分析功能——但对于这个问题来说,它已经足够好了。