MySQL 中的排名函数
我需要找出客户的等级。这里我根据我的要求添加相应的 ANSI 标准 SQL 查询。请帮我将其转换为 MySQL 。
SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],
FirstName,
Age,
Gender
FROM Person
MySQL有没有查询排名的函数?
I need to find out rank of customers. Here I am adding the corresponding ANSI standard SQL query for my requirement. Please help me to convert it to MySQL .
SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],
FirstName,
Age,
Gender
FROM Person
Is there any function to find out rank in MySQL?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
一种选择是使用排名变量,如下所示:
(SELECT @curRank := 0)
部分允许变量初始化,而无需单独的SET
命令。测试用例:
结果:
One option is to use a ranking variable, such as the following:
The
(SELECT @curRank := 0)
part allows the variable initialization without requiring a separateSET
command.Test case:
Result:
这是一个通用的解决方案,它将分区上的密集等级分配给行。它使用用户变量:
请注意,变量赋值放置在
CASE
表达式内。这(理论上)解决了评估顺序问题。添加IS NOT NULL
是为了处理数据类型转换和短路问题。PS:通过删除所有检查平局的条件,可以轻松地将其转换为分区上的行号。
db<>fiddle 演示
Here is a generic solution that assigns dense rank over partition to rows. It uses user variables:
Notice that the variable assignments are placed inside the
CASE
expression. This (in theory) takes care of order of evaluation issue. TheIS NOT NULL
is added to handle datatype conversion and short circuiting issues.PS: It can easily be converted to row number over partition by by removing all conditions that check for tie.
Demo on db<>fiddle
虽然投票最多的答案排名,但它不会分区,您可以进行自连接来对整个内容进行分区:
用例
答案:
While the most upvoted answer ranks, it doesn't partition, You can do a self Join to get the whole thing partitioned also:
Use Case
Answer:
对丹尼尔版本的调整,用于计算百分位数和排名。另外,两个分数相同的人将获得相同的排名。
示例数据的查询结果 -
A tweak of Daniel's version to calculate percentile along with rank. Also two people with same marks will get the same rank.
Results of the query for a sample data -
丹尼尔和萨尔曼的答案的结合。然而,排名不会给出,因为存在并列的连续序列。相反,它会跳过排名到下一个。所以最大总是达到行数。
架构和测试用例:
输出:
Combination of Daniel's and Salman's answer. However the rank will not give as continues sequence with ties exists . Instead it skips the rank to next. So maximum always reach row count.
Schema and Test Case:
Output:
MySQL 5.7
在 MySQL 5.7 中,您可以使用 JSON 和局部变量,如下所示,以模拟 RANK() OVER (PARTITION BY ..):
每个分区键,这存储 ROW_NUMBER()< /code> (
"rn-x"
),根据排序键的上一个值 ("pre-vx"
),以及上一个ROW_NUMBER( )
("pre-rn-x"
) 用于绑定行,以模拟RANK()
行为。此处使用 JSON 的好处是 PARTITION BY 子句不会影响查询的顺序。MySQL 8.0
从 MySQL 8 开始,您终于可以在 MySQL 中使用窗口函数:
https://dev.mysql.com/doc/refman/ 8.0/en/window-functions.html
您的查询可以以完全相同的方式编写:
MySQL 5.7
In MySQL 5.7, you can use JSON and local variables as follows, to emulate
RANK() OVER (PARTITION BY ..)
:Per partition key, this stores the
ROW_NUMBER()
("rn-x"
), previous value according to the sort key ("pre-v-x"
), as well as the previousROW_NUMBER()
("pre-rn-x"
) for tied rows, to emulateRANK()
behaviour. The benefit of using JSON here is that thePARTITION BY
clause does not influence the ordering of the query.MySQL 8.0
Starting with MySQL 8, you can finally use window functions also in MySQL:
https://dev.mysql.com/doc/refman/8.0/en/window-functions.html
Your query can be written exactly the same way:
@Sam,你的观点在概念上非常好,但我认为你误解了MySQL文档在引用页面上所说的内容——或者我误解了:-)——我只是想添加这一点,以便如果有人对@感到不舒服丹尼尔的回答他们会更放心,或者至少会更深入地挖掘。
您会看到
SELECT
内的"@curRank := @curRank + 1 ASrank"
不是“一个语句”,它是语句的一个“原子”部分,因此它应该是安全的。您引用的文档继续显示了语句的 2 个(原子)部分中相同的用户定义变量的示例,例如,
“SELECT @curRank, @curRank := @curRank + 1 AS rating”.
有人可能会说 @Daniel 的答案中使用了两次
@curRank
:(1)"@curRank := @curRank + 1 AS rating"
和 (2) < code>"(SELECT @curRank := 0) r" 但由于第二次用法是FROM
子句的一部分,我很确定它一定会首先被评估;本质上使它成为第二个也是前面的声明。事实上,在您引用的同一个 MySQL 文档页面上,您会在注释中看到相同的解决方案——这可能是 @Daniel 从中获得的;是的,我知道这是评论,但这是官方文档页面上的评论,确实有一定的分量。
@Sam, your point is excellent in concept but I think you misunderstood what the MySQL docs are saying on the referenced page -- or I misunderstand :-) -- and I just wanted to add this so that if someone feels uncomfortable with the @Daniel's answer they'll be more reassured or at least dig a little deeper.
You see the
"@curRank := @curRank + 1 AS rank"
inside theSELECT
is not "one statement", it's one "atomic" part of the statement so it should be safe.The document you reference goes on to show examples where the same user-defined variable in 2 (atomic) parts of the statement, for example,
"SELECT @curRank, @curRank := @curRank + 1 AS rank"
.One might argue that
@curRank
is used twice in @Daniel's answer: (1) the"@curRank := @curRank + 1 AS rank"
and (2) the"(SELECT @curRank := 0) r"
but since the second usage is part of theFROM
clause, I'm pretty sure it is guaranteed to be evaluated first; essentially making it a second, and preceding, statement.In fact, on that same MySQL docs page you referenced, you'll see the same solution in the comments -- it could be where @Daniel got it from; yeah, I know that it's the comments but it is comments on the official docs page and that does carry some weight.
确定给定值排名的最直接解决方案是计算其之前的值的数量。假设我们有以下值:
30
值均被视为第三40
值均被视为第六(排名)或第四(密集排名)现在回到原来的问题。以下是一些按 OP 中所述排序的示例数据(预期排名添加在右侧):
计算 的 RANK() OVER (PARTITION BY Gender ORDER BY Age) Sarah,您可以使用以下查询:
计算<全部<的
RANK() OVER (PARTITION BY Gender ORDER BY Age)
/strong> 行,您可以使用此查询:这是结果(连接值添加在右侧):
The most straight forward solution to determine the rank of a given value is to count the number of values before it. Suppose we have the following values:
30
values are considered 3rd40
values are considered 6th (rank) or 4th (dense rank)Now back to the original question. Here is some sample data which is sorted as described in OP (expected ranks are added on the right):
To calculate
RANK() OVER (PARTITION BY Gender ORDER BY Age)
for Sarah, you can use this query:To calculate
RANK() OVER (PARTITION BY Gender ORDER BY Age)
for All rows you can use this query:And here is the result (joined values are added on right):
如果你只想对一个人进行排名,你可以执行以下操作:
此排名对应于 oracle RANK 函数(如果你有相同年龄的人,他们会获得相同的排名,并且之后的排名是不连续的)。
它比在子查询中使用上述解决方案之一并从中进行选择以获得一个人的排名要快一点。
这可以用来对每个人进行排名,但它比上述解决方案慢。
If you want to rank just one person you can do the following:
This ranking corresponds to the oracle RANK function (Where if you have people with the same age they get the same rank, and the ranking after that is non-consecutive).
It's a little bit faster than using one of the above solutions in a subquery and selecting from that to get the ranking of one person.
This can be used to rank everyone but it's slower than the above solutions.
为了避免 Erandac 的答案中结合 Daniel 和 Salman 的答案中的“however”,可以使用以下“分区”之一解决方法”
此代码片段中第三个变体中的分区排名将返回连续的排名数字。这将产生类似于
rank() over partition by
结果的数据结构。作为示例,请参见下文。特别是,对于每个新的partitionRank,partitionSequence将始终从1开始,使用以下方法:To avoid the "however" in Erandac's answer in combination of Daniel's and Salman's answers, one may use one of the following "partition workarounds"
The partition ranking in the 3rd variant in this code snippet will return continous ranking numbers. this will lead to a data structur similar to the
rank() over partition by
result. As an example, see below. In particular, the partitionSequence will always start with 1 for each new partitionRank, using this method:对丹尼尔的答案进行调整,
一些 MySQL 版本在 IF 语句中抛出错误
所以我必须用大括号将 IF 条件测试包装在 Ranking 中
这 anly 计算排名但不计算 Dense_rank
In a tweak to Daniel's answer,
Some MySQL Versions throw errors at the IF statement
So I had to wrap the IF condition testing in Ranking with braces
This anly calculates ranking but not Dense_rank
该行将打印上面的选择查询;
1660616f-f908-4d7e-ab60-c00b77adaf96
对于日期列
对于时间戳列
This line will print the select above query;
1660616f-f908-4d7e-ab60-c00b77adaf96
For Date column
For timestamp column