【问题标题】:Is it possible to make a recursive SQL query?是否可以进行递归 SQL 查询?
【发布时间】:2010-09-08 08:40:59
【问题描述】:

我有一张类似这样的表:

CREATE TABLE example (
  id integer primary key,
  name char(200),
  parentid integer,
  value integer);

我可以使用 parentid 字段将数据排列成树状结构。

现在这是我无法解决的问题。给定一个 parentid,是否可以编写一条 SQL 语句来将该 parentid 下的所有 value 字段相加并递归到树的分支?

更新:我正在使用 posgreSQL,因此我无法使用精美的 MS-SQL 功能。无论如何,我希望将其视为一般 SQL 问题。

顺便说一句,在提出问题后的 15 分钟内得到 6 个答案,让我印象深刻!去堆栈溢出!

【问题讨论】:

  • 我正在使用 posgreSQL,所以我无法使用那些花哨的 MS-SQL 功能。无论如何,我希望将其视为通用 SQL 问题。顺便说一句,在提出问题的 15 分钟内有 6 个答案给我留下了深刻的印象!去堆栈溢出!
  • 这是分层数据。我发现 Anthony Mollinaro 在 SQL Cookbook (O'Reilly) 中关于层次数据的讨论非常方便;他涵盖了几乎所有流行的 DBMS,包括 PostrgreSQL。
  • 如果你来自谷歌,请查看@Chris KL 的回复,因为 PostgreSQl 8.4 递归查询在 postgreSQL 上可用。

标签: sql postgresql recursive-query


【解决方案1】:

这是一个使用公用表表达式的示例脚本:

with recursive sumthis(id, val) as (
    select id, value
    from example
    where id = :selectedid
    union all
    select C.id, C.value
    from sumthis P
    inner join example C on P.id = C.parentid
)
select sum(val) from sumthis

上面的脚本创建了一个名为sumthis 的“虚拟”表,其中包含idval 列。它被定义为与union all 合并的两个选择的结果。

首先 select 获取根 (where id = :selectedid)。

第二个select 迭代地跟随之前结果的孩子,直到没有任何东西可以返回。

然后可以像处理普通表格一样处理最终结果。在这种情况下,val 列被求和。

【讨论】:

  • 如果我正确理解了您的代码,它需要一些 (id, value) 元组匹配 selectedid。然后将它们与 sumthis 的新实例连接起来,该实例再次包含那些相同的元组。那么为什么这个查询会终止呢?我的理解失败可能在于:selectedid 的设置方式。或者也许子 sumthis 表的值来自哪里
【解决方案2】:

自 8.4 版起,PostgreSQL 为使用 SQL 标准 WITH 语法的公用表表达式提供了 recursive query support

【讨论】:

    【解决方案3】:

    如果您想要一个适用于任何 ANSI SQL-92 RDBMS 的可移植解决方案,您需要向表中添加一个新列。

    Joe Celko 是在 SQL 中存储层次结构的 Nested Sets 方法的原作者。可以谷歌"nested sets" hierarchy了解更多背景信息。

    或者您可以将 parentid 重命名为 leftid 并添加 rightid

    这是我对嵌套集的尝试总结,因为我不是 Joe Celko,所以它会非常短:SQL 是一种基于集合的语言,而邻接模型(存储父 ID)不是基于集合的表示一个层次结构。因此,没有纯粹的基于集合的方法来查询邻接模式。

    然而,近年来大多数主要平台都引入了扩展来处理这个精确的问题。因此,如果有人回复特定于 Postgres 的解决方案,请务必使用它。

    【讨论】:

    • @Portman 我看过嵌套集。似乎是个好主意,但插入/删除成本似乎非常高。
    • 是的,似乎。但是相信我——一旦你编写了 CRUD 程序,它的性能就会非常好。
    • 嵌套集模型中的每次更新都要求您更新表中 > 50% 的行。这是一个聪明的机制,但在任何现实世界的情况下都是完全禁忌的。我通常是 Celko 的粉丝,但他在这一点上是错误的。
    • Intelligententerprise Link 现已失效
    【解决方案4】:

    在 PostgreSQL 中有几种方法可以满足您的需要。

    类似这样的:

    create or replace function example_subtree (integer)
    returns setof example as
    'declare results record;
             child record;
     begin
      select into results * from example where parent_id = $1;
      if found then
        return next results;
        for child in select id from example
                      where parent_id = $1
          loop
            for temp in select * from example_subtree(child.id)
            loop
              return next temp;
            end loop;
          end loop;
      end if;
      return null;
    end;' language 'plpgsql';
    
    select sum(value) as value_sum
      from example_subtree(1234);
    

    【讨论】:

      【解决方案5】:

      SQL 中进行递归查询的标准方法是递归CTEPostgreSQL 支持它们,因为 8.4

      在早期版本中,您可以编写递归集合返回函数:

      CREATE FUNCTION fn_hierarchy (parent INT)
      RETURNS SETOF example
      AS
      $$
              SELECT  example
              FROM    example
              WHERE   id = $1
              UNION ALL
              SELECT  fn_hierarchy(id)
              FROM    example
              WHERE   parentid = $1
      $$
      LANGUAGE 'sql';
      
      SELECT  *
      FROM    fn_hierarchy(1)
      

      见这篇文章:

      【讨论】:

      • +1 用于递归 CTE,目前所有主要 DBMS 都支持 - MySQL 和 SQLite 除外
      • 也许我做错了,但是如果我在第一个 SELECT 子句中指定了多个字段,例如 SELECT id, name,我会在 @987654330 处收到 each UNION query must have the same number of columns 错误@ 线。我想在最后的SELECT 中我可以重新加入example 表以获取其余字段,但它不再那么优雅了。
      【解决方案6】:

      如果您使用 SQL Server 2005,那么使用公用表表达式有一种非常酷的方法。

      它消除了创建临时表的所有繁琐工作,并且基本上允许您只使用 WITH 和 UNION 来完成所有工作。

      这是一个很好的教程:

      http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html

      【讨论】:

        【解决方案7】:

        使用common table expression

        可能想指出这仅是 SQL Server 2005 或更高版本。 Dale Ragan

        here's an article 在没有公用表表达式的情况下由 SqlTeam 递归。

        【讨论】:

        • 可能想表明这只是 SQL Server 2005 或更高版本。
        • 现在不想查,但我知道oracle 10g有With子句,不知道那里是否也可以。
        • Oracle 11gR2 引入了对递归 WITH 子句的支持;在此之前 WITH 子句不能引用自身。对于以前的版本,Oracle 至少从版本 7 或 8 开始就有自己的分层查询语法(START WITH+CONNECT BY),可能更早。
        【解决方案8】:

        以下代码编译通过,测试OK。

        创建或替换函数子树 (bigint) 返回 setof 示例为 $$ 宣布 结果记录; 入境记录; 记录记录; 开始 select into results * from example where parent = $1; 如果找到然后 用于从父 = $1 和子父循环的示例中选择子项 for recs in select * from subtree(entry.child) 循环 返回下一个记录; 结束循环; 结束循环; 万一; 返回下一个结果; 结尾; $$ 语言 'plpgsql';

        在我的例子中需要条件“child parent”,因为节点指向自己。

        玩得开心:)

        【讨论】:

          【解决方案9】:

          Oracle 有“START WITH”和“CONNECT BY”

          select 
              lpad(' ',2*(level-1)) || to_char(child) s
          
          from 
              test_connect_by 
          
          start with parent is null
          connect by prior child = parent;
          

          http://www.adp-gmbh.ch/ora/sql/connect_by.html

          【讨论】:

            【解决方案10】:

            顺便说一句,尽管这个问题已经得到很好的回答,但应该注意的是,如果我们将其视为:

            通用 SQL 问题

            那么 SQL 实现相当简单,因为 SQL'99 允许通过 WITH RECURSIVE 语句在规范中进行线性递归(尽管我相信没有 RDBMS 完全实现该标准)。所以从理论上讲,我们现在就可以做到。

            【讨论】:

              【解决方案11】:

              没有一个示例对我有用,因此我已将其修复如下:

              宣布 结果记录; 入境记录; 记录记录; 开始 对于 select * from project where pid = $1 循环中的结果 返回下一个结果; for recs in select * from project_subtree(results.id) 循环 返回下一个记录; 结束循环; 结束循环; 返回; 结尾;

              【讨论】:

                【解决方案12】:

                这是 SQL Server 吗?您不能编写一个循环遍历并将结果合并在一起的 TSQL 存储过程吗?

                我也很感兴趣,如果有一个只用 SQL 的方式来做这件事。根据我在地理数据库课上的记忆,应该有。

                【讨论】:

                  【解决方案13】:

                  我认为在 SQL 2008 中使用HierarchyID 会更容易

                  【讨论】:

                  • 同意。数据库绑定菜单会变得更好。
                  【解决方案14】:

                  如果您需要存储任意图形,而不仅仅是层次结构,您可以将 Postgres 推到一边并尝试使用图形数据库,例如 AllegroGraph

                  图形数据库中的所有内容都存储为三元组(源节点、边、目标节点),它为您提供一流的支持来操作图形结构并使用类似 SQL 的语言对其进行查询。

                  它不能与 Hibernate 或 Django ORM 之类的东西很好地集成,但如果您对图形结构很认真(不仅仅是嵌套集模型给您的层次结构),请检查一下。

                  我也相信 Oracle 终于在他们的最新产品中添加了对真实图的支持,但我很惊讶它花了这么长时间,很多问题都可以从这个模型中受益。

                  【讨论】:

                  • 好奇为什么不赞成投票——这是 AllegroGraph 特有的问题?对图形数据库的一般关注?没有明确涉及如何执行递归查询?
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-06-09
                  • 2011-07-06
                  • 1970-01-01
                  • 2010-10-20
                  • 2021-05-21
                  • 2014-06-12
                  • 2023-03-23
                  相关资源
                  最近更新 更多