存储过程
创建、调用和删除存储过程,封装复杂业务逻辑
语法
DELIMITER // CREATE PROCEDURE proc_name ([IN|OUT|INOUT] param type, ...) BEGIN -- SQL statements END // DELIMITER ; CALL proc_name(args);
参数
| 参数 | 说明 | 示例 | 级别 |
|---|---|---|---|
IN |
输入参数 | CREATE PROCEDURE get_user(IN uid BIGINT) |
常用 |
OUT |
输出参数 | CREATE PROCEDURE count_orders(IN uid BIGINT, OUT total INT) |
常用 |
INOUT |
输入输出参数 | CREATE PROCEDURE increment(INOUT val INT) |
进阶 |
DECLARE |
声明局部变量 | DECLARE v_count INT DEFAULT 0; |
常用 |
IF...THEN |
条件判断 | IF v_count > 0 THEN ... END IF; |
常用 |
CURSOR |
游标遍历结果集 | DECLARE cur CURSOR FOR SELECT id FROM users; |
进阶 |
示例
创建订单处理存储过程
DELIMITER //
CREATE PROCEDURE sp_create_order(
IN p_user_id BIGINT,
IN p_product_id BIGINT,
IN p_quantity INT,
OUT p_order_no VARCHAR(32)
)
BEGIN
DECLARE v_price DECIMAL(10,2);
DECLARE v_stock INT;
-- 查询商品价格和库存
SELECT price, stock INTO v_price, v_stock
FROM products WHERE id = p_product_id FOR UPDATE;
-- 检查库存
IF v_stock < p_quantity THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '库存不足';
END IF;
-- 生成订单号
SET p_order_no = CONCAT('ORD', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s'), LPAD(FLOOR(RAND()*10000), 4, '0'));
-- 扣减库存
UPDATE products SET stock = stock - p_quantity WHERE id = p_product_id;
-- 创建订单
INSERT INTO orders (order_no, user_id, total_amount, status)
VALUES (p_order_no, p_user_id, v_price * p_quantity, 0);
END //
DELIMITER ;封装下单逻辑:检查库存、扣库存、创建订单,保证原子性
调用存储过程
-- 调用并获取输出参数 CALL sp_create_order(1001, 2001, 2, @order_no); SELECT @order_no;
用 @变量 接收 OUT 参数
查看和删除存储过程
-- 查看所有存储过程 SHOW PROCEDURE STATUS WHERE Db = 'ecommerce'; -- 查看存储过程定义 SHOW CREATE PROCEDURE sp_create_order; -- 删除存储过程 DROP PROCEDURE IF EXISTS sp_create_order;
修改存储过程需要先 DROP 再重新 CREATE
带循环的存储过程
DELIMITER //
CREATE PROCEDURE sp_batch_expire_orders()
BEGIN
DECLARE v_affected INT DEFAULT 1;
-- 分批处理,每次 1000 条
WHILE v_affected > 0 DO
UPDATE orders
SET status = -1, updated_at = NOW()
WHERE status = 0
AND created_at < DATE_SUB(NOW(), INTERVAL 30 MINUTE)
LIMIT 1000;
SET v_affected = ROW_COUNT();
END WHILE;
END //
DELIMITER ;分批处理超时订单,避免一次锁太多行
常见错误
ERROR 1305 (42000): PROCEDURE xxx does not exist
存储过程名或数据库名错误,用 SHOW PROCEDURE STATUS 确认
ERROR 1064: You have an error in your SQL syntax near ''
忘记用 DELIMITER 修改分隔符,存储过程中的分号被提前截断
技巧
- 存储过程适合封装复杂事务逻辑,但不要过度使用,会增加数据库负担
- 调试存储过程可以用 SELECT 输出中间变量值
- 现代架构更倾向在应用层处理业务逻辑,存储过程适合数据密集型操作