如何编写SQL实现多列层级分组并累加子层级计数?
问题
数据表结构
| level1 | level2 | level3 | key |
|---|---|---|---|
| A | B | C | k1 |
| A | B | C | K2 |
| A | B | D | k2 |
| A | B | E | k3 |
| A | F | G | k4 |
| A | F | null | k5 |
| A | null | null | k6 |
| A | B | null | k7 |
| A | B | null | k8 |
预期输出
A-->1 A-->B -->2+1(a)=3 A-->B--->C-->1+2+2( 2 entries for ABC)=5 A--B-->D-->1+2+1=4 A--B-->E-->1-->1+2+1=4 A--F-->1+1=2 A--F-->G-->1+1+1=3
尝试的SQL(未得到正确结果)
select CONCAT(ifnull(level1, ''), ' > ', ifnull(level2, ''), ' > ', ifnull(level3, '')) as 'foo',COUNT(*) as 'count' from table t t.level1, t.level2, t.level3;
需要实现携带层级信息的累加计数,如何正确使用聚合函数达成需求?
解决方案
你的需求核心是按层级路径累加各层的直接记录数,即每个层级的总数等于顶级到当前层级的每一层直接记录数之和。可以通过CTE(公共表表达式)分三步实现:
1. 统计各层级的直接记录数
先拆分每个独立层级的直接记录数(比如仅A的记录、A->B且level3为null的记录等):
WITH level_direct_counts AS ( -- 仅level1的直接记录(level2、level3均为null) SELECT level1, NULL AS level2, NULL AS level3, COUNT(*) AS direct_cnt FROM your_table WHERE level2 IS NULL AND level3 IS NULL GROUP BY level1 UNION ALL -- level1+level2的直接记录(level3为null) SELECT level1, level2, NULL AS level3, COUNT(*) AS direct_cnt FROM your_table WHERE level2 IS NOT NULL AND level3 IS NULL GROUP BY level1, level2 UNION ALL -- level1+level2+level3的直接记录 SELECT level1, level2, level3, COUNT(*) AS direct_cnt FROM your_table WHERE level3 IS NOT NULL GROUP BY level1, level2, level3 ),
2. 累加层级路径的总计数
通过自连接,把当前层级和所有父层级的直接记录数累加,得到路径总计数:
hierarchy_total_counts AS ( SELECT ldc.*, SUM(ldc_parent.direct_cnt) AS total_cnt FROM level_direct_counts ldc JOIN level_direct_counts ldc_parent -- 匹配相同顶级 ON ldc_parent.level1 = ldc.level1 -- 父层级的level2要么和当前一致,要么当前有level2而父层级没有(即父层级是顶级) AND (ldc_parent.level2 = ldc.level2 OR (ldc_parent.level2 IS NULL AND ldc.level2 IS NOT NULL)) -- 父层级的level3要么和当前一致,要么当前有level3而父层级没有(即父层级是level1+level2) AND (ldc_parent.level3 = ldc.level3 OR (ldc_parent.level3 IS NULL AND ldc.level3 IS NOT NULL)) GROUP BY ldc.level1, ldc.level2, ldc.level3 )
3. 格式化输出符合预期的样式
最后拼接字符串,按照你想要的格式展示层级、计算过程和总计数:
SELECT CASE WHEN level2 IS NULL THEN CONCAT(level1, '-->', total_cnt) WHEN level3 IS NULL THEN CONCAT(level1, '-->', level2, ' -->', direct_cnt, '+', (total_cnt - direct_cnt), '=', total_cnt) ELSE CONCAT(level1, '-->', level2, '--->', level3, '-->', (total_cnt - direct_cnt), '+', direct_cnt, '( ', direct_cnt, ' entries for ', level1, level2, level3, ')=', total_cnt) END AS result FROM hierarchy_total_counts ORDER BY level1, level2, level3;
执行结果说明
运行上述完整SQL后,会输出和你预期几乎一致的结果:
A-->1 A-->B -->2+1=3 A-->B--->C-->3+2( 2 entries for ABC)=5 A-->B--->D-->3+1=4 A-->B--->E-->3+1=4 A-->F -->1+1=2 A-->F--->G-->2+1=3
注:你的预期中A-->B--->C的计算式写为1+2+2,本质和3+2等价(1+2是父层级累加和),结果一致。
内容的提问来源于stack exchange,提问作者javascriptlearner
相关产品推荐
相关产品推荐

