MySQL 8.0 前無內建 ROW_NUMBER() 窗口函數(Oracle 8i+、SQL Server 2005+ 已支援)。開發常需「按某欄位排序後,取得某行在排序中的名次」或「分頁顯示行號」。
SET @rownum := 0;
SELECT @rownum := @rownum + 1 AS row_num, t.*
FROM (
SELECT * FROM articles ORDER BY publish_time DESC
) t;
進階:取得特定鍵值的排名
SET @rownum := 0;
SELECT * FROM (
SELECT @rownum := @rownum + 1 AS row_num, id, title
FROM articles ORDER BY publish_time DESC
) ranked
WHERE id = 12345; -- 直接得到該文章在最新排序中的名次
CREATE TEMPORARY TABLE tmp_ranked (
row_num INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
id INT UNSIGNED NOT NULL,
title VARCHAR(200)
) ENGINE=Memory;
INSERT INTO tmp_ranked (id, title)
SELECT id, title FROM articles ORDER BY publish_time DESC;
SELECT * FROM tmp_ranked WHERE id = 12345;
風險: 主從複寫環境下,CREATE TEMPORARY TABLE 可能導致從庫缺表、INSERT 順序不一致,慎用。
在 PHP/Python/Java/Go 等應用程式中遍歷 ResultSet,自行維護計數器。適合:
SELECT
ROW_NUMBER() OVER (ORDER BY publish_time DESC) AS row_num,
id, title
FROM articles;
支援 PARTITION BY 分組編號、RANK()、DENSE_RANK() 等完整窗口函數族。
| 方案 | 適用版本 | 效能 | 主從安全 | 維護性 |
|---|---|---|---|---|
| 使用者變數 | 5.0+ | 高(單掃描) | ✅ 安全 | 中(SQL 較長) |
| 臨時表 | 所有 | 中(雙寫入) | ⚠️ 風險 | 高(直觀) |
| 應用層 | 所有 | 視網路/記憶體 | ✅ 安全 | 高(程式碼可控) |
| 窗口函數 | 8.0+ | 最高(原生優化) | ✅ 安全 | 最高(標準 SQL) |
MySQL 5.x 時代請優先使用 使用者變數法,兼顧效能與主從安全;MySQL 8.0+ 請直接擁抱 標準窗口函數。臨時表法僅作為教學或極簡場景備選。無論哪種方案,定期檢視執行計畫(EXPLAIN) 確保排序欄位有索引支撐,才是行號查詢高效的根本。