MySQL 8.0 前不支援標準 ROW_NUMBER() 窗口函數,需變通實現結果集行號。8.0+ 已原生支援,但舊版本仍廣泛使用。
SELECT
ROW_NUMBER() OVER (ORDER BY create_time) AS rn,
id, username, create_time
FROM users;
-- 支援 PARTITION BY 分組編號
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dept_rn
SET @rownum = 0;
SELECT
(@rownum := @rownum + 1) AS rn,
id, username, create_time
FROM users
ORDER BY create_time;
SELECT * FROM (
SELECT
(@rownum := @rownum + 1) AS rn,
t.*
FROM (SELECT * FROM users ORDER BY create_time) t
CROSS JOIN (SELECT @rownum := 0) r
) ranked
WHERE rn BETWEEN 11 AND 20; -- 分頁應用
SET @rn = 0;
SELECT * FROM (
SELECT
(@rn := @rn + 1) AS rank,
id, score
FROM scores
ORDER BY score DESC
) t
WHERE id = 1001; -- 返回 id=1001 的排名
CROSS JOIN (SELECT @rn := 0) 初始化。LIMIT 下推優化。
CREATE TEMPORARY TABLE tmp_ranked (
rn INT AUTO_INCREMENT PRIMARY KEY,
id INT,
username VARCHAR(50),
create_time DATETIME
) ENGINE=Memory;
INSERT INTO tmp_ranked (id, username, create_time)
SELECT id, username, create_time FROM users ORDER BY create_time;
SELECT * FROM tmp_ranked WHERE rn BETWEEN 11 AND 20;
優點:行號持久化、可索引。缺點:需建表權限、臨時表在主從複制可能有問題 (STATEMENT 格式)。
| 版本 | 推薦方案 | 優點 | 缺點 |
|---|---|---|---|
| 8.0+ | ROW_NUMBER() OVER | 標準、支援分組、效能優 | 需升級 |
| 5.7/5.6 | 用戶變數 @rownum | 無需建表、語法簡單 | 順序不保證、不支援分組、複制風險 |
| 所有版本 | 臨時表 AUTO_INCREMENT | 行號持久、可二次查詢 | 額外開銷、複制風險 |