MySQL查询:仅筛选Expires字段变更的分组审计记录
问题
我负责维护一个记录生产数据库操作审计追踪的MySQL数据库,该审计日志会为每一次INSERT、UPDATE或DELETE操作生成记录。当前表结构及数据示例如下:
| AudID | AudAction | AudTimestamp | Group | Expires | Meta | User |
|---|---|---|---|---|---|---|
| 1001 | INSERT | 2023-01-01 00:00:00 | GRP_1 | 2023-01-15 00:00:00 | 11111 | User_1 |
| 1002 | INSERT | 2023-01-01 00:01:00 | GRP_2 | 2023-01-15 00:00:00 | 11111 | User_2 |
| 1003 | INSERT | 2023-01-01 00:02:00 | GRP_3 | 2023-01-15 00:00:00 | 11111 | User_1 |
| 1004 | INSERT | 2023-01-01 00:03:00 | GRP_4 | 2023-01-15 00:00:00 | 11111 | User_1 |
| 1005 | UPDATE | 2023-01-01 00:04:00 | GRP_1 | 2023-01-29 00:00:00 | 11111 | User_5 |
| 1006 | UPDATE | 2023-01-01 00:05:00 | GRP_1 | 2023-01-29 00:00:00 | 22222 | User_1 |
| 1007 | INSERT | 2023-01-01 00:06:00 | GRP_5 | 2023-01-15 00:00:00 | 11111 | User_5 |
| 1008 | INSERT | 2023-01-01 00:07:00 | GRP_6 | 2023-01-15 00:00:00 | 11111 | User_1 |
| 1009 | UPDATE | 2023-01-01 00:08:00 | GRP_4 | 2023-01-01 00:00:00 | 22222 | User_3 |
| 1010 | UPDATE | 2023-01-01 00:09:00 | GRP_4 | 2023-01-15 00:00:00 | 22222 | User_1 |
| 1011 | UPDATE | 2023-01-01 00:10:00 | GRP_3 | 2023-01-01 00:00:00 | 33333 | User_4 |
| 1012 | INSERT | 2023-01-01 00:11:00 | GRP_7 | 2023-01-01 00:00:00 | 11111 | User_1 |
| 1013 | INSERT | 2023-01-01 00:12:00 | GRP_8 | 2023-01-01 00:00:00 | 11111 | User_6 |
| 1014 | UPDATE | 2023-01-01 00:13:00 | GRP_1 | 2023-01-31 00:00:00 | 22222 | User_1 |
| 1015 | INSERT | 2023-01-01 00:14:00 | GRP_9 | 2023-01-01 00:00:00 | 11111 | User_7 |
需要生成一份按Group字段分组的报表,仅保留Expires字段发生变更的记录:
- 包含每个
Group的首次INSERT记录 - 包含UPDATE中
Expires字段与同Group上一条记录不同的情况 - 排除仅更新其他字段的行
期望的查询输出如下:
| AudID | AudAction | AudTimestamp | Group | Expires | User |
|---|---|---|---|---|---|
| 1001 | INSERT | 2023-01-01 00:00:00 | GRP_1 | 2023-01-15 00:00:00 | User_1 |
| 1005 | UPDATE | 2023-01-01 00:04:00 | GRP_1 | 2023-01-29 00:00:00 | User_5 |
| 1014 | UPDATE | 2023-01-01 00:13:00 | GRP_1 | 2023-01-31 00:00:00 | User_1 |
| 1002 | INSERT | 2023-01-01 00:01:00 | GRP_2 | 2023-01-15 00:00:00 | User_2 |
| 1003 | INSERT | 2023-01-01 00:02:00 | GRP_3 | 2023-01-15 00:00:00 | User_1 |
| 1004 | INSERT | 2023-01-01 00:03:00 | GRP_4 | 2023-01-15 00:00:00 | User_1 |
| 1010 | UPDATE | 2023-01-01 00:09:00 | GRP_4 | 2023-01-15 00:00:00 | User_1 |
| 1007 | INSERT | 2023-01-01 00:06:00 | GRP_5 | 2023-01-15 00:00:00 | User_5 |
| 1008 | INSERT | 2023-01-01 00:07:00 | GRP_6 | 2023-01-15 00:00:00 | User_1 |
| 1012 | INSERT | 2023-01-01 00:11:00 | GRP_7 | 2023-01-01 00:00:00 | User_1 |
| 1013 | INSERT | 2023-01-01 00:12:00 | GRP_8 | 2023-01-01 00:00:00 | User_6 |
| 1015 | INSERT | 2023-01-01 00:14:00 | GRP_9 | 2023-01-01 00:00:00 | User_7 |
需要编写实现该需求的SQL语句。
解决方案
使用MySQL的窗口函数LAG()获取同Group中上一条记录的Expires值,再筛选符合条件的记录:
SELECT AudID, AudAction, AudTimestamp, `Group`, Expires, User FROM ( SELECT *, -- 按Group分组、AudTimestamp排序,获取上一条记录的Expires值 LAG(Expires) OVER (PARTITION BY `Group` ORDER BY AudTimestamp) AS prev_expires FROM audit_log ) AS sub WHERE -- 保留Group的首次INSERT记录 (prev_expires IS NULL AND AudAction = 'INSERT') -- 保留Expires发生变更的UPDATE记录 OR (AudAction = 'UPDATE' AND Expires != prev_expires) ORDER BY `Group`, AudTimestamp;
逻辑说明
- 子查询处理:通过
LAG()窗口函数,为每条记录匹配同Group中时间更早的上一条记录的Expires值,命名为prev_expires。 - 筛选规则:
- 当
prev_expires为NULL且操作是INSERT时,说明这是该Group的第一条记录,直接保留。 - 当操作是
UPDATE且当前Expires与prev_expires不相等时,说明过期时间发生了变更,保留该记录。
- 当
- 结果排序:最终按
Group和AudTimestamp排序,与期望输出格式一致。
注意:Group是MySQL关键字,查询时需用反引号(`)包裹避免语法错误。
内容的提问来源于stack exchange,提问作者sl_piper
相关产品推荐
相关产品推荐

