mysql存储过程

55 阅读3分钟

一、功能说明

本文档提供一套 MySQL 自动化脚本,实现优惠券过期后自动更新状态 + 库存回滚功能,核心逻辑:

  1. 开启事件调度器,每 15 秒执行一次任务;
  1. 检测过期未处理(state='0')的优惠券;
  1. 按商品 ID 统计过期优惠券数量,回滚对应商品库存;
  1. 将过期优惠券状态更新为1(已过期)。

二、前置配置:开启事件调度器

1. 查看事件调度器状态

show variables like '%sche%';
  • 若event_scheduler值为OFF,需手动开启。

2. 开启事件调度器(全局生效)

set global event_scheduler = 1;
  • 说明:该配置重启 MySQL 后失效,若需永久生效,需在my.cnf/my.ini中添加event_scheduler=ON,重启服务。

三、核心 SQL 语句(手动执行验证)

1. 优惠券发放初始化(示例)

更新指定优惠券为发放状态,有效期 30 天:

UPDATE coupon 
SET hairTime=NOW(), status='1', expiryTime=(SELECT date_add(now(), interval 30 day)) 
WHERE id="1115";

2. 过期优惠券查询

-- 统计过期未处理的优惠券总数
SELECT COUNT(id) FROM coupon WHERE NOW()>expiryTime AND state='0';
-- 按商品ID分组统计过期未处理的优惠券数量
SELECT count(*) num, productID 
FROM coupon 
WHERE NOW()>expiryTime AND state='0' 
GROUP BY productID;

3. 手动执行过期处理(测试用)

-- 1. 回滚商品库存(过期优惠券数量从库存中扣除回滚)
UPDATE stock s 
INNER JOIN (
    SELECT count(*) num, productID 
    FROM coupon 
    WHERE NOW()>expiryTime AND state='0' 
    GROUP BY productID
) a ON s.productID = a.productID 
SET s.roll_num = s.roll_num - (a.num);
-- 2. 更新优惠券状态为已过期(state='1')
UPDATE coupon 
SET state='1' 
WHERE id IN (
    SELECT a.id FROM (
        SELECT id FROM coupon WHERE NOW()>expiryTime AND state='0'
    ) a
);
  • 说明:子查询SELECT a.id FROM (...) a用于避免 MySQL「不能直接更新查询的表」的语法限制。

四、存储过程:封装过期处理逻辑

1. 创建存储过程

delimiter //
-- 若存储过程已存在则删除
drop procedure if exists task_proce//
-- 创建存储过程
create procedure task_proce()
begin
    -- 步骤1:库存回滚
    UPDATE stock s 
    INNER JOIN (
        SELECT count(*) num, productID 
        FROM coupon 
        WHERE NOW()>expiryTime AND state='0' 
        GROUP BY productID
    ) a ON s.productID = a.productID 
    SET s.roll_num = s.roll_num - (a.num);
    
    -- 步骤2:更新优惠券状态
    UPDATE coupon 
    SET state='1' 
    WHERE id IN (
        SELECT a.id FROM (
            SELECT id FROM coupon WHERE NOW()>expiryTime AND state='0'
        ) a
    );
end//
delimiter ;
  • 关键字说明:
    • delimiter //:临时修改语句结束符(默认;会与存储过程内的;冲突);
    • drop procedure if exists:避免重复创建报错;
    • 存储过程内包含完整的过期处理逻辑,可直接调用执行。

2. 手动调用存储过程(测试)

call task_proce();

五、定时任务:自动执行存储过程

1. 创建定时事件(每 15 秒执行一次)

-- 若事件已存在则删除
drop event if exists task_event;
-- 创建事件
create event task_event
on schedule every 15 second  -- 执行频率:每15秒
on completion preserve disable  -- 创建时默认禁用
do call task_proce();  -- 执行存储过程

2. 事件状态管理

-- 启用事件(开始自动执行)
alter event task_event on completion preserve enable;
-- 禁用事件(暂停自动执行)
alter event task_event on completion preserve disable;
-- 查看事件调度器状态(验证是否开启)
show variables like 'event_scheduler';

六、关键说明与注意事项

  1. 表结构依赖:脚本默认存在以下表及字段,需确保表结构一致:
    • coupon:id(优惠券 ID)、productID(商品 ID)、expiryTime(过期时间)、state(状态:0 = 未过期,1 = 已过期)、hairTime(发放时间);
    • stock:productID(商品 ID)、roll_num(库存数量)。
  1. 数据一致性
    • 若优惠券数量较大,建议将执行频率调整为 1 分钟(every 1 minute),避免高频次锁表;
    • 生产环境建议先在测试库验证,再迁移到正式环境。
  1. 权限要求:执行以下操作需具备对应权限:
    • 开启事件调度器:SUPER权限;
    • 创建存储过程 / 事件:CREATE ROUTINE、ALTER ROUTINE、EVENT权限。
  1. 异常处理
    • 若库存roll_num可能出现负数,可在UPDATE stock语句中添加条件SET s.roll_num = GREATEST(s.roll_num - a.num, 0)(确保库存不小于 0);
    • 建议添加日志表,记录每次任务执行结果(如处理优惠券数量、库存变更情况),便于问题排查。