MySQL 8.0 之前**不支援標準窗口函數** ROW_NUMBER()、RANK()、DENSE_RANK() (Oracle 8i+、SQL Server 2005+、PostgreSQL 8.4+ 均已支援)。若需在查詢結果中顯示行號 (如分頁顯示序號、計算排名、定位特定排序位置的記錄),需採用變數模擬、臨時表或應用層處理。本文詳細對比三種方案優劣。
SET @rownum := 0;
SELECT
(@rownum := @rownum + 1) AS row_num,
t.*
FROM (
SELECT * FROM your_table ORDER BY sort_col
) t;
進階用法:定位特定值的排序位置**
SET @rownum := 0;
SELECT * FROM (
SELECT
(@rownum := @rownum + 1) AS row_num,
col1, col2
FROM your_table
ORDER BY sort_col
) ranked
WHERE col1 = 'target_value'; -- 獲取該值在排序後的行號
ROW_NUMBER() OVER (ORDER BY sort_col),語義清晰、優化器支援更好。CREATE TEMPORARY TABLE tmp_ranked (
id INT AUTO_INCREMENT PRIMARY KEY,
col1 TYPE, col2 TYPE, ...
) ENGINE=Memory; -- 或 InnoDB
INSERT INTO tmp_ranked (col1, col2, ...)
SELECT col1, col2, ... FROM your_table ORDER BY sort_col;
SELECT * FROM tmp_ranked WHERE col1 = 'target_value';
CREATE TEMPORARY TABLE、INSERT ... AUTO_INCREMENT 在 Statement 格式下主從可能不一致 (臨時表不複寫、自增值發散)。DROP TEMPORARY TABLE 清理,否則連接斷開自動清理但會話複用時殘留。在 PHP/Python/Java/Go 等應用代碼中遍歷結果集賦值行號:
# Python 範例
rows = cursor.fetchall()
for i, row in enumerate(rows, start=1):
row['row_num'] = i
process(row)
WHERE row_num BETWEEN 100 AND 200),需全量拉取後切片。SELECT
ROW_NUMBER() OVER (ORDER BY sort_col) AS row_num,
col1, col2
FROM your_table
ORDER BY sort_col;
-- 分組內行號
SELECT
ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY sort_col) AS group_row_num,
col1, col2
FROM your_table;
PARTITION BY、RANK()、DENSE_RANK()、NTILE() 等豐富功能。| 場景 | 推薦方案 | 備註 |
|---|---|---|
| MySQL 5.6/5.7、單查詢生成行號、結果集較小 | 用戶變數 @rownum | 最通用、無副作用 |
| MySQL 5.6/5.7、需在 SQL 中按行號過濾 (如分頁) | 用戶變數 + 子查詢/衍生表 | 注意衍生表物化開銷 (5.7+ 可優化) |
| MySQL 8.0+ | ROW_NUMBER() 窗口函數 | 標準、高效、功能全 |
| 極大結果集、僅應用層顯示序號 | 應用層枚舉 | 避免資料庫排序壓力 |
| 需持久化行號供多次查詢 | 實體表 + 觸發器/定時任務維護 | 臨時表不持久、不複寫 |
核心原則:能在應用層解決的行號生成,盡量不下推到資料庫;必須在 SQL 端完成時,**MySQL 8.0+ 用窗口函數,舊版用變數模擬,堅決避免臨時表 + AUTO_INCREMENT**。