mysql之排名實現


前言:mysql沒有實現類似排名(rank)功能的函數。但是我們可以通過基數的查詢加上其他函數可是實現類似的功能。

題目:編寫一個 SQL 查詢來實現分數排名。

一:首先我們創建一張並插入一些數據如下,用於方便后面排名的演示。

CREATE TABLE `players` (
  `pid` int(2) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `age` int(2) NOT NULL,
  PRIMARY KEY (`pid`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;
 
INSERT INTO `players` (`pid`, `name`, `age`) VALUES
(1, 'Samual', 25),
(2, 'Vino', 20),
(3, 'John', 20),
(4, 'Andy', 22),
(5, 'Brian', 21),
(6, 'Dew', 24),
(7, 'Kris', 25),
(8, 'William', 26),
(9, 'George', 23),
(10, 'Peter', 19),
(11, 'Tom', 20),
(12, 'Andre', 20);

二:簡單排名,相同的年紀隨機分配排名次序

SELECT pid, name, age, @curRank := @curRank + 1 AS rank
FROM players p, (
SELECT @curRank := 0
) q
ORDER BY age

解題分析:聲明一個session級別的變量(注意在sql中聲明變量需要在變量名前加@)。

步驟一:在子查詢中聲明一個變量,並初始化為0。在select函數每記錄一行數據變量加1(在select函數后‘:=’為賦值操作)。

結果:

| PID |    NAME | AGE | RANK |
|-----|---------|-----|------|
|  10 |   Peter |  19 |    1 |
|  12 |   Andre |  20 |    2 |
|   2 |    Vino |  20 |    3 |
|   3 |    John |  20 |    4 |
|  11 |     Tom |  20 |    5 |
|   5 |   Brian |  21 |    6 |
|   4 |    Andy |  22 |    7 |
|   9 |  George |  23 |    8 |
|   6 |     Dew |  24 |    9 |
|   7 |    Kris |  25 |   10 |
|   1 |  Samual |  25 |   11 |
|   8 | William |  26 |   12 |

備注1:我們通過子查詢把聲明變量和select函數通過一條語句完成(利用子查詢)。我們也可以分離聲明變量和查詢分開如備注2

備注2:分離聲明變量和查詢分,通過兩條sql。

sql1
SET @curRank := 0;
sql2
SELECT pid, name, age, @curRank := @curRank + 1 AS rank
FROM players
ORDER BY age

三:相同的分數需有相同的排名名次,排名無間隙

SELECT pid, name, age, 
CASE 
WHEN @prevRank = age THEN @curRank 
WHEN @prevRank := age THEN @curRank := @curRank + 1
END AS rank
FROM players p, 
(SELECT @curRank :=0, @prevRank := NULL) r
ORDER BY age

結果

| PID |    NAME | AGE | RANK |
|-----|---------|-----|------|
|  10 |   Peter |  19 |    1 |
|  12 |   Andre |  20 |    2 |
|   2 |    Vino |  20 |    2 |
|   3 |    John |  20 |    2 |
|  11 |     Tom |  20 |    2 |
|   5 |   Brian |  21 |    3 |
|   4 |    Andy |  22 |    4 |
|   9 |  George |  23 |    5 |
|   6 |     Dew |  24 |    6 |
|   7 |    Kris |  25 |    7 |
|   1 |  Samual |  25 |    7 |
|   8 | William |  26 |    8 |

解題分析:通過格外一個變量,記錄上一條記錄的年紀。通過比較當前年齡和該變量是否相同來判斷排名是否加一。

備注3:分析sql

CASE 
    WHEN @prevRank = age THEN @curRank                     
    WHEN @prevRank := age THEN @curRank := @curRank + 1   
END 

第一個   WHEN @prevRank = age THEN @curRank  。 是判斷語句,用於判斷上一條數據的年紀和當前數據的年紀是否相等,如果為true,返回當前排名。如果為false,繼續執行下一條WHEN

第二個   WHEN @prevRank := age THEN @curRank := @curRank + 1 。是賦值,即把當前年齡賦值@prevRank(賦值操作總是返回為true),並排名加一。

四:相同的分數需有相同的排名名次,排名有間隙

SELECT pid, name, age, rank FROM
(SELECT pid, name, age,
@curRank := IF(@prevRank = age, @curRank, @incRank) AS rank, 
@incRank := @incRank + 1, 
@prevRank := age
FROM players p, (
SELECT @curRank :=0, @prevRank := NULL, @incRank := 1
) r 
ORDER BY age) s

結果

| PID |    NAME | AGE | RANK |
|-----|---------|-----|------|
|  10 |   Peter |  19 |    1 |
|  12 |   Andre |  20 |    2 |
|   2 |    Vino |  20 |    2 |
|   3 |    John |  20 |    2 |
|  11 |     Tom |  20 |    2 |
|   5 |   Brian |  21 |    6 |
|   4 |    Andy |  22 |    7 |
|   9 |  George |  23 |    8 |
|   6 |     Dew |  24 |    9 |
|   7 |    Kris |  25 |   10 |
|   1 |  Samual |  25 |   10 |
|   8 | William |  26 |   12 |

解題分析:在上一個sql的基礎上再另加一個變量,用於記錄select讀取的行數.(即標題2中的排名)。並用三目運算符來判斷使用哪個排名。

備注:mysql中IF(expr1,expr2,expr3)函數是我們熟悉的三目運算函數。即If expr1 is TRUE (expr1 <> 0 and expr1 <> NULL), IF() returns expr2. Otherwise, it returns expr3.

 

參考1:http://fellowtuts.com/mysql/query-to-obtain-rank-function-in-mysql/

參考2:https://dev.mysql.com/doc/refman/5.7/en/control-flow-functions.html#function_if

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM