MySQL 8.0 前無窗口函數,常用會話變量 @rownum 模擬 ROW_NUMBER()。但變量在會話級別**持久化**,若不顯式初始化會導致連續執行結果錯亂。本文通過實測揭示陷阱並給出標準寫法。
CREATE TABLE test (ID INT AUTO_INCREMENT PRIMARY KEY, Col1 VARCHAR(50), Col2 VARCHAR(50), Col3 INT, Col4 FLOAT); INSERT INTO test (Col1,Col2,Col3,Col4) VALUES (1,2,3,4),(1,2,6,5),(1,2,7,8),(1,'激發',5,7); -- 首次執行 SELECT @row := CASE WHEN @row IS NULL THEN 1 ELSE @row+1 END AS RNr, ID, Col1, Col2, Col3, Col4 FROM test; -- 結果 RNr 全為 1(因 @row 初始為 NULL,CASE 只取 THEN 1)
再次執行同語句,RNr 變為 2,3,4,5(會話變量已保留上次值)。
@row 屬於**當前連接會話**,連接不斷則值保留。@row IS NULL 為真,所有行走 THEN 1。@row 變為最後一行的值(4),下次執行直接走 ELSE @row+1。SELECT
@row := CASE WHEN @row IS NULL THEN 1 ELSE @row + 1 END AS RNr,
ID, Col1, Col2, Col3, Col4
FROM test
JOIN (SELECT @row := 0) AS vars; -- 衍生表保證每次查詢重新初始化
JOIN (SELECT @row := 0) 在查詢啟動時執行,**每次查詢獨立初始化**,不受上次殘留影響。SELECT
@rn := CASE WHEN @prev_col1 = Col1 THEN @rn + 1 ELSE 1 END AS rn,
@prev_col1 := Col1 AS Col1,
ID, Col2, Col3, Col4
FROM test
JOIN (SELECT @rn := 0, @prev_col1 := '') AS vars
ORDER BY Col1, ID; -- 必須先按分組字段排序
輸出:每個 Col1 組內編號從 1 開始。
-- 全局編號 SELECT ROW_NUMBER() OVER (ORDER BY ID) AS rn, * FROM test; -- 分組編號 SELECT ROW_NUMBER() OVER (PARTITION BY Col1 ORDER BY ID) AS rn, * FROM test;
JOIN (SELECT @var := init)),不要依賴外部 SET。ORDER BY 必須放在變量賦值**之前**生效(子查詢/衍生表排序後再賦值)。@rn_)避免與其他查詢衝突。