在业务系统里,历史日志表往往只用来排查问题,时间久了就会堆积大量过期记录。如果每次都靠人工执行删除语句,不仅容易忘,还可能误删数据。用SQL存储过程把清理逻辑封装好,再交给数据库的定时任务去调度,是比较稳妥的做法。
一、明确清理规则
动手写之前,先确认两件事:日志表里的哪一列是时间字段,比如 create_time;以及要保留多少天的数据,例如保留最近30天。规则清楚后,存储过程只需要做一件简单的事:删除时间早于截止日期的记录。
二、MySQL存储过程示例
下面以MySQL 8.0为例,创建一个名为 clean_expired_logs 的存储过程。它通过传入保留天数参数,计算截止时间并删除旧数据,同时使用事务和异常处理保证安全。
DELIMITER //
CREATE PROCEDURE clean_expired_logs(IN retain_days INT)
BEGIN
DECLARE cutoff_time DATETIME;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- 出错时回滚,避免部分删除
ROLLBACK;
RESIGNAL;
END;
-- 计算截止时间
SET cutoff_time = DATE_SUB(NOW(), INTERVAL retain_days DAY);
START TRANSACTION;
-- 删除早于截止时间的日志
DELETE FROM history_log
WHERE create_time < cutoff_time;
COMMIT;
END //
DELIMITER ;
调用方式
手动调用时,执行下面语句即可清理保留30天以外的数据:
CALL clean_expired_logs(30);
三、配置定期执行任务
MySQL可以使用事件调度器来定时跑这个存储过程。先确认事件开关已打开,再创建一个每天凌晨执行的事件。
-- 开启事件调度
SET GLOBAL event_scheduler = ON;
CREATE EVENT IF NOT EXISTS ev_clean_logs
ON SCHEDULE EVERY 1 DAY
STARTS TIMESTAMP(CURRENT_DATE, '02:00:00')
DO
CALL clean_expired_logs(30);
四、SQL Server中的写法差异
如果是SQL Server,可以用类似逻辑,但语法不同。它一般配合 SQL Server Agent 作业来定期执行,存储过程里用 GETDATE() 和系统存储过程处理错误。
CREATE PROCEDURE clean_expired_logs
@retain_days INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @cutoff_time DATETIME;
SET @cutoff_time = DATEADD(DAY, -@retain_days, GETDATE());
BEGIN TRY
BEGIN TRAN;
DELETE FROM history_log
WHERE create_time < @cutoff_time;
COMMIT TRAN;
END TRY
BEGIN CATCH
ROLLBACK TRAN;
THROW;
END CATCH
END;
五、注意事项
- 大表删除建议分批进行,比如每次删一万行,防止锁表太久。
- 删除前最好先 SELECT 确认受影响行数,或在测试库验证。
- 如果日志表有从库,注意删除操作在复制链路中的延迟影响。
把清理逻辑写成存储过程并由定时任务调用,可以让数据库维护变得更省心。你只要选对时间字段和保留周期,剩下的交给数据库自己处理就行。