萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 存儲過程入門與實戰:從建立到遞歸樹形查詢

存儲過程優勢

  • 預編譯:減少 SQL 解析開銷,降低網路傳輸
  • 業務封裝:複雜邏輯下沉資料庫層,應用層簡化
  • 事務控制:原子性操作更容易管理 (電商下單、扣款)
  • 權限隔離:應用只需 EXECUTE 權限,無需直連表
  • 維護分離:修改邏輯不需改應用代碼、重新部署

基本語法

DELIMITER //

CREATE PROCEDURE proc_name (IN param1 INT, OUT param2 VARCHAR(100))
BEGIN
    -- 宣告區
    DECLARE var1 INT DEFAULT 0;
    DECLARE var2 VARCHAR(50);
    
    -- 游標宣告
    DECLARE cur CURSOR FOR SELECT col1, col2 FROM tbl WHERE condition;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET var1 = 1;
    
    -- 主體邏輯
    OPEN cur;
    FETCH cur INTO var1, var2;
    WHILE var1 = 0 DO
        -- 處理邏輯
        FETCH cur INTO var1, var2;
    END WHILE;
    CLOSE cur;
    
    SET param2 = var2;
END //

DELIMITER ;

實戰案例一:遞歸查詢樹形結構 (分類/組織架構)

-- 建立臨時表存儲結果
DROP TEMPORARY TABLE IF EXISTS tmp_entp_type_info;
CREATE TEMPORARY TABLE tmp_entp_type_info (
    id INT(20), type_name VARCHAR(255), fid INT(11), lvl INT
);

-- 存儲過程
DROP PROCEDURE IF EXISTS proEntpTypeInfo;
DELIMITER //
CREATE PROCEDURE proEntpTypeInfo(IN iid INT, IN lvl INT)
BEGIN
    DECLARE tid INT DEFAULT -1;
    DECLARE ttype_name VARCHAR(255) DEFAULT '';
    DECLARE tptype_id INT DEFAULT -1;
    
    DECLARE cur1 CURSOR FOR 
        SELECT id, type_name, ptype_id 
        FROM entp_type_info 
        WHERE (ptype_id = iid OR id = iid) AND type = 20 AND is_del = 0;
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET tid = NULL;
    
    SET @@max_sp_recursion_depth = 13;  -- 允許遞歸深度
    
    OPEN cur1;
    FETCH cur1 INTO tid, ttype_name, tptype_id;
    
    WHILE tid IS NOT NULL DO
        INSERT INTO tmp_entp_type_info VALUES (tid, ttype_name, tptype_id, lvl);
        CALL proEntpTypeInfo(tid, lvl + 1);  -- 遞歸調用
        FETCH cur1 INTO tid, ttype_name, tptype_id;
    END WHILE;
    
    CLOSE cur1;
END //
DELIMITER ;

-- 調用
CALL proEntpTypeInfo(7, 0);
SELECT * FROM tmp_entp_type_info ORDER BY lvl, id;

實戰案例二:批量更新關聯表 (部門別名同步)

-- 表結構
CREATE TABLE dept (name CHAR(255) PRIMARY KEY, alias CHAR(255)) ENGINE=MyISAM;
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, dept CHAR(255), dept_alias CHAR(255), KEY idx_dept(dept)) ENGINE=MyISAM;

-- 存儲過程:游標逐行處理
DROP PROCEDURE IF EXISTS testProcedure;
DELIMITER //
CREATE PROCEDURE testProcedure()
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE tID INT;
    DECLARE tDept CHAR(255);
    DECLARE tAlias CHAR(255);
    
    DECLARE cur CURSOR FOR SELECT id, dept FROM users;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    
    OPEN cur;
    FETCH cur INTO tID, tDept;
    WHILE done = 0 DO
        SELECT alias INTO tAlias FROM dept WHERE name = tDept;
        UPDATE users SET dept_alias = tAlias WHERE id = tID;
        FETCH cur INTO tID, tDept;
    END WHILE;
    CLOSE cur;
END //
DELIMITER ;

效能對比 (20 萬用戶、1000 部門)

方法耗時備註
單條 UPDATE 子查詢4.25 秒UPDATE users u SET u.dept_alias=(SELECT alias FROM dept WHERE name=u.dept); 最優
存儲過程游標17.67 秒逐行往返、上下文切換開銷大
PHP 循環單條 UPDATE極慢網路往返殺手

關鍵結論

  • **簡單集合操作用單條 SQL (JOIN/子查詢),效能遠超存儲過程**
  • 存儲過程適合:**複雜流程控制、多步事務、動態 SQL、權限隔離**
  • 避免在存儲過程中大量逐行游標處理,改用集合思維
  • MySQL 存儲過程除錯困難,建議配合 SELECT ... INTO @var 打印調試

常用管理命令

SHOW PROCEDURE STATUS WHERE Db = 'mydb';
SHOW CREATE PROCEDURE proc_name;
DROP PROCEDURE IF EXISTS proc_name;
copyright © 萬盛學電腦網 all rights reserved