問題描述
需求:
查詢出每月 order_amount(訂單金額)
排行前3的記錄。
例如對於2019-02
,查詢結果中就應該是這3條:
解決方法
MySQL 5.7 和 MySQL 8.0 有不同的處理方法。
1. MySQL 5.7
我們先寫一個查詢語句。
根據 order_date
中的年、月
,和order_amount
進行降序排列。
然后,添加一個新列:order_amount
(本條記錄在本月中的名次)。
執行結果:
可以看到,根據年、月、訂單金額
排序了,還多了一列order_rank
,顯示出了本條記錄在本月的訂單金額排名情況。
上面SQL中比較個性的是這部分:
@current_month
和@order_rank
是我們自定義的變量。
使用 :=
可以動態創建一個變量,而不需要使用 set
命令。
這句的含義:
取得order_date
中的月份值,賦值給current_month
,這樣就可以跟蹤每個月份。
這句的含義:
比較 current_month
和本條記錄中的月份,如果一樣,order_rank
自增1,否則,置為1。
注意,@current_month
是在 @order_rank
的后面,例如執行到這條記錄時:
if
判斷中,MONTH(order_date)
值為 2,而 current_month
值為 1,還是上條記錄設置的。
接下來,把上面的SQL語句作為一個子查詢,然后使用一個 where
條件就可以輕松拿到每組的 top 3。
最終語句:
執行結果:
2. MySQL 8
MySQL 8 引入了一個 rank()
函數,可以更簡便的實現排行的功能。
執行結果:
效果和 5.7 中的方法是一致的。
我們看下語句中的 rank()
方法:
-
PARTITION BY
是指定分區依據,這里是根據訂單的年、月
進行分區。 -
ORDER BY
指定了分區內的排序依據,這里是根據訂單的年、月、金額
進行降序排列。
這樣就會自動計算出排行數值。
需要注意的是,這個地方和 5.7 的方法不一樣:
就是參與排序的幾個值一樣的時候,rank
值是一樣的。
最終的SQL語句:
翻譯整理自:
如果您有興趣實踐一下,在公眾號“性能與架構”中發送消息:200106,會回復實踐筆記的下載地址,包含建表語句、測試數據、MySQL5.7和8.0的這2個查詢語句。
推薦閱讀:
- 高並發案例 - 庫存超發問題
- 異地多活架構
- MySQL order by 是怎么工作的?
- 如何判斷一個元素是否存在於一個億級數據集中?
- Zookeeper vs Etcd
- MySQL8 的 Hash join 算法