儲存過程將 SQL 邏輯下沉至資料庫層,減少網路往返、利用預編譯提升性能、封裝業務規則。本文涵蓋語法、參數模式、流程控制、Python mysql-connector 調用全流程(含 OUT/INOUT 參數獲取技巧)。
DELIMITER $$ CREATE PROCEDURE proc_name ( IN p_in_param INT, -- 輸入 OUT p_out_param INT, -- 輸出 INOUT p_io_param INT -- 輸入輸出 ) BEGIN -- 邏輯體 SELECT COUNT(*) INTO p_out_param FROM orders WHERE status = p_in_param; SET p_io_param = p_io_param + p_in_param; END$$ DELIMITER ;
| 模式 | 調用時 | 體內可讀 | 體內可寫 | 返回給調用者 |
|---|---|---|---|---|
| IN | 必須傳值 | ✅ | ❌ | ❌ |
| OUT | 傳變量名(如 @v) | ❌ | ✅ | ✅ |
| INOUT | 傳已賦值變量 | ✅ | ✅ | ✅ |
-- IN + OUT
CALL GetOfficeByCountry('USA', @total);
SELECT @total;
-- INOUT 累加器
SET @counter = 1;
CALL set_counter(@counter, 1); -- 2
CALL set_counter(@counter, 5); -- 7
SELECT @counter; -- 7
-- IF IF cond THEN stmts; [ELSEIF cond THEN stmts;] [ELSE stmts;] END IF; -- CASE CASE val WHEN v1 THEN stmts; WHEN v2 THEN stmts; ELSE stmts; END CASE; -- WHILE label: WHILE cond DO stmts; END WHILE label; LEAVE label; -- break ITERATE label; -- continue -- REPEAT / LOOP REPEAT stmts; UNTIL cond END REPEAT; label: LOOP stmts; END LOOP label;
from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config # 自定義配置讀取
def call_find_all_sp():
try:
db_config = read_db_config()
conn = MySQLConnection(**db_config)
cursor = conn.cursor()
# 1. 調用無參數過程
cursor.callproc('find_all')
# 2. 獲取多個結果集
for result in cursor.stored_results():
rows = result.fetchall()
print(rows) # 業務處理
# 3. 調用帶 OUT/INOUT 參數的過程
# cursor.callproc('proc_name', ['in_val', 0, 0]) # 佔位符
# OUT/INOUT 值在服務端會話變量 @_proc_name_索引
# 索引對應參數聲明順序(從 0 開始)
cursor.execute("SELECT @_proc_name_1, @_proc_name_2")
out_vals = cursor.fetchone()
print("OUT params:", out_vals)
except Error as e:
print(f"Error: {e}")
finally:
cursor.close()
conn.close()
if __name__ == '__main__':
call_find_all_sp()
callproc() 不直接返回 OUT/INOUT 值,MySQL 將其存放於用戶會話變量:
@_procedure_name_param_index(索引從 0 開始,對應 CREATE PROCEDURE 參數順序)。SELECT @_proc_0, @_proc_1 ... 獲取。# 過程:CREATE PROCEDURE p(IN a INT, OUT b INT, INOUT c INT)
cursor.callproc('p', [10, 0, 100]) # 佔位 0/100
cursor.execute("SELECT @_p_1, @_p_2") # b=OUT(索引1), c=INOUT(索引2)
out_b, inout_c = cursor.fetchone()
COMMIT/ROLLBACK,由調用方控制邊界(除非自治事務)。EXECUTE ON PROCEDURE db.proc TO 'app'@'%'。SELECT ... INTO @debug_var + SELECT @debug_var,或寫入日志表。