递归 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:使用递归子查询分解的聚合函数的主要内容,如果未能解决你的问题,请参考以下文章

递归 SQL 中的聚合函数

SQL语句汇总——聚合函数分组子查询及组合查询

带有子查询的 SQL 多个聚合函数

sql聚合函数的应用

SQL Server“不能对包含聚合或子查询的表达式执行聚合函数”,但 Sybase 可以

调优 SQL 查询:在同一张表上具有聚合函数的子查询