首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 模擬 ROW_NUMBER() 分頁排序:用戶變量實戰與避坑指南

     編輯:MySQL 資料庫相關知識

背景:MySQL 8.0 前無原生窗口函數

MySQL 5.6/5.7 缺乏 ROW_NUMBER(),常用用戶變量模擬。但變量生命周期與會話綁定,**未顯式初始化會導致結果錯誤**。

問題複現

-- 建表測試
CREATE TABLE test (
    ID INT PRIMARY KEY AUTO_INCREMENT,
    Col1 VARCHAR(50), Col2 VARCHAR(50), Col3 INT, Col4 FLOAT
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO test(Col1,Col2,Col3,Col4) VALUES 
(1,2,3,4),(1,2,6,5),(1,2,7,8),(1,'激發',5,7);

-- 錯誤寫法:未初始化 @row
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,每行判斷都走 THEN 分支)

正確寫法:顯式初始化

-- 方法一:子查詢初始化(推薦,單條語句)
SELECT 
    @row:=@row+1 AS RNr,
    t.ID, t.Col1, t.Col2, t.Col3, t.Col4
FROM test t
JOIN (SELECT @row:=0) r;

-- 方法二:前置 SET 語句(分兩步,交互式調試用)
SET @row:=0;
SELECT @row:=@row+1 AS RNr, ID, Col1, Col2, Col3, Col4 FROM test;

進階應用:分組內排序(PARTITION BY 模擬)

-- 需求:按 Col1 分組,組內按 Col3 降序編號
SELECT 
    @rn:=CASE WHEN @prev=Col1 THEN @rn+1 ELSE 1 END AS row_num,
    @prev:=Col1 AS grp,
    t.*
FROM (
    SELECT * FROM test ORDER BY Col1, Col3 DESC
) t
JOIN (SELECT @rn:=0, @prev:='') vars;

MySQL 8.0+ 原生窗口函數寫法(對比)

-- 簡潔、標準、優化器友好
SELECT 
    ROW_NUMBER() OVER (PARTITION BY Col1 ORDER BY Col3 DESC) AS row_num,
    ID, Col1, Col2, Col3, Col4
FROM test;

避坑清單

坑點現象根因解決
變量未初始化序號全 1 或 亂序會話級變量殘留上次值JOIN (SELECT @var:=0) 強制初始化
ORDER BY 後賦值序號不按預期順序MySQL 不保證賦值順序 = 排序順序子查詢先排序,外層賦值
複雜查詢多次引用序號跳躍/重複優化器可能多次執行選項用 CTE 物化中間結果
主從複製不一致從庫序號不同基於語句複製時變量非確定性啟用 ROW 格式 / 升級 8.0 用原生函數

性能對比

  • 用戶變量:單線程串行執行,無法利用並行查詢,大數據集極慢
  • 原生窗口函數:優化器可推導索引利用,支持並行執行(8.0.20+)

結論:MySQL 5.6/5.7 維護期內,用戶變量模擬僅限於小數據集、臨時腳本、遷移過渡。**新建系統直接上 MySQL 8.0+,用標準窗口函數**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved