1311 字
7 分钟
从入门到精通MySQL存储过程:复杂场景实战指南

从入门到精通MySQL存储过程:复杂场景实战指南#

一、存储过程的核心价值#

存储过程是预编译的SQL语句集合,具备四大核心优势:

  1. 网络传输效率提升(减少70%+的查询请求)
  2. 业务逻辑封装(实现服务层与数据层的解耦)
  3. 原子操作保障(通过事务管理实现)
  4. 权限集中控制(精确到存储过程级别的权限分配)

二、快速入门:基础语法结构#

2.1 创建首个存储过程#

DELIMITER $$
CREATE PROCEDURE GetUserLevel(IN userId INT, OUT userLevel VARCHAR(20))
BEGIN
    SELECT 
        CASE 
            WHEN purchase_total > 10000 THEN '钻石会员'
            WHEN purchase_total > 5000 THEN '黄金会员'
            ELSE '普通会员'
        END INTO userLevel
    FROM user_stats 
    WHERE user_id = userId;
END$$
DELIMITER ;

2.2 执行与结果获取#

CALL GetUserLevel(1024, @level);
SELECT @level AS user_level;

三、进阶开发:复杂语法要素#

3.1 多类型参数处理#

CREATE PROCEDURE ProcessOrder(
    IN orderId INT,
    INOUT retryCount INT,
    OUT errorCode INT
)

3.2 游标与异常处理#

DECLARE orderCursor CURSOR FOR
    SELECT product_id, quantity, unit_price 
    FROM order_items 
    WHERE order_id = orderId;
    
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' 
BEGIN
    SET errorCode = 1001;
    ROLLBACK;
END;

DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
    GET DIAGNOSTICS CONDITION 1
    @sqlstate = RETURNED_SQLSTATE, 
    @errno = MYSQL_ERRNO,
    @text = MESSAGE_TEXT;
    INSERT INTO error_logs VALUES (NOW(), @errno, @text);
    ROLLBACK;
END;

3.3 多层条件判断#

IF inventory_qty < required_qty THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = '库存不足';
ELSEIF inventory_qty - required_qty < min_stock THEN
    UPDATE inventory SET status = '需补货' WHERE product_id = currentProduct;
END IF;

四、精通实战:电商订单拆解系统#

4.1 场景需求#

处理包含以下复杂逻辑的订单:

  1. 多商品库存验证
  2. 自动拆单(区分现货与预售)
  3. 会员积分实时计算
  4. 分布式事务补偿机制

4.2 完整存储过程实现#

DELIMITER $$
CREATE PROCEDURE ProcessComplexOrder(
    IN mainOrderId BIGINT,
    IN isSplitAllowed BOOLEAN,
    OUT resultCode INT,
    OUT errorMsg TEXT
)
proc_label: BEGIN
    DECLARE vUserId INT;
    DECLARE vTotalAmount DECIMAL(12,2);
    DECLARE vCurrentProduct INT;
    DECLARE vRequiredQty INT;
    DECLARE vWarehouseId INT;
    DECLARE done INT DEFAULT FALSE;
    
    -- 声明嵌套游标
    DECLARE mainCursor CURSOR FOR
        SELECT product_id, quantity, warehouse_id 
        FROM order_details 
        WHERE order_id = mainOrderId;
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    -- 事务开始
    START TRANSACTION;
    
    -- 验证主订单有效性
    SELECT user_id, total_amount INTO vUserId, vTotalAmount
    FROM orders 
    WHERE order_id = mainOrderId 
    FOR UPDATE;
    
    IF vUserId IS NULL THEN
        SET resultCode = 404;
        SET errorMsg = '主订单不存在';
        ROLLBACK;
        LEAVE proc_label;
    END IF;
    
    -- 开启游标处理
    OPEN mainCursor;
    item_loop: LOOP
        FETCH mainCursor INTO vCurrentProduct, vRequiredQty, vWarehouseId;
        IF done THEN
            LEAVE item_loop;
        END IF;
        
        -- 库存验证子过程
        CALL CheckInventory(
            vCurrentProduct, 
            vRequiredQty, 
            vWarehouseId, 
            @stockStatus, 
            @availableQty
        );
        
        -- 库存不足处理逻辑
        IF @stockStatus = 'INSUFFICIENT' THEN
            IF isSplitAllowed THEN
                -- 创建拆分订单
                INSERT INTO split_orders 
                VALUES (NULL, mainOrderId, vCurrentProduct, vRequiredQty);
                
                UPDATE order_details 
                SET quantity = @availableQty 
                WHERE product_id = vCurrentProduct 
                AND order_id = mainOrderId;
            ELSE
                SET resultCode = 500;
                SET errorMsg = CONCAT('产品 ', vCurrentProduct, ' 库存不足');
                ROLLBACK;
                LEAVE proc_label;
            END IF;
        END IF;
        
        -- 实际库存扣减
        UPDATE product_stock 
        SET current_stock = current_stock - vRequiredQty,
            locked_stock = locked_stock + vRequiredQty
        WHERE product_id = vCurrentProduct 
        AND warehouse_id = vWarehouseId;
    END LOOP;
    CLOSE mainCursor;
    
    -- 积分计算(含阶梯规则)
    SET @points = CASE
        WHEN vTotalAmount > 1000 THEN FLOOR(vTotalAmount * 0.15)
        WHEN vTotalAmount > 500 THEN FLOOR(vTotalAmount * 0.1)
        ELSE FLOOR(vTotalAmount * 0.05)
    END;
    
    -- 更新用户积分(原子操作)
    UPDATE user_points 
    SET total_points = total_points + @points,
        available_points = available_points + @points 
    WHERE user_id = vUserId;
    
    -- 记录积分日志
    INSERT INTO points_log 
    VALUES (NULL, vUserId, mainOrderId, @points, NOW());
    
    -- 提交事务
    COMMIT;
    SET resultCode = 200;
    SET errorMsg = '订单处理成功';
END$$
DELIMITER ;

4.3 配套子过程:库存检查#

CREATE PROCEDURE CheckInventory(
    IN productId INT,
    IN requiredQty INT,
    IN warehouseId INT,
    OUT stockStatus VARCHAR(20),
    OUT availableQty INT
)
BEGIN
    DECLARE currentStock INT;
    DECLARE safetyStock INT DEFAULT 10;
    
    SELECT (current_stock - locked_stock) 
    INTO currentStock
    FROM product_stock 
    WHERE product_id = productId 
    AND warehouse_id = warehouseId;
    
    IF currentStock - requiredQty >= safetyStock THEN
        SET stockStatus = 'SUFFICIENT';
        SET availableQty = requiredQty;
    ELSEIF currentStock > safetyStock THEN
        SET stockStatus = 'PARTIAL';
        SET availableQty = currentStock - safetyStock;
    ELSE
        SET stockStatus = 'INSUFFICIENT';
        SET availableQty = 0;
    END IF;
END;

五、高级优化策略#

5.1 性能调优技巧#

  1. 游标优化:改用JOIN查询替代逐行处理
UPDATE order_details od
JOIN product_stock ps ON od.product_id = ps.product_id
SET od.status = '已分配'
WHERE od.order_id = mainOrderId
AND ps.current_stock - ps.locked_stock >= od.quantity;
  1. 执行计划分析:使用EXPLAIN解析复杂查询
EXPLAIN FORMAT=JSON
SELECT ... FROM orders 
JOIN order_details ...;
  1. 缓存优化:合理使用查询缓存
SELECT SQL_CACHE * FROM product_info 
WHERE product_id = 123;

5.2 安全防护方案#

  1. SQL注入防御
CREATE PROCEDURE SafeQuery(IN filterParam VARCHAR(50))
BEGIN
    SET @query = CONCAT('SELECT * FROM products WHERE name = ', QUOTE(filterParam));
    PREPARE stmt FROM @query;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END;
  1. 权限隔离配置
GRANT EXECUTE ON PROCEDURE ProcessComplexOrder TO 'order_processor'@'%';
REVOKE DELETE ON orders FROM 'order_processor'@'%';

六、调试与监控#

6.1 调试技巧#

-- 开启过程跟踪
SET global log_bin_trust_function_creators = 1;
SET global general_log = 1;

-- 使用SELECT调试变量
SELECT @currentProduct AS debug_product, @availableQty AS debug_stock;

6.2 性能监控#

-- 查看存储过程执行统计
SELECT * FROM sys.routine_stats 
WHERE routine_name = 'ProcessComplexOrder';

-- 分析过程执行耗时
SHOW PROFILE CPU, BLOCK IO FOR QUERY 123;

七、最佳实践总结#

  1. 事务粒度控制:单个事务不超过50条DML语句
  2. 错误处理层级:至少包含三级错误处理(SQL异常、业务异常、警告)
  3. 游标使用原则:万级以下数据量使用,大数据量建议分页处理
  4. 版本控制方案:使用CREATE OR REPLACE进行版本迭代
  5. 性能基准测试:定期进行压力测试,推荐使用sysbench工具

通过本指南的进阶案例,开发者可以处理包含分布式事务、库存预占、订单拆解、积分计算等复杂场景。实际应用中建议结合具体的业务需求,对事务隔离级别、错误处理机制进行针对性优化。

从入门到精通MySQL存储过程:复杂场景实战指南
https://turnip1202.github.io/my-blog-astro/posts/study/mysql-study1/
作者
Turnip1202
发布于
2025-03-06
许可协议
CC BY-NC-SA 4.0