MySQL中行增改提交时自动更新DENSE_RANK的需求与问题
解决方案
问题回顾
原数据表(表1)
| ID | Action Performed Indicator | Event Time |
|---|---|---|
| 1001 | text 1 | 2023-03-31 10:00:00 |
| 1001 | text 2 | 2023-03-31 10:00:00 |
| 1001 | text 1 | 2023-03-28 10:50:00 |
核心需求
在MySQL环境下,当表中插入、更新行时,自动维护ranker字段,实现与DENSE_RANK() OVER (PARTITION BY ID, \Action Performed Indicator` ORDER BY `Event Time` DESC)完全一致的排名逻辑。**禁止使用$、@、:`符号,不能用窗口函数,存储函数内无法执行UPDATE**。
期望结果
| ID | Action Performed Indicator | Event Time | ranker |
|---|---|---|---|
| 1001 | text 1 | 2023-03-31 10:00:00 | 1 |
| 1001 | text 2 | 2023-03-31 10:00:00 | 1 |
| 1001 | text 1 | 2023-03-28 10:50:00 | 2 |
实现步骤
1. 新增ranker字段
首先确保目标表包含用于存储排名的字段:
ALTER TABLE your_table ADD COLUMN ranker INT;
替换
your_table为你的实际表名
2. 创建触发器维护排名
通过AFTER INSERT和AFTER UPDATE触发器,在数据变更后自动更新对应分组的排名。核心逻辑是通过自连接统计分组内大于等于当前行时间的不同时间值数量,以此实现DENSE_RANK的“同值同排名”特性。
插入后更新触发器
DELIMITER // CREATE TRIGGER update_ranker_after_insert AFTER INSERT ON your_table FOR EACH ROW BEGIN UPDATE your_table t1 JOIN ( SELECT ID, `Action Performed Indicator`, `Event Time`, COUNT(DISTINCT t2.`Event Time`) AS rank_val FROM your_table t1 JOIN your_table t2 ON t1.ID = t2.ID AND t1.`Action Performed Indicator` = t2.`Action Performed Indicator` AND t2.`Event Time` >= t1.`Event Time` GROUP BY t1.ID, t1.`Action Performed Indicator`, t1.`Event Time` ) t_rank ON t1.ID = t_rank.ID AND t1.`Action Performed Indicator` = t_rank.`Action Performed Indicator` AND t1.`Event Time` = t_rank.`Event Time` SET t1.ranker = t_rank.rank_val WHERE t1.ID = NEW.ID AND t1.`Action Performed Indicator` = NEW.`Action Performed Indicator`; END // DELIMITER ;
更新后更新触发器
当更新分组字段(ID、Action Performed Indicator)或时间字段时,需要同时维护旧分组和新分组的排名:
DELIMITER // CREATE TRIGGER update_ranker_after_update AFTER UPDATE ON your_table FOR EACH ROW BEGIN UPDATE your_table t1 JOIN ( SELECT ID, `Action Performed Indicator`, `Event Time`, COUNT(DISTINCT t2.`Event Time`) AS rank_val FROM your_table t1 JOIN your_table t2 ON t1.ID = t2.ID AND t1.`Action Performed Indicator` = t2.`Action Performed Indicator` AND t2.`Event Time` >= t1.`Event Time` GROUP BY t1.ID, t1.`Action Performed Indicator`, t1.`Event Time` ) t_rank ON t1.ID = t_rank.ID AND t1.`Action Performed Indicator` = t_rank.`Action Performed Indicator` AND t1.`Event Time` = t_rank.`Event Time` SET t1.ranker = t_rank.rank_val WHERE (t1.ID = OLD.ID AND t1.`Action Performed Indicator` = OLD.`Action Performed Indicator`) OR (t1.ID = NEW.ID AND t1.`Action Performed Indicator` = NEW.`Action Performed Indicator`); END // DELIMITER ;
逻辑说明
- 自连接查询中,
COUNT(DISTINCT t2.Event Time)统计的是当前分组内,时间大于等于当前行的唯一时间值数量,完全匹配DENSE_RANK的排名规则:相同时间的行排名相同,更早的行排名递增。 - 触发器仅更新受影响的分组(插入时的新分组、更新时的旧/新分组),避免全表更新带来的性能损耗。
- 全程未使用任何被禁止的符号或窗口函数,完全符合限制条件。
内容的提问来源于stack exchange,提问作者SRIHARI VENKATESAN
相关产品推荐
相关产品推荐

