首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 存儲過程完整指南:語法、參數、流程控制、最佳實踐與實戰案例

     編輯:MySQL 資料庫相關知識

存儲過程與存儲函數:核心區別

特性存儲過程存儲函數
創建語法CREATE PROCEDURECREATE FUNCTION
調用方式CALL proc_name()SELECT func_name() (在 SQL 中)
返回值無直接返回,通過 OUT/INOUT 參數必須 返回單一值 (RETURNS)
事務控制可執行 COMMIT/ROLLBACK**不可** 顯式事務控制
調用限制可調用函數、其他過程不可調用過程,可調用其他函數
適用場景複雜業務邏輯、批量處理、DDL、事務管理計算、轉換、單值查詢、SQL 表達式複用

統稱「存儲例程」。MySQL 5.0+ 正式支持。

一、基礎語法速查

1. 存儲過程

CREATE PROCEDURE proc_name ([IN|OUT|INOUT] param_name type, ...)
[特性 ...]
BEGIN
  -- SQL 語句,每句以 ; 結尾
END

2. 存儲函數

CREATE FUNCTION func_name ([param_name type, ...])
RETURNS return_type
[特性 ...]
BEGIN
  -- 必須包含 RETURN 值
  RETURN expression;
END

3. 參數模式

  • IN(默認):輸入參數,調用者 → 過程。過程內修改**不影響**調用者變量
  • OUT:輸出參數,過程 → 調用者。過程內初始為 NULL,調用時**必須用用戶變量 @var 接收**
  • INOUT:雙向。調用者傳入值,過程可修改並返回新值。同樣需 @var

4. 參數傳遞實戰演示

-- IN:值傳遞,外部不變
CREATE PROCEDURE p_in(IN id INT)
BEGIN
  SET id = id + 1;
  SELECT id AS inner_id;
END;
SET @x = 10; CALL p_in(@x); SELECT @x AS outer_x;  -- inner=11, outer=10

-- OUT:單向輸出,外部初始值被忽略
CREATE PROCEDURE p_out(OUT id INT)
BEGIN
  SELECT id AS init_id;  -- NULL
  SET id = 100;
  SELECT id AS final_id; -- 100
END;
SET @y = 999; CALL p_out(@y); SELECT @y AS outer_y; -- outer=100

-- INOUT:雙向,引用傳遞語義
CREATE PROCEDURE p_inout(INOUT id INT)
BEGIN
  SELECT id AS init_id;  -- 10
  SET id = id * 2;
  SELECT id AS final_id; -- 20
END;
SET @z = 10; CALL p_inout(@z); SELECT @z AS outer_z; -- outer=20

二、變量聲明與賦值

-- 局部變量:僅在 BEGIN...END 塊內有效,必須在塊首聲明
DECLARE var_name type [DEFAULT value];
DECLARE a INT DEFAULT 0;
DECLARE b VARCHAR(50);

-- 賦值方式
SET a = 100;
SET b = 'hello';
SELECT COUNT(*) INTO a FROM users;  -- 查詢結果賦值
SELECT id, name INTO a, b FROM users LIMIT 1;  -- 多列賦值

三、流程控制結構

1. 條件判斷

-- IF...ELSEIF...ELSE
IF condition1 THEN
  stmt1;
ELSEIF condition2 THEN
  stmt2;
ELSE
  stmt3;
END IF;

-- CASE...WHEN...THEN...END CASE
CASE
  WHEN condition1 THEN stmt1;
  WHEN condition2 THEN stmt2;
  ELSE stmt3;
END CASE;

-- 簡單 CASE(值比較)
CASE expr
  WHEN val1 THEN stmt1;
  WHEN val2 THEN stmt2;
  ELSE stmt3;
END CASE;

2. 循環結構

-- WHILE:先判斷後執行
WHILE condition DO
  stmt_list;
END WHILE;

-- REPEAT:先執行後判斷(至少一次),類似 do-while
REPEAT
  stmt_list;
UNTIL condition
END REPEAT;

-- LOOP:無條件無限循環,需配合 LEAVE/ITERATE 退出
[label:] LOOP
  stmt_list;
  IF exit_cond THEN LEAVE label; END IF;
  IF skip_cond THEN ITERATE label; END IF;
END LOOP [label];

3. 循環控制語句

  • LEAVE label:跳出指定標籤的循環或 BEGIN...END 塊
  • ITERATE label:跳過本次循環剩餘語句,直接進入下一次迭代

四、實戰案例:質數篩選存儲過程

DELIMITER $$

DROP PROCEDURE IF EXISTS `pro_prime` $$
CREATE PROCEDURE `pro_prime`(IN max_num INT)
BEGIN
  DECLARE i INT DEFAULT 2;
  DECLARE j INT;
  DECLARE is_prime TINYINT;
  
  -- 結果表(臨時表避免殘留)
  CREATE TEMPORARY TABLE IF NOT EXISTS tmp_primes (prime INT PRIMARY KEY);
  TRUNCATE TABLE tmp_primes;
  
  outer_loop: WHILE i <= max_num DO
    SET is_prime = 1;
    SET j = 2;
    inner_loop: WHILE j * j <= i DO
      IF i % j = 0 THEN
        SET is_prime = 0;
        LEAVE inner_loop;
      END IF;
      SET j = j + 1;
    END WHILE inner_loop;
    
    IF is_prime = 1 THEN
      INSERT INTO tmp_primes VALUES (i);
    END IF;
    SET i = i + 1;
  END WHILE outer_loop;
  
  SELECT * FROM tmp_primes;
END $$

DELIMITER ;

-- 調用
CALL pro_prime(100);  -- 返回 100 以內所有質數

五、存儲過程嵌套調用

DELIMITER //

CREATE PROCEDURE p1() BEGIN SELECT 'Step 1'; END//
CREATE PROCEDURE p2() BEGIN SELECT 'Step 2'; END//
CREATE PROCEDURE p3() 
BEGIN
  CALL p1();
  CALL p2();
  SELECT 'All done';
END//

DELIMITER ;

CALL p3();

六、分隔符管理:腳本部署必知

MySQL 默認分隔符 ;。存儲過程體內含多條語句,直接執行腳本會被過早截斷。

-- 腳本文件標準模板
DELIMITER $$

DROP PROCEDURE IF EXISTS `my_proc` $$
CREATE PROCEDURE `my_proc`()
BEGIN
  -- 過程體
  SELECT 1;
  SELECT 2;
END $$

DELIMITER ;  -- 恢復默認分隔符

圖形化工具通常自動處理分隔符,手工執行腳本**必須**手動管理。

七、管理與元數據查看

操作語句
刪除過程DROP PROCEDURE [IF EXISTS] proc_name;
刪除函數DROP FUNCTION [IF EXISTS] func_name;
查看過程狀態SHOW PROCEDURE STATUS [LIKE 'pattern'];\G
查看函數狀態SHOW FUNCTION STATUS [LIKE 'pattern'];\G
查看創建語法SHOW CREATE PROCEDURE db_name.proc_name;\G
查看函數創建語法SHOW CREATE FUNCTION db_name.func_name;\G
查看定義信息SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='db' AND ROUTINE_NAME='proc';\G

八、進階特性子句

CREATE PROCEDURE p()
[特性列表]
BEGIN ... END

特性列表(可組合):
LANGUAGE SQL                    -- 唯一支持的語言
| [NOT] DETERMINISTIC           -- 確定性:相同輸入必產生相同輸出(影響複製、優化器)
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }  -- SQL 訪問級別
| SQL SECURITY { DEFINER | INVOKER }  -- 執行權限上下文:定義者/調用者
| COMMENT 'string'              -- 註釋
  • DETERMINISTIC:標記為確定性可允許複製到從庫、優化器緩存結果。若包含 UUID(), RAND(), NOW() 等**必須標記 NOT DETERMINISTIC**
  • SQL SECURITY DEFINER(默認):以創建者權限執行,調用者只需 EXECUTE 權限。INVOKER:以調用者權限執行,需調用者擁有底層表權限

九、學生成績統計綜合案例

-- 表結構
CREATE TABLE scores (
  stu_id INT PRIMARY KEY,
  name VARCHAR(20),
  english INT,
  math INT,
  physics INT
);

DELIMITER $$
DROP PROCEDURE IF EXISTS calc_stat $$
CREATE PROCEDURE calc_stat(IN total_students INT)
BEGIN
  DECLARE v_id INT DEFAULT 1;
  DECLARE v_name VARCHAR(20);
  DECLARE v_e, v_m, v_p INT;
  DECLARE v_total INT;
  DECLARE v_avg DECIMAL(5,2);
  
  -- 結果表(臨時表,會話結束自動清理)
  CREATE TEMPORARY TABLE IF NOT EXISTS tmp_stat (
    stu_id INT, name VARCHAR(20), english INT, math INT, physics INT,
    total INT, average DECIMAL(5,2)
  );
  TRUNCATE TABLE tmp_stat;
  
  REPEAT
    SELECT english, math, physics, name 
    INTO v_e, v_m, v_p, v_name
    FROM scores WHERE stu_id = v_id;
    
    SET v_total = v_e + v_m + v_p;
    SET v_avg = v_total / 3;
    
    INSERT INTO tmp_stat VALUES (v_id, v_name, v_e, v_m, v_p, v_total, v_avg);
    SET v_id = v_id + 1;
  UNTIL v_id > total_students
  END REPEAT;
  
  SELECT * FROM tmp_stat ORDER BY average DESC;
  -- 臨時表會話結束自動銷毀,無需手動 DROP
END $$
DELIMITER ;

-- 調用(假設 10 位學生)
CALL calc_stat(10);

十、最佳實踐與避坑指南

  1. 業務邏輯下沉慎重:存儲過程難調試、難版本控制、難重構、綁定廠商。核心業務邏輯建議留在應用層,僅將**高頻、大數據量、強一致性要求的批量操作**下沉
  2. 避免長事務:過程中隱式/顯式事務持有鎖時間長,導致併發急劇下降
  3. 參數校驗前置:過程首行驗證輸入參數合法性,非法則 SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='...' 拋錯
  4. 游標使用謹慎:逐行處理性能極差,優先用集合操作(INSERT ... SELECT, UPDATE ... JOIN)替代
  5. 臨時表優於永久表:中間結果用 CREATE TEMPORARY TABLE,會話隔離、自動清理
  6. 權限最小化:應用賬號僅授予 EXECUTE 權限,不授予底層表 DML 權限
  7. 版本管理:存儲過程腳本納入 Git,部署走 CI/CD,禁用手工在線修改

總結

MySQL 存儲過程/函數提供了強大的服務端程序化能力,但**並非銀彈**。掌握語法、參數模式、流程控制、分隔符管理是基礎;理解執行上下文、權限模型、複製影響是進階;**「業務邏輯應用層,數據密集操作存儲層」** 是架構取捨的黃金法則。合理使用,能解決批量處理、跨表原子操作、減少網絡往返等棘手問題;濫用則會成為維護噩夢。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved