递归 SQL:使用递归子查询分解的聚合函数
Posted
技术标签:
【中文标题】递归 SQL:使用递归子查询分解的聚合函数【英文标题】:Recursive SQL: aggregate function using Recursive Subquery Factoring 【发布时间】:2014-09-04 14:14:41 【问题描述】:表 T 代表一棵树。每条记录都是一个节点,每个节点只有一个父节点。
此查询计算每个节点的每个分支的 SUM()。
WITH t AS
(SELECT 1 id, NULL parent_id, NULL value FROM dual UNION ALL
SELECT 10 id, 1 parent_id, 1000 value FROM dual UNION ALL
SELECT 20 id, 1 parent_id, 2000 value FROM dual UNION ALL
SELECT 30 id, 10 parent_id, 3000 value FROM dual UNION ALL
SELECT 40 id, 10 parent_id, 4000 value FROM dual UNION ALL
SELECT 50 id, 20 parent_id, 5000 value FROM dual UNION ALL
SELECT 60 id, 1 parent_id, 6000 value FROM dual UNION ALL
SELECT 70 id, 60 parent_id, 7000 value FROM dual UNION ALL
SELECT 80 id, 70 parent_id, 8000 value FROM dual
) SELECT CAST(LPAD(' ', (LEVEL-1)*4) || ID AS VARCHAR2(20)) id
,VALUE self_value
,(SELECT SUM (value)
FROM t t2
CONNECT BY
PRIOR t2.ID = t2.parent_id
START WITH t2.id = t.id) branch_value
FROM t
CONNECT BY PRIOR t.id = t.parent_id
START WITH t.parent_id IS NULL
ORDER SIBLINGS BY t.id;
ID SELF_VALUE BRANCH_VALUE
-------- ---------- ------------
1 36000
10 1000 8000
30 3000 3000
40 4000 4000
20 2000 7000
50 5000 5000
60 6000 21000
70 7000 15000
80 8000 8000
选择了 9 行。
我一直在尝试使用替代的子查询分解语法来实现此查询的相同结果。任何帮助将不胜感激!
【问题讨论】:
正如您所说的“每个节点只有一个父节点”,您的格式是否错误? 30 和 40 有 10 作为父级。 感谢您的评论 Jorge.. 我的意思是每个节点/记录只有一个父节点。在树形结构中,每个父母可能有几个孩子。这就是 #30 和 #40 发生的情况,它们都以 #10 作为其父级。如果这还不够清楚,请告诉我。 拥有...谢谢。英语不是我的主要语言,我总是把父母和孩子混在一起。对不起。我有一个问题,你想在没有 WITH 命令的情况下这样做吗? No Jorge,.. 看看来自 Oracle 的 Recursive Subquery Factoring doc 并在文章末尾查看一些示例以了解它是什么。我想在没有 CONNECT BY 子句的情况下这样做 这是我到目前为止所得到的;遗憾的是还没有/我对 Oracle 还不够熟悉...sqlfiddle.com/#!4/d41d8/32449 【参考方案1】:在递归查询支持GROUP BY
之前,我认为只有一个查询是不可能的。所以我在WITH
-clause 中添加了第二个子查询。也许这足以解决您的问题(不错的脑筋急转弯)。
这将计算数据:
WITH t AS
(SELECT 1 id, NULL parent_id, NULL value FROM dual UNION ALL
SELECT 10 id, 1 parent_id, 1000 value FROM dual UNION ALL
SELECT 20 id, 1 parent_id, 2000 value FROM dual UNION ALL
SELECT 30 id, 10 parent_id, 3000 value FROM dual UNION ALL
SELECT 40 id, 10 parent_id, 4000 value FROM dual UNION ALL
SELECT 50 id, 20 parent_id, 5000 value FROM dual UNION ALL
SELECT 60 id, 1 parent_id, 6000 value FROM dual UNION ALL
SELECT 70 id, 60 parent_id, 7000 value FROM dual UNION ALL
SELECT 80 id, 70 parent_id, 8000 value FROM dual),
hierarchy (id,ancestor,value) AS (
SELECT t.id,t.id,t.value
FROM t
UNION ALL
SELECT t.id,h.ancestor,t.value
FROM t
INNER JOIN hierarchy h
ON t.parent_id = h.id)
SELECT h.ancestor, t.parent_id, t.value, SUM(h.value)
FROM hierarchy h
INNER JOIN t
ON t.id = h.ancestor
GROUP BY h.ancestor,t.value,t.parent_id;
要获得与问题中描述的相同的顺序和格式,请添加路径和深度的计算:
WITH t AS
(SELECT 1 id, NULL parent_id, NULL value FROM dual UNION ALL
SELECT 10 id, 1 parent_id, 1000 value FROM dual UNION ALL
SELECT 20 id, 1 parent_id, 2000 value FROM dual UNION ALL
SELECT 30 id, 10 parent_id, 3000 value FROM dual UNION ALL
SELECT 40 id, 10 parent_id, 4000 value FROM dual UNION ALL
SELECT 50 id, 20 parent_id, 5000 value FROM dual UNION ALL
SELECT 60 id, 1 parent_id, 6000 value FROM dual UNION ALL
SELECT 70 id, 60 parent_id, 7000 value FROM dual UNION ALL
SELECT 80 id, 70 parent_id, 8000 value FROM dual),
hierarchy (id,ancestor,value,depth,path) AS (
SELECT t.id,t.id,t.value,0,''||t.id
FROM t
UNION ALL
SELECT t.id,h.ancestor,t.value,h.depth+1,h.path||'.'||t.id
FROM t
INNER JOIN hierarchy h
ON t.parent_id = h.id)
SELECT LPAD(h.ancestor,p.depth*4+1,' ') AS id, t.value AS self_value, SUM(h.value) as branch_value
FROM hierarchy h
INNER JOIN t
ON t.id = h.ancestor
INNER JOIN (SELECT id,depth,path
FROM hierarchy
WHERE ancestor IN (SELECT id FROM t WHERE parent_id IS NULL)
ORDER BY path) p
ON p.id = t.id
GROUP BY h.ancestor,t.value,t.parent_id,p.path,p.depth
ORDER BY p.path;
【讨论】:
嗨,用户,干得好!.. 总数还可以,但我正在尝试重写您的版本,以实现与原始版本相同的输出(深度优先),这是另一个目标问题。 我添加了第二个查询,它额外计算路径和深度,以与问题中描述的完全相同的方式排序和格式化输出。以上是关于递归 SQL:使用递归子查询分解的聚合函数的主要内容,如果未能解决你的问题,请参考以下文章