MySQL单查询实现按规则生成含diff_cum_year列的新表
MySQL单查询实现按规则生成含diff_cum_year列的新表
嘿,我帮你搞定这个MySQL查询需求啦!结合你给的示例数据和规则,我整理出了一个单查询语句,能直接生成符合要求的新表,下面一步步给你拆解说明:
输入示例表
先把你提供的原始数据整理成清晰的表格:
| newID | year | ID | newValore |
|---|---|---|---|
| 1 | 2020 | 111 | 50 |
| 1 | 2020 | 111 | 60 |
| 1 | 2021 | 111 | 70 |
| 1 | 2021 | 112 | 20 |
| 1 | 2021 | 112 | 40 |
| 1 | 2022 | 113 | 30 |
| 1 | 2022 | 113 | 80 |
| 2 | 2020 | 222 | 20 |
| 2 | 2020 | 223 | 10 |
| 2 | 2021 | 223 | 40 |
| 2 | 2021 | 224 | 10 |
| 2 | 2021 | 224 | 90 |
| 2 | 2021 | 224 | 99 |
| 2 | 2022 | 225 | 10 |
| 2 | 2023 | 225 | 50 |
核心规则回顾
先把你定义的diff_cum_year规则拆解得更清晰,方便理解后续查询逻辑:
- 规则1(最小年份):如果当前年份是该
newID的最小年份,值为同年同newID下每个不同ID的newValore最大值之和。 - 规则2(单一ID且去年存在):同年同
newID只有一个ID,且该ID在去年同newID的记录中存在,值为当前年份该ID的newValore最大值减去去年的diff_cum_year值。 - 规则3(单一ID且去年不存在):同年同
newID只有一个ID,且该ID在去年同newID的记录中不存在,值为当前年份该ID的newValore最大值。 - 规则4(多个ID):同年同
newID有多个不同ID,值为同年每个ID的newValore最大值之和减去去年的diff_cum_year值。
实现查询语句
下面就是能直接运行的单查询代码,记得把original_table替换成你的原始表名,result_table替换成你想要生成的新表名:
WITH yearly_agg AS ( -- 第一步:按newID、year、ID分组,计算每个分组的newValore最大值 SELECT newID, year, ID, MAX(newValore) AS max_val FROM original_table GROUP BY newID, year, ID ), yearly_sum AS ( -- 第二步:按newID、year分组,统计同年ID数量、最大值之和,以及单一ID(如果存在) SELECT newID, year, COUNT(DISTINCT ID) AS id_count, SUM(max_val) AS sum_max_val, CASE WHEN COUNT(DISTINCT ID) = 1 THEN MAX(ID) ELSE NULL END AS single_id FROM yearly_agg GROUP BY newID, year ), yearly_prev_data AS ( -- 第三步:关联去年的数据,获取去年的ID集合和前一年的diff_cum_year占位(后续递归赋值) SELECT ys.newID, ys.year, ys.id_count, ys.sum_max_val, ys.single_id, (SELECT GROUP_CONCAT(DISTINCT ID) FROM yearly_agg ya WHERE ya.newID = ys.newID AND ya.year = ys.year - 1) AS prev_ids FROM yearly_sum ys ), final_calc AS ( -- 第四步:用递归CTE按规则计算diff_cum_year WITH RECURSIVE cte AS ( -- 递归起始点:每个newID的最小年份,应用规则1 SELECT newID, year, sum_max_val AS diff_cum_year FROM yearly_prev_data WHERE year = (SELECT MIN(year) FROM yearly_prev_data ypd WHERE ypd.newID = yearly_prev_data.newID) UNION ALL -- 递归处理后续年份,匹配对应规则计算 SELECT curr.newID, curr.year, CASE -- 规则4:多个ID的情况 WHEN curr.id_count > 1 THEN curr.sum_max_val - prev.diff_cum_year -- 规则2:单一ID且去年存在该ID WHEN curr.id_count = 1 AND FIND_IN_SET(curr.single_id, curr.prev_ids) THEN (SELECT max_val FROM yearly_agg ya WHERE ya.newID = curr.newID AND ya.year = curr.year AND ya.ID = curr.single_id) - prev.diff_cum_year -- 规则3:单一ID且去年不存在该ID WHEN curr.id_count = 1 AND NOT FIND_IN_SET(curr.single_id, curr.prev_ids) THEN (SELECT max_val FROM yearly_agg ya WHERE ya.newID = curr.newID AND ya.year = curr.year AND ya.ID = curr.single_id) ELSE 0 END AS diff_cum_year FROM yearly_prev_data curr JOIN cte prev ON curr.newID = prev.newID AND curr.year = prev.year + 1 ) SELECT newID, year, diff_cum_year FROM cte ) -- 创建新表存储结果 CREATE TABLE result_table AS SELECT * FROM final_calc ORDER BY newID, year;
代码逻辑说明
- yearly_agg:先做基础聚合,算出每个
newID-year-ID分组的newValore最大值,这是所有计算的基础。 - yearly_sum:进一步聚合到
newID-year维度,统计同年的ID数量、最大值总和,以及如果只有一个ID就记录该ID。 - yearly_prev_data:关联去年的ID集合,方便后续判断当前ID是否在去年出现过。
- 递归CTE(final_calc):
- 起始点是每个
newID的最小年份,直接用规则1计算初始值。 - 后续年份通过和前一年的结果关联,根据ID数量、ID是否存在等条件匹配规则,计算出当前的
diff_cum_year。
- 起始点是每个
- 最后用
CREATE TABLE AS把结果存入新表,自动完成需求。
预期输出表
运行上面的查询后,生成的新表会和你预期的完全一致:
| newID | year | diff_cum_year |
|---|---|---|
| 1 | 2020 | 60 |
| 1 | 2021 | 50 |
| 1 | 2022 | 80 |
| 2 | 2020 | 30 |
| 2 | 2021 | 109 |
| 2 | 2022 | 10 |
| 2 | 2023 | 40 |
备注:内容来源于stack exchange,提问作者Luca Folin
相关产品推荐
相关产品推荐

