MySQL 預存程序支援輸入(IN)、輸出(OUT)、輸入輸出(INOUT)三種參數模式,並提供 DECLARE HANDLER 機制處理執行時錯誤。本文詳細說明參數語法、Handler 類型、條件值定義與完整實作範例。
CREATE PROCEDURE proc_name (
IN in_param INT, -- 僅輸入
OUT out_param VARCHAR(50), -- 僅輸出
INOUT io_param INT -- 輸入輸出雙向
)
BEGIN
-- 程序體
END;
DECLARE handler_type HANDLER FOR condition_value[, ...] statement
handler_type: CONTINUE | EXIT
condition_value:
SQLSTATE [VALUE] sqlstate_value
| condition_name
| SQLWARNING | NOT FOUND | SQLEXCEPTION
| mysql_error_code
| 類型 | 行為 |
|---|---|
| CONTINUE | 發生錯誤後繼續執行後續陳述式 |
| EXIT | 發生錯誤後離開當前 BEGIN...END 區塊 |
MySQL 錯誤代碼 > SQLSTATE 代碼 > 命名條件
-- 宣告命名條件
DECLARE duplicate_key CONDITION FOR 1062;
DECLARE foreign_key_violated CONDITION FOR 1216;
-- 使用命名條件
DECLARE CONTINUE HANDLER FOR duplicate_key
BEGIN
SET out_status = 'Duplicate Entry';
END;
DECLARE CONTINUE HANDLER FOR foreign_key_violated
BEGIN
SET out_status = 'Foreign key violated';
END;
CREATE PROCEDURE sp_add_location (
IN in_location VARCHAR(30),
IN in_address1 VARCHAR(30),
IN in_address2 VARCHAR(30),
IN zipcode VARCHAR(10),
OUT out_status VARCHAR(30)
)
BEGIN
DECLARE CONTINUE HANDLER FOR 1062 -- Duplicate entry
SET out_status = 'Duplicate Entry';
SET out_status = 'OK';
INSERT INTO locations (location, address1, address2, zipcode)
VALUES (in_location, in_address1, in_address2, zipcode);
END;
CREATE PROCEDURE sp_not_found()
READS SQL DATA
BEGIN
DECLARE l_last_row INT DEFAULT 0;
DECLARE l_dept_id INT;
DECLARE c_dept CURSOR FOR SELECT department_id FROM departments;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_last_row = 1;
OPEN c_dept;
dept_loop: LOOP
FETCH c_dept INTO l_dept_id;
IF l_last_row = 1 THEN
LEAVE dept_loop;
END IF;
END LOOP dept_loop;
CLOSE c_dept;
END;
CREATE PROCEDURE sp_add_department (
IN p_department_name VARCHAR(30),
IN p_manager_surname VARCHAR(30),
IN p_manager_firstname VARCHAR(30),
IN p_location VARCHAR(30),
OUT p_sqlcode INT,
OUT p_status_message VARCHAR(100)
)
BEGIN
/* 宣告命名條件 */
DECLARE duplicate_key CONDITION FOR 1062;
DECLARE foreign_key_violated CONDITION FOR 1216;
/* 宣告變數與游標 */
DECLARE l_manager_id INT;
DECLARE csr_mgr_id CURSOR FOR
SELECT employee_id FROM employees
WHERE surname = UPPER(p_manager_surname)
AND firstname = UPPER(p_manager_firstname);
/* 宣告異常處理器 */
DECLARE CONTINUE HANDLER FOR duplicate_key
BEGIN
SET p_sqlcode = 1062;
SET p_status_message = 'Duplicate key error';
END;
DECLARE CONTINUE HANDLER FOR foreign_key_violated
BEGIN
SET p_sqlcode = 1216;
SET p_status_message = 'Foreign key violated';
END;
DECLARE CONTINUE HANDLER FOR NOT FOUND
BEGIN
SET p_sqlcode = 1329;
SET p_status_message = 'No record found';
END;
/* 執行邏輯 */
SET p_sqlcode = 0;
OPEN csr_mgr_id;
FETCH csr_mgr_id INTO l_manager_id;
IF p_sqlcode <> 0 THEN
SET p_status_message = CONCAT(p_status_message, ' when fetching manager id');
ELSE
INSERT INTO departments (department_name, manager_id, location)
VALUES (UPPER(p_department_name), l_manager_id, UPPER(p_location));
IF p_sqlcode <> 0 THEN
SET p_status_message = CONCAT(p_status_message, ' when inserting new department');
END IF;
END IF;
CLOSE csr_mgr_id;
END;
善用參數模式與 HANDLER 機制,可建構健壯、可維護的 MySQL 預存程序,將業務邏輯與錯誤處理封裝於資料庫層。