存儲過程是預編譯的 SQL 程式碼集合,儲存在資料庫中,支援參數、變量、流程控制與錯誤處理,能提升效能、減少網路傳輸、封裝業務邏輯並強化安全性。本文涵蓋 MySQL 5.0+ 存儲過程的創建、參數模式、變量作用域、條件/迴圈控制、標籤與 ITERATE、常用字串/數學/日期函數,並附完整範例。
SHOW PROCEDURE STATUS;
SELECT name FROM mysql.proc WHERE db = 'your_db' AND type = 'PROCEDURE';
SELECT routine_name FROM information_schema.routines WHERE routine_schema = 'your_db';
DELIMITER //
CREATE PROCEDURE proc1(OUT s INT)
BEGIN
SELECT COUNT(*) INTO s FROM user;
END //
DELIMITER ;
CALL proc1(@cnt);
SELECT @cnt;
DROP PROCEDURE proc1;
關鍵點: DELIMITER // 暫時改分隔符,避免過程內分號被誤判;結束後 DELIMITER ; 還原。使用圖形工具(如 MySQL Workbench)可免改分隔符。
| 模式 | 說明 |
|---|---|
| IN | 輸入參數,調用時必須指定,過程內修改不回傳(預設) |
| OUT | 輸出參數,過程內可修改並回傳 |
| INOUT | 輸入輸出,調用時指定,過程內可修改並回傳 |
DELIMITER //
CREATE PROCEDURE demo_in(IN p_in INT)
BEGIN
SELECT p_in;
SET p_in = 2;
SELECT p_in;
END //
DELIMITER ;
SET @p_in = 1;
CALL demo_in(@p_in); -- 過程內變 2,但 @p_in 仍為 1
DELIMITER //
CREATE PROCEDURE demo_out(OUT p_out INT)
BEGIN
SELECT p_out; -- NULL
SET p_out = 2;
SELECT p_out; -- 2
END //
DELIMITER ;
SET @p_out = 1;
CALL demo_out(@p_out); -- @p_out 變為 2
DELIMITER //
CREATE PROCEDURE demo_inout(INOUT p_inout INT)
BEGIN
SELECT p_inout; -- 1
SET p_inout = 2;
SELECT p_inout; -- 2
END //
DELIMITER ;
SET @p_inout = 1;
CALL demo_inout(@p_inout); -- @p_inout 變為 2
DECLARE l_int INT UNSIGNED DEFAULT 4000000;
DECLARE l_numeric NUMERIC(8,2) DEFAULT 9.95;
DECLARE l_date DATE DEFAULT '1999-12-31';
DECLARE l_varchar VARCHAR(255) DEFAULT 'test';
SET var = expr, var2 = expr2 ...;
-- 客戶端使用
SELECT 'Hello' INTO @x;
SET @y = 'World';
SET @z = 1+2+3;
-- 存儲過程中使用
CREATE PROCEDURE GreetWorld()
SELECT CONCAT(@greeting, ' World');
SET @greeting = 'Hello';
CALL GreetWorld(); -- Hello World
-- 過程間傳遞
CREATE PROCEDURE p1() SET @last = 'p1';
CREATE PROCEDURE p2() SELECT CONCAT('Last: ', @last);
CALL p1(); CALL p2(); -- Last: p1
注意:濫用用戶變量會降低可讀性,優先用局部變量與參數。
-- 單行註釋/* ... */ 多行註釋(C 風格)內層 `BEGIN ... END` 區塊可宣告同名變量遮蔽外層,離開區塊即銷毀,需透過 OUT 參數或用戶變量保留值。
IF-THEN-ELSE
IF var = 0 THEN
INSERT INTO t VALUES(17);
END IF;
IF param = 0 THEN
UPDATE t SET s1 = s1 + 1;
ELSE
UPDATE t SET s1 = s1 + 2;
END IF;
CASE
CASE var
WHEN 0 THEN INSERT INTO t VALUES(17);
WHEN 1 THEN INSERT INTO t VALUES(18);
ELSE INSERT INTO t VALUES(19);
END CASE;
WHILE(先判斷再執行)
WHILE var < 6 DO
INSERT INTO t VALUES(var);
SET var = var + 1;
END WHILE;
REPEAT(先執行再判斷)
REPEAT
INSERT INTO t VALUES(v);
SET v = v + 1;
UNTIL v >= 5
END REPEAT;
LOOP + LEAVE(無預設條件,靠 LEAVE 跳出)
LOOP_LABEL: LOOP
INSERT INTO t VALUES(v);
SET v = v + 1;
IF v >= 5 THEN LEAVE LOOP_LABEL; END IF;
END LOOP;
標籤可用於 `BEGIN`、`WHILE`、`REPEAT`、`LOOP` 前,配合 `LEAVE` 跳出、`ITERATE` 重新開始該標籤區塊。
LOOP_LABEL: LOOP
IF v = 3 THEN
SET v = v + 1;
ITERATE LOOP_LABEL; -- 跳過下方 INSERT,直接下一輪
END IF;
INSERT INTO t VALUES(v);
SET v = v + 1;
IF v >= 5 THEN LEAVE LOOP_LABEL; END IF;
END LOOP;
| 函數 | 說明 |
|---|---|
| CONCAT(str1, str2, ...) | 連接字串 |
| SUBSTRING(str, pos[, len]) | 擷取子字串(pos 從 1 開始) |
| TRIM([BOTH|LEADING|TRAILING] [pad] FROM str) | 去除指定位置字元 |
| REPLACE(str, from, to) | 替換字串 |
| UPPER/LOWER | 大小寫轉換 |
| LENGTH/CHAR_LENGTH | 位元組長度/字元長度 |
| LOCATE/INSTR | 尋找子字串位置 |
| LPAD/RPAD | 左/右填充至指定長度 |
| LEFT/RIGHT | 取左/右 n 字元 |
注意: SUBSTRING('abcd', 0, 2) 回傳空串,SUBSTRING('abcd', 1, 2) 回傳 'ab'。
| 函數 | 說明 |
|---|---|
| ABS, CEILING, FLOOR, ROUND | 絕對值、向上/下取整、四捨五入 |
| MOD(n, m) | 取餘 |
| POWER, SQRT | 冪、開根號 |
| RAND([seed]) | 隨機數 |
| HEX, CONV, BIN | 進制轉換 |
| LEAST, GREATEST | 最小/最大值 |
| FORMAT(num, decimals) | 格式化數字(保留小數位) |
ROUND(1.23) → 1(整數);ROUND(1.567, 2) → 1.57(浮點)。
| 函數 | 說明 |
|---|---|
| NOW(), CURDATE(), CURTIME() | 當前日期時間/日期/時間 |
| DATE_ADD/DATE_SUB(date, INTERVAL expr unit) | 日期加減 |
| DATEDIFF(date1, date2) | 兩日期差(天) |
| TIMESTAMPDIFF(unit, dt1, dt2) | 精確差值(YEAR/MONTH/DAY/HOUR...) |
| DATE_FORMAT(dt, fmt) | 格式化輸出 |
| STR_TO_DATE(str, fmt) | 字串轉日期 |
| YEAR/MONTH/DAY/HOUR/MINUTE/SECOND | 提取單一欄位 |
| LAST_DAY(date) | 該月最後一天 |
| DAYOFWEEK/DAYOFYEAR/WEEK | 星期/年內天數/週數 |
| MAKEDATE(year, dayofyear) | 年+年內天數 → 日期 |
| MAKETIME(hour, min, sec) | 組合時間 |
| SEC_TO_TIME / TIME_TO_SEC | 秒↔時間互轉 |
| CONVERT_TZ(dt, from_tz, to_tz) | 時區轉換 |
SHOW CREATE PROCEDURE db.proc_name;ALTER PROCEDURE proc_name ...(僅修改特性,不改邏輯)DROP PROCEDURE proc_name;PREPARE/EXECUTE),防注入掌握存儲過程,能將資料密集型邏輯下沉到資料庫層,大幅降低應用層複雜度與網路延遲,是高效能資料庫應用的關鍵技能。