-- 1. 無參預存程序
CREATE PROCEDURE sp_refresh_stats()
BEGIN
ANALYZE TABLE orders, users, products;
END;
-- 2. 帶參數 (IN/OUT/INOUT)
CREATE PROCEDURE sp_get_order_stats(
IN p_user_id BIGINT,
OUT p_total_orders INT,
OUT p_total_amount DECIMAL(12,2),
INOUT p_currency VARCHAR(3) -- 入參同時可被修改返回
)
BEGIN
SELECT COUNT(*), COALESCE(SUM(amount),0)
INTO p_total_orders, p_total_amount
FROM orders WHERE user_id = p_user_id;
IF p_currency IS NULL THEN
SET p_currency = 'CNY';
END IF;
END;
-- 3. 調用
CALL sp_get_order_stats(1001, @orders, @amount, @curr);
SELECT @orders, @amount, @curr;
| 構造 | 語法範例 | 適用場景 |
|---|---|---|
| IF-THEN-ELSE | IF condition THEN ... ELSEIF ... ELSE ... END IF; | 分支邏輯 |
| CASE | CASE WHEN cond1 THEN ... WHEN cond2 THEN ... ELSE ... END CASE; | 多條件分支 |
| WHILE | WHILE condition DO ... END WHILE; | 已知次數/條件循環 |
| REPEAT-UNTIL | REPEAT ... UNTIL condition END REPEAT; | 至少執行一次 |
| LOOP + LEAVE | label: LOOP ... IF cond THEN LEAVE label; END IF; END LOOP; | 複雜退出邏輯 |
| ITERATE | ITERATE label; | 跳過本次循環繼續下一次 |
outer_block: BEGIN
DECLARE v_cnt INT DEFAULT 0;
inner_block: BEGIN
DECLARE v_cnt INT DEFAULT 100; -- 同名變量,內層遮蔽外層
SELECT v_cnt; -- 100
END inner_block;
SELECT v_cnt; -- 0
END outer_block;
CREATE PROCEDURE sp_process_pending_orders()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_order_id BIGINT;
DECLARE v_amount DECIMAL(12,2);
DECLARE cur CURSOR FOR SELECT id, amount FROM orders WHERE status='pending';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_order_id, v_amount;
IF done THEN LEAVE read_loop; END IF;
-- 業務邏輯:風控檢查、扣減庫存、發送通知...
UPDATE orders SET status='processing' WHERE id = v_order_id;
END LOOP;
CLOSE cur;
END;
CREATE PROCEDURE sp_transfer(IN from_id BIGINT, IN to_id BIGINT, IN amt DECIMAL(12,2))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL; -- 重新拋出錯誤給調用者
END;
START TRANSACTION;
UPDATE accounts SET balance = balance - amt WHERE id = from_id AND balance >= amt;
IF ROW_COUNT() = 0 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
END IF;
UPDATE accounts SET balance = balance + amt WHERE id = to_id;
COMMIT;
END;
-- 列出所有預存程序
SHOW PROCEDURE STATUS [WHERE Db='my_db'];
-- 查看具體定義
SHOW CREATE PROCEDURE sp_get_order_stats;
-- 信息庫查詢 (可編寫報表)
SELECT ROUTINE_NAME, ROUTINE_TYPE, DEFINER, CREATED, LAST_ALTERED
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA = 'my_db' AND ROUTINE_TYPE = 'PROCEDURE';
IF p_id <= 0 THEN SIGNAL ...,避免髒數據進入事務。PREPARE/EXECUTE 雖強大但難調試、易注入、優化器難分析。極少數場景 (分表路由) 才用。EXECUTE ON PROCEDURE ...,無需表級權限,實現數據訪問封裝。INSERT INTO debug_log(msg) VALUES(...) 記錄關鍵變量,或引入 sys.schema_table_statistics 監控調用性能。MySQL 預存程序是 **「數據層微服務」**:封裝業務不變量、保證事務原子性、減少網絡往返、實現權限隔離。掌握 IN/OUT/INOUT、HANDLER、CURSOR、SIGNAL/RESIGNAL 核心機制,配合「薄應用、厚數據」架構演進,能顯著提升系統穩定性與可維護性。