在編寫 MySQL 預存程序時,DECLARE 與 SET @var 定義的變量常讓開發者困惑。本文透過實例演示,徹底釐清「局部變量」與「會話變量」的作用域、生命週期、可見性差異,助你避開共享狀態導致的 Bug。
| 特性 | DECLARE 變量 | @ 會話變量 |
|---|---|---|
| 作用域 | 僅在 BEGIN ... END 區塊內 | 整個會話(連線)期間 |
| 生命週期 | 程序結束即銷毀 | 連線斷開或顯式清除前持久 |
| 可見性 | 僅當前程序可見 | 同一連線的所有程序、SQL 可見 |
| 語法 | DECLARE var_name type [DEFAULT value]; | SET @var_name = value;(無需宣告) |
| 類型檢查 | 強型別,宣告時固定 | 動態型別,賦值時決定 |
| 典型用途 | 暫存計算、流程控制、遊標變量 | 跨程序傳值、會話級配置、除錯追蹤 |
-- 1. 建立測試程序
DROP PROCEDURE IF EXISTS temp;
DELIMITER //
CREATE PROCEDURE temp()
BEGIN
DECLARE a INT DEFAULT 1; -- 局部變量
SET a = a + 1;
SET @b = @b + 1; -- 會話變量
SELECT a, @b;
END //
DELIMITER ;
-- 2. 初始化會話變量
SET @b = 1;
-- 3. 連續呼叫三次
CALL temp(); -- 結果:a=2, @b=2
CALL temp(); -- 結果:a=2, @b=3
CALL temp(); -- 結果:a=2, @b=4
觀察:
a 每次呼叫都重新初始化為 1,遞增後為 2,不保留上次呼叫的值。@b 在會話層面持久化,每次呼叫在上次基礎上遞增,跨呼叫共享狀態。CREATE PROCEDURE outer_proc()
BEGIN
SET @shared = 'init';
CALL inner_proc();
SELECT @shared; -- 可見 inner_proc 的修改
END;
CREATE PROCEDURE inner_proc()
BEGIN
SET @shared = 'modified';
-- 也可直接 SELECT @shared 讀取 outer 設定的值
END;
這使得會話變量成為「跨程序通信」的輕量管道,但也帶來隱性耦合風險:程序不再純粹,副作用難以追蹤。
DECLARE:封裝邏輯、避免副作用、利於測試與並發。v_ 前綴(如 v_count),會話變量用 @sv_(如 @sv_config),一眼辨識作用域。SET @var = NULL; 或連線池回收時重置,防止殘留汙染後續業務。SET @x = 1 等同於 DECLARE x INT DEFAULT 1」 → 錯,前者無類型約束、作用域全域。@@global.xxx 系統變量。DECLARE 可以在程序中間宣告」 → 必須在 BEGIN 之後、任何語句之前集中宣告。理解變量作用域是編寫健壯預存程序的基石。堅持「高內聚、低耦合」,讓 DECLARE 成為主力,會話變量只作精準補刀,你的程序將更易維護、除錯與擴展。