MySQL中的sortingfunction
我不是MySQL的专家。 我需要找出客户的排名。 在这里,我为我的要求添加了相应的ANSI标准SQL查询。 请帮我把它转换成MySQL。
SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender], FirstName, Age, Gender FROM Person
有没有什么function可以在MySQL中找出排名?
一种select是使用排名variables,如下所示:
SELECT first_name, age, gender, @curRank := @curRank + 1 AS rank FROM person p, (SELECT @curRank := 0) r ORDER BY age;
(SELECT @curRank := 0)
部分允许variables初始化,而不需要单独的SET
命令。
testing用例:
CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1)); INSERT INTO person VALUES (1, 'Bob', 25, 'M'); INSERT INTO person VALUES (2, 'Jane', 20, 'F'); INSERT INTO person VALUES (3, 'Jack', 30, 'M'); INSERT INTO person VALUES (4, 'Bill', 32, 'M'); INSERT INTO person VALUES (5, 'Nick', 22, 'M'); INSERT INTO person VALUES (6, 'Kathy', 18, 'F'); INSERT INTO person VALUES (7, 'Steve', 36, 'M'); INSERT INTO person VALUES (8, 'Anne', 25, 'F');
结果:
+------------+------+--------+------+ | first_name | age | gender | rank | +------------+------+--------+------+ | Kathy | 18 | F | 1 | | Jane | 20 | F | 2 | | Nick | 22 | M | 3 | | Bob | 25 | M | 4 | | Anne | 25 | F | 5 | | Jack | 30 | M | 6 | | Bill | 32 | M | 7 | | Steve | 36 | M | 8 | +------------+------+--------+------+ 8 rows in set (0.02 sec)
这是一个通用的解决scheme,根据列对表进行sorting并赋予排名; 具有关系的行被赋予相同的等级(为此使用额外的variables):
SET @prev_value = NULL; SET @rank_count = 0; SELECT id, rank_column, CASE WHEN @prev_value = rank_column THEN @rank_count WHEN @prev_value := rank_column THEN @rank_count := @rank_count + 1 END AS rank FROM rank_table ORDER BY rank_column
请注意,第二个WHEN
子句中有两个赋值语句。 示例数据:
CREATE TABLE rank_table(id INT, rank_column INT); INSERT INTO rank_table (id, rank_column) VALUES (1, 10), (2, 20), (3, 30), (4, 30), (5, 30), (6, 40), (7, 50), (8, 50), (9, 50);
输出:
+------+-------------+------+ | id | rank_column | rank | +------+-------------+------+ | 1 | 10 | 1 | | 2 | 20 | 2 | | 3 | 30 | 3 | | 4 | 30 | 3 | | 5 | 30 | 3 | | 6 | 40 | 4 | | 7 | 50 | 5 | | 8 | 50 | 5 | | 9 | 50 | 5 | +------+-------------+------+
SQL小提琴
虽然最高的答案排名,它不分区,你可以做一个自我join得到整个事情分区:
SELECT a.first_name, a.age, a.gender, count(b.age)+1 as rank FROM person a left join person b on a.age>b.age and a.gender=b.gender group by a.first_name, a.age, a.gender
用例
CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1)); INSERT INTO person VALUES (1, 'Bob', 25, 'M'); INSERT INTO person VALUES (2, 'Jane', 20, 'F'); INSERT INTO person VALUES (3, 'Jack', 30, 'M'); INSERT INTO person VALUES (4, 'Bill', 32, 'M'); INSERT INTO person VALUES (5, 'Nick', 22, 'M'); INSERT INTO person VALUES (6, 'Kathy', 18, 'F'); INSERT INTO person VALUES (7, 'Steve', 36, 'M'); INSERT INTO person VALUES (8, 'Anne', 25, 'F');
答 :
Bill 32 M 4 Bob 25 M 2 Jack 30 M 3 Nick 22 M 1 Steve 36 M 5 Anne 25 F 3 Jane 20 F 2 Kathy 18 F 1
丹尼尔版本的一个调整来计算百分位数和级别。 同样的两个人也会得到相同的等级。
set @totalStudents = 0; select count(*) into @totalStudents from marksheets; SELECT id, score, @curRank := IF(@prevVal=score, @curRank, @studentNumber) AS rank, @percentile := IF(@prevVal=score, @percentile, (@totalStudents - @studentNumber + 1)/(@totalStudents)*100), @studentNumber := @studentNumber + 1 as studentNumber, @prevVal:=score FROM marksheets, ( SELECT @curRank :=0, @prevVal:=null, @studentNumber:=1, @percentile:=100 ) r ORDER BY score DESC
查询样本数据的结果 –
+----+-------+------+---------------+---------------+-----------------+ | id | score | rank | percentile | studentNumber | @prevVal:=score | +----+-------+------+---------------+---------------+-----------------+ | 10 | 98 | 1 | 100.000000000 | 2 | 98 | | 5 | 95 | 2 | 90.000000000 | 3 | 95 | | 6 | 91 | 3 | 80.000000000 | 4 | 91 | | 2 | 91 | 3 | 80.000000000 | 5 | 91 | | 8 | 90 | 5 | 60.000000000 | 6 | 90 | | 1 | 90 | 5 | 60.000000000 | 7 | 90 | | 9 | 84 | 7 | 40.000000000 | 8 | 84 | | 3 | 83 | 8 | 30.000000000 | 9 | 83 | | 4 | 72 | 9 | 20.000000000 | 10 | 72 | | 7 | 60 | 10 | 10.000000000 | 11 | 60 | +----+-------+------+---------------+---------------+-----------------+
丹尼尔和萨尔曼的答案相结合。 然而,排名不会像继续关系那样存在关系。 相反,它跳到下一个级别。 所以最大总是达到行数。
SELECT first_name, age, gender, IF(age=@_last_age,@curRank:=@curRank,@curRank:=@_sequence) AS rank, @_sequence:=@_sequence+1,@_last_age:=age FROM person p, (SELECT @curRank := 1, @_sequence:=1, @_last_age:=0) r ORDER BY age;
模式和testing用例:
CREATE TABLE person (id int, first_name varchar(20), age int, gender char(1)); INSERT INTO person VALUES (1, 'Bob', 25, 'M'); INSERT INTO person VALUES (2, 'Jane', 20, 'F'); INSERT INTO person VALUES (3, 'Jack', 30, 'M'); INSERT INTO person VALUES (4, 'Bill', 32, 'M'); INSERT INTO person VALUES (5, 'Nick', 22, 'M'); INSERT INTO person VALUES (6, 'Kathy', 18, 'F'); INSERT INTO person VALUES (7, 'Steve', 36, 'M'); INSERT INTO person VALUES (8, 'Anne', 25, 'F'); INSERT INTO person VALUES (9, 'Kamal', 25, 'M'); INSERT INTO person VALUES (10, 'Saman', 32, 'M');
输出:
+------------+------+--------+------+--------------------------+-----------------+ | first_name | age | gender | rank | @_sequence:=@_sequence+1 | @_last_age:=age | +------------+------+--------+------+--------------------------+-----------------+ | Kathy | 18 | F | 1 | 2 | 18 | | Jane | 20 | F | 2 | 3 | 20 | | Nick | 22 | M | 3 | 4 | 22 | | Kamal | 25 | M | 4 | 5 | 25 | | Anne | 25 | F | 4 | 6 | 25 | | Bob | 25 | M | 4 | 7 | 25 | | Jack | 30 | M | 7 | 8 | 30 | | Bill | 32 | M | 8 | 9 | 32 | | Saman | 32 | M | 8 | 10 | 32 | | Steve | 36 | M | 10 | 11 | 36 | +------------+------+--------+------+--------------------------+-----------------+
@Sam,你的观点是优秀的概念,但我想你误解了MySQL文档在被引用的页面上说的是什么 – 或者我误解了:-) – 我只是想添加这个,所以如果有人感到不舒服@丹尼尔的回答他们会更放心,或者至less深入一点。
您看到SELECT内部的“@curRank:= @curRank + 1 AS排名”不是“一个语句”,它是语句的一个“primefaces”部分,因此它应该是安全的。
您引用的文档继续显示在语句的2(primefaces)部分中的相同用户定义variables(例如,“SELECT @curRank,@curRank:= @curRank + 1 AS rank”)的示例。
有人可能会认为@curRank在@Daniel的答案中使用了两次:(1)“@curRank:= @curRank + 1 AS排名”和(2)“(SELECT @curRank:= 0)r”用法是FROM子句的一部分,我敢肯定它是保证被首先评估; 基本上使它成为第二个和之前的陈述。
事实上,在你引用的那个MySQL文档页面上,你会在注释中看到相同的解决scheme – 它可能是@Daniel从中得到的; 是的,我知道这是评论,但它是官方文档页面上的评论,并确实有一定的权重。
如果你想排名只有一个人,你可以做到以下几点:
SELECT COUNT(Age) + 1 FROM PERSON WHERE(Age < age_to_rank)
这个排名对应于oracle的RANK函数(如果你有同龄人,他们得到相同的排名,而排名在后面是不连续的)。
这比在子查询中使用上述解决scheme中的一种更快一些,并且从中进行select以获得一个人的排名。
这可以用来排名每个人,但比上述解决scheme慢。
SELECT Age AS age_var, ( SELECT COUNT(Age) + 1 FROM Person WHERE (Age < age_var) ) AS rank FROM Person