【问题标题】:Nested join on same table (tree structure)同一张表上的嵌套连接(树结构)
【发布时间】:2009-11-23 10:55:29
【问题描述】:

我的日期以树形结构组织。

以下适用(Oracle SQL 语法):

CREATE TABLE TREE
(
  NAME VARCHAR2(20),
  ID NUMBER(10, 0),
  PARENT NUMBER(10, 0)
)
;

INSERT INTO "TREE" (NAME, ID) VALUES ('a', '1');
INSERT INTO "TREE" (NAME, ID, PARENT) VALUES ('a.1', '2', '1');
INSERT INTO "TREE" (NAME, ID, PARENT) VALUES ('a.2', '3', '1');
INSERT INTO "TREE" (NAME, ID, PARENT) VALUES ('a.2.1', '4', '3');
INSERT INTO "TREE" (NAME, ID, PARENT) VALUES ('a.2.2', '5', '3');
INSERT INTO "TREE" (NAME, ID) VALUES ('b', '6');

我想通过 id 返回完整的树,所以查询:

select name, id <<<TODO LOGIC>> where id = 1

我会得到

|  name  |  id  |
|  a     |  1   |
|  a.1   |  2   |
|  a.2   |  3   |
|  a.2.1 |  4   |
|  a.2.2 |  5   |

对于一个子树,我会得到:

select name, id <<<TODO LOGIC>> where id = 3

我会得到

|  name  |  id  |
|  a.2   |  3   |
|  a.2.1 |  4   |
|  a.2.2 |  5   |

其中,对于平面条目 b,它将得到 ​​p>

select name, id <<<TODO LOGIC>> where id = 6

我会得到

|  name  |  id  |
|  b     |  6   |

似乎简单的遗漏连接查询无法实现此目的,还是我遗漏了什么?

以下查询确实返回了完整的结构,但是当开始使用 where 语句进行过滤时,它会失败。

select t1.id t1Id, t2.id t2Id, t1.name t1Name, t2.name t2Name from tree t1 left outer join tree t2 on t1.id = t2.parent

【问题讨论】:

    标签: sql oracle tree hierarchy


    【解决方案1】:

    当您具有树结构时,您可能需要分层查询。这里是:

     select t.*
       from tree t
    connect by prior t.id = t.parent
      start with t.id = :id
      order siblings by t.id
    

    详情请见Hierarchical Queries。

    【讨论】:

      【解决方案2】:

      您可以在 Oracle 上使用 start with - connect by 语法。如果我没记错的话是这样的

      select * from Tree t
      start with t.ID = 1 connect by prior t.ID = t.Parent
      

      但是我没有 Oracle 可以立即检查它。也许是prior t.Parent = t.ID。请注意,它有时会很慢,请谨慎使用。

      另一种方法是创建表来存储节点之间的所有间接关系(不仅仅是a-a.1,还有a-a.2.1等)。您可以使用 PL/SQL 递归存储过程来填充它。两种方式:

      1. 简单的方法是创建一个完全重新填充间接表的过程。您可以在运行报告之前调用它。

      2. 如果你需要即时效果,你应该编写refill程序,以便它只为树中的一条记录更新间接关系。然后,您禁止直接插入和更新 Tree,并强制它们通过存储的 PL/SQL 过程(如 InsertTree/UpdateTree),而后者又会调用过程来更新具有间接关系的表。

      【讨论】:

      • 谢谢,SELECT id, name, parent FROM tree start with id = ?通过之前的 id = 父级连接;确实是解决办法。太棒了:)
      • 不客气。请务必在计划的数据量上测试查询速度 - 正如我所说,有时它可能真的很慢(特别是如果您加入更多表或添加一些额外条件)。
      【解决方案3】:

      您可以为此使用union,并且您需要限制树的深度以便可以在一次查询中选择它。

      SELECT id, name
      FROM TREE as node
      WHERE 
        node.id = :id
      UNION
      SELECT child1.id, child1.name
      FROM TREE as node
        inner join TREE as child1 on node.id = child1.parent
      WHERE 
        node.id = :id
      UNION
      SELECT child2.id, child2.name
      FROM TREE as node
        inner join TREE as child1 on node.id = child1.parent
        inner join TREE as child2 on child1.id = child2.parent
      WHERE 
        node.id = :id
      

      这里的问题是,SQL 在递归方面非常糟糕(而关系结构实际上在这方面很棒)。

      要使其完全动态化,请对树中的每个级别使用查询,或者如果有任何可用的内容,请使用数据库引擎特定的 SQL 扩展。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-26
        • 1970-01-01
        • 2013-07-13
        • 1970-01-01
        相关资源
        最近更新 更多