MySQL 8.0 前無原生 LAG()、LEAD(),但可利用用戶變量在單次查詢中實現「取上一行/下一行欄位值」的效果,常用於同比環比、差值計算、分組邊界判斷等場景。
SELECT
@lag_value AS lag_col, -- 輸出上一行值 (第一行為 NULL/初始值)
@lag_value := target_col, -- 更新變量為當前行值,供下一行使用
t.*
FROM target_table t,
(SELECT @lag_value := 初始值) r
ORDER BY 排序欄位; -- 必須指定順序,否則結果不可預期
初始值選擇:
(SELECT @lag_value := 0) 或 NULL(SELECT @lag_value := '') 或 NULL範例:計算每日銷售額環比增長
SELECT
dt,
amount,
@prev_amount AS prev_amount,
@prev_amount := amount,
CASE WHEN @prev_amount IS NULL OR @prev_amount = 0 THEN NULL
ELSE ROUND((amount - @prev_amount) / @prev_amount * 100, 2)
END AS mom_growth_pct
FROM daily_sales,
(SELECT @prev_amount := NULL) r
ORDER BY dt;
MySQL 變量只能順序讀取,需「倒序 + LAG + 正序」三步走:
@rn := @rn + 1 按目標順序排序。SELECT dt, amount, lead_amount FROM (
SELECT
@lag_val AS lead_amount,
@lag_val := amount,
t.dt, t.amount, t.rn
FROM (
SELECT
@rn := @rn + 1 AS rn,
dt, amount
FROM daily_sales,
(SELECT @rn := 0) r
ORDER BY dt
) t,
(SELECT @lag_val := NULL) r
ORDER BY rn DESC
) t2
ORDER BY rn;
(SELECT @var := init) 顯式初始化,否則受會話殘留值影響。SELECT @var, @var:=col ... 先讀舊值、後賦新值;切勿寫成 @var:=col, @var。LIMIT 或物化視表。-- LAG
SELECT dt, amount,
LAG(amount) OVER (ORDER BY dt) AS prev_amount
FROM daily_sales;
-- LEAD
SELECT dt, amount,
LEAD(amount) OVER (ORDER BY dt) AS next_amount
FROM daily_sales;
-- 分組內 LAG/LEAD
SELECT dept, emp, salary,
LAG(salary) OVER (PARTITION BY dept ORDER BY salary) AS prev_in_dept
FROM employee;
結論: 變量模擬可作為舊版本臨時方案,但代碼脆弱、難維護。**生產環境強烈建議升級至 MySQL 8.0+**,獲得標準、高效、可維護的窗口函數支援。