變量

MySQL 提供了變量來保存數據,根據變量的作用範圍分為系統變量、會話變量和局部變量。

系統變量

系統變量就是全局變量,對所有的 MySQL 客户端都有效,通常會在服務器啓動時使用命令行上的選項或配置文件來設置系統變量。

查看變量

SHOW [GLOBAL | SESSION] VARIABLES
    [LIKE 'pattern' | WHERE expr]

-- 使用 GLOBAL 修飾語,展示系統變量,如果一個系統變量沒有值,則不顯示
-- 使用 SESSION 修飾語,展示當前會話變量,如果一個會話變量沒有值,則展示這個變量的系統變量的值
-- 默認使用 SESSION 修飾語

-- 查看指定的變量
SELECT @@[GLOBAL | SESSION].變量名; -- 默認為 SESSION

-- 設置變量值
set @@[GLOBAL | SESSION].變量名 = 值; -- 默認為 SESSION
-- 或者
set [GLOBAL | SESSION] 變量名 = 值; -- 默認為 SESSION

用户變量

用户變量是前綴為一個 @ 的變量,其生命週期和會話變量一致:都隨當前數據庫會話的創建而初始化,隨會話斷開(如退出客户端、關閉連接)而失效,不會跨會話保留,也不會在 MySQL 服務器重啓後存活。

-- 設置變量
set @name = 'Tom';

-- 查詢變量
select @name;

局部變量

局部變量(Local Variable)是僅在存儲過程、自定義函數、觸發器的代碼體(BEGIN ... END 塊內)有效的臨時變量, 僅服務於代碼塊內的邏輯處理(如臨時存儲中間結果、循環計數),代碼塊執行結束後立即銷燬。

-- 定義變量
DECLARE 變量名 數據類型 [DEFAULT 默認值];

-- 修改變量
- 方式1:用 SET 賦值(推薦,簡潔)
SET 變量名 = 值/表達式;

-- 方式2:用 SELECT INTO 賦值(查詢結果存入變量,需確保查詢返回單行)
SELECT 字段名 INTO 變量名 FROM 表名 WHERE 條件 LIMIT 1;


流程控制

判斷語句

IF ... THEN 語句

IF 條件1 THEN
    邏輯1;
ELSEIF 條件2 THEN
    邏輯2;
ELSE
    邏輯3; -- 所有條件不滿足時執行
END IF; -- 必須以 END IF 結束(注意空格)

-- 根據訂單金額計算折扣
DELIMITER //
CREATE FUNCTION fn_calculate_discount(order_amount DECIMAL(10,2))
RETURNS DECIMAL(3,2) -- 返回折扣率(0.8=8折)
DETERMINISTIC
BEGIN
    DECLARE discount DECIMAL(3,2);
    IF order_amount >= 1000 THEN
        SET discount = 0.8; -- 滿1000打8折
    ELSEIF order_amount >= 500 THEN
        SET discount = 0.9; -- 滿500打9折
    ELSE
        SET discount = 1.0; -- 不滿500無折扣
    END IF;
    RETURN discount;
END //
DELIMITER ;
-- 調用:SELECT fn_calculate_discount(800); -- 輸出 0.9

CASE 語句

CASE 變量/表達式
    WHEN 值1/條件1 THEN 邏輯1;
    WHEN 值2/條件2 THEN 邏輯2;
    ELSE 邏輯N;
END CASE;

-- 根據用户等級返回權益
DELIMITER //
CREATE FUNCTION fn_get_user_rights(level INT)
RETURNS VARCHAR(50)
DETERMINISTIC
BEGIN
    CASE level
        WHEN 1 THEN RETURN '普通會員:無權益';
        WHEN 2 THEN RETURN 'VIP會員:95折';
        WHEN 3 THEN RETURN '超級VIP:8折+免運費';
        ELSE RETURN '未知等級';
    END CASE;
END //
DELIMITER ;

-- 調用:SELECT fn_get_user_rights(3); -- 輸出 超級VIP:8折+免運費

循環語句

用於批量處理數據(如批量插入、批量更新),MySQL 支持 WHILE、REPEAT、LOOP 三種循環,配合 LEAVE(跳出循環)、ITERATE(跳過當前循環)使用。

WHILE 循環(先判斷後執行,最常用)

[循環標籤:] WHILE 循環條件 DO
    循環體邏輯;
    -- 可選:修改循環變量(避免死循環)
END WHILE [循環標籤]; -- 標籤可選,用於跳出循環

-- 批量插入 10 條測試數據
DELIMITER //
CREATE PROCEDURE proc_batch_insert()
MODIFIES SQL DATA
BEGIN
    DECLARE i INT DEFAULT 1; -- 循環計數器
    DECLARE max_num INT DEFAULT 10; -- 最大插入條數
    -- 清空舊數據
    DELETE FROM test_data;
    -- WHILE 循環:插入 1-10 的數據
    WHILE i <= max_num DO
        INSERT INTO test_data (id, name) VALUES (i, CONCAT('test_', i));
        SET i = i + 1; -- 計數器自增(關鍵,避免死循環)
    END WHILE;
END //
DELIMITER ;

-- 調用:CALL proc_batch_insert(); -- 插入 10 條數據

REPEAT 循環(先執行後判斷,至少執行一次)

REPEAT
    循環體邏輯;
    -- 修改循環變量
UNTIL 結束條件 -- 條件為 TRUE 時退出循環(無需分號)
END REPEAT;


-- 查詢用户訂單,直到找到未支付訂單或遍歷完
DELIMITER //
CREATE PROCEDURE proc_find_unpaid_order(user_id INT)
READS SQL DATA
BEGIN
    DECLARE order_id INT;
    DECLARE is_unpaid BOOLEAN DEFAULT FALSE;
    DECLARE i INT DEFAULT 1;
    REPEAT
        -- 查詢第 i 條訂單
        SELECT id INTO order_id  
        FROM `order`  
        WHERE user_id = user_id  
        LIMIT i-1, 1; -- 分頁查詢(第 i 條)

        -- 判斷是否未支付
        IF EXISTS (SELECT 1 FROM `order` WHERE id = order_id AND status = 0) THEN
            SET is_unpaid = TRUE;
        END IF;
        SET i = i + 1;

    UNTIL is_unpaid OR i > 10 -- 找到未支付,或查詢超過10條則退出
    END REPEAT;

END //

DELIMITER ;

LOOP 循環(無限循環,需手動跳出)

循環標籤: LOOP
    循環體邏輯;
    IF 退出條件 THEN
        LEAVE 循環標籤; -- 跳出循環
    END IF;
END LOOP 循環標籤;

-- 隨機生成 5 個 100-200 之間的整數
DELIMITER //
CREATE PROCEDURE proc_generate_random()
NO SQL
BEGIN
    DECLARE num INT;
    DECLARE count INT DEFAULT 0;
    DECLARE result VARCHAR(100) DEFAULT '';

    -- 定義循環標籤
    loop_label: LOOP
        -- 生成 100-200 的隨機數
        SET num = FLOOR(100 + RAND() * 101);

        -- 拼接結果
        SET result = CONCAT(result, num, ',');

        -- 計數器自增
        SET count = count + 1;

        -- 生成 5 個後退出
        IF count >= 5 THEN
            LEAVE loop_label; -- 跳出循環
        END IF;

    END LOOP loop_label;

END //
DELIMITER ;

循環輔助:LEAVE 和 ITERATE

LEAVE:跳出指定標籤的循環(類似編程語言的 break);ITERATE:跳過當前循環的剩餘邏輯,直接進入下一次循環(類似 continue)。

-- 用 ITERATE 跳過偶數,只保留奇數
DELIMITER //
CREATE PROCEDURE proc_filter_odd()
NO SQL
BEGIN
    DECLARE i INT DEFAULT 1;
    DECLARE result VARCHAR(100) DEFAULT '';

    loop_label: LOOP
        IF i > 10 THEN
            LEAVE loop_label; -- 跳出循環
        END IF;

        -- 偶數則跳過當前循環
        IF i % 2 = 0 THEN
            SET i = i + 1;
            ITERATE loop_label; -- 跳過後續邏輯,進入下一次循環
        END IF;

        -- 奇數則拼接
        SET result = CONCAT(result, i, ',');
        SET i = i + 1;

    END LOOP loop_label;

END //
DELIMITER ;