【问题标题】:Hierarchical query for graph from subquery factoring to Oracles ``connect by``从子查询分解到 Oracle ``connect by`` 的图形分层查询
【发布时间】:2018-05-23 09:08:16
【问题描述】:

我有一个简单的有向图结构,形式为:

CREATE TABLE node (id NUMBER(8, 0), NAME VARCHAR2(100));
CREATE TABLE edge (parent NUMBER(8, 0), child NUMBER(8, 0));

其中node 存储所有节点,edge 存储从parentchild 的所有有向边。 我现在想要的是一个视图,它显示从所有根(没有传入边的节点)到图中所有其他节点(包括零长度路径)的所有路径。该图保证没有循环。

我使用了使用子查询分解的分层查询,结果如下:

WITH rec_view(id, parent, lvl, root_id, path) AS (
    SELECT
        id
        , NULL
        , 1 lvl
        , id root_id
        , '/' || TO_CHAR(id) path
    FROM node g
    WHERE NOT EXISTS (SELECT 1 FROM edge h WHERE h.child = g.id)
    UNION ALL
    SELECT
        hier.child
        , prev.id
        , prev.lvl + 1 lvl
        , prev.root_id
        , prev.path || '/' || hier.child path
    FROM rec_view prev
        JOIN edge hier ON prev.id = hier.parent 
)
SELECT
    n.id, n.NAME, v.root_id, v.lvl, v.path
FROM
    rec_view v
    JOIN node n ON v.id = n.id

有了这个测试数据,我得到了 10 行的预期结果。

INSERT INTO node VALUES (1, 'KBR');
INSERT INTO node VALUES (2, 'H');
INSERT INTO node VALUES (3, 'N');
INSERT INTO node VALUES (4, 'KBR H');
INSERT INTO node VALUES (5, 'KBR N');
INSERT INTO node VALUES (6, 'Dummy');
INSERT INTO node VALUES (7, 'Nach H');

INSERT INTO edge VALUES (1, 4);
INSERT INTO edge VALUES (1, 5);
INSERT INTO edge VALUES (2, 4);
INSERT INTO edge VALUES (3, 5);
INSERT INTO edge VALUES (4, 7);

ID  Name   Root  Level    Path
------------------------------
1   KBR       1      1      /1
2   H         2      1      /2
3   N         3      1      /3
4   KBR H     1      2    /1/4
4   KBR H     2      2    /2/4
5   KBR N     1      2    /1/5
5   KBR N     3      2    /3/5
6   Dummy     6      1      /6
7   Nach H    2      3  /2/4/7
7   Nach H    1      3  /1/4/7

但是,我想要的是一个使用 Oracles CONNECT BY 功能的查询,因为我认为它比子查询分解更容易阅读和理解,它也是我们公司分层查询的首选方式。但是我提出的问题给我带来了太多的结果。我得到的是这样的:

SELECT
    parent.id id
    , parent.NAME NAME
    , connect_by_root parent.NAME root_id
    , LEVEL lvl
    , sys_connect_by_path(parent.id, '/') path
FROM
    node parent
    LEFT JOIN edge hier ON parent.id = hier.parent
START WITH
    NOT EXISTS (SELECT 1 FROM edge h WHERE h.child = parent.id)
CONNECT BY
    PRIOR hier.child = parent.id

这里我得到 11 行,而不是路径 /1 的行包含两次的 10 行。

ID  Name    Root  Level    Path
-------------------------------
1   KBR        1      1      /1
4   KBR H      1      2    /1/4
7   Nach H     1      3  /1/4/7
1   KBR        1      1      /1
5   KBR N      1      2    /1/5
2   H          2      1      /2
4   KBR H      2      2    /2/4
7   Nach H     2      3  /2/4/7
3   N          3      1      /3
5   KBR N      3      2    /3/5
6   Dummy      6      1      /6

我的查询有什么问题,如何解决这个问题以仅包含所需的行?

【问题讨论】:

    标签: sql oracle hierarchy


    【解决方案1】:

    我的查询有什么问题,如何解决这个问题以仅包含所需的行?

    您的查询没有问题。分层查询正在考虑边缘,因此如果您包含 child 列,您将看到带有 id = 1 的行的差异:

    SQL Fiddle

    查询 1

    SELECT parent.id id
         , parent.NAME NAME
         , connect_by_root parent.NAME root_id
         , LEVEL lvl
         , sys_connect_by_path(parent.id, '/') path
         , child
    FROM   node parent
           LEFT JOIN edge hier
           ON parent.id = hier.parent
    START WITH
        NOT EXISTS (SELECT 1 FROM edge h WHERE h.child = parent.id)
    CONNECT BY
        PRIOR hier.child = parent.id
    

    Results

    | ID |   NAME | ROOT_ID | LVL |   PATH |  CHILD |
    |----|--------|---------|-----|--------|--------|
    |  1 |    KBR |     KBR |   1 |     /1 |      4 |
    ...
    |  1 |    KBR |     KBR |   1 |     /1 |      5 |
    ...
    

    行是不同的,因为它们有不同的子边。

    如果您只想获得不同的行,请添加 DISTINCT 关键字。

    查询 2

    SELECT DISTINCT
           parent.id id
         , parent.NAME NAME
         , connect_by_root parent.NAME root_id
         , LEVEL lvl
         , sys_connect_by_path(parent.id, '/') path
    FROM   node parent
           LEFT JOIN edge hier
           ON parent.id = hier.parent
    START WITH
        NOT EXISTS (SELECT 1 FROM edge h WHERE h.child = parent.id)
    CONNECT BY
        PRIOR hier.child = parent.id
    

    Results

    | ID |   NAME | ROOT_ID | LVL |   PATH |
    |----|--------|---------|-----|--------|
    |  2 |      H |       H |   1 |     /2 |
    |  7 | Nach H |       H |   3 | /2/4/7 |
    |  4 |  KBR H |       H |   2 |   /2/4 |
    |  4 |  KBR H |     KBR |   2 |   /1/4 |
    |  7 | Nach H |     KBR |   3 | /1/4/7 |
    |  5 |  KBR N |     KBR |   2 |   /1/5 |
    |  1 |    KBR |     KBR |   1 |     /1 |
    |  3 |      N |       N |   1 |     /3 |
    |  5 |  KBR N |       N |   2 |   /3/5 |
    |  6 |  Dummy |   Dummy |   1 |     /6 |
    

    【讨论】:

      【解决方案2】:

      左连接查询没有按预期返回结果:

      SQL> SELECT *
        2  FROM
        3      node parent
        4      LEFT JOIN edge hier ON parent.id = hier.parent  ;
             ID NAME                                                                                PARENT     CHILD
      --------- -------------------------------------------------------------------------------- --------- ---------
              1 KBR                                                                                      1         4
              1 KBR                                                                                      1         5
              2 H                                                                                        2         4
              3 N                                                                                        3         5
              4 KBR H                                                                                    4         7
              6 Dummy                                                                                      
              7 Nach H                                                                                     
              5 KBR N                                                                                      
      8 rows selected
      

      Parent.id 应该加入 heir.child 以使结果具有适当的形状,可以与 connect by 子句一起使用

      SQL> SELECT *
        2  FROM
        3      node parent
        4      LEFT JOIN edge hier ON parent.id = hier.child  ;
             ID NAME                                                                                PARENT     CHILD
      --------- -------------------------------------------------------------------------------- --------- ---------
              4 KBR H                                                                                    1         4
              5 KBR N                                                                                    1         5
              4 KBR H                                                                                    2         4
              5 KBR N                                                                                    3         5
              7 Nach H                                                                                   4         7
              6 Dummy                                                                                      
              1 KBR                                                                                        
              2 H                                                                                          
              3 N                                                                                          
      9 rows selected
      

      现在,可以将 parent 设置为 null 并将 heir.parent 连接到 parent.id 的优先级以获得正确的层次结构

      SQL> column name format a20
      SQL> column root_id format a10
      SQL> column path format a10
      SQL> 
      SQL> SELECT
        2      parent.id id
        3      , parent.NAME NAME
        4      , connect_by_root parent.NAME root_id
        5      , LEVEL lvl
        6      , sys_connect_by_path(parent.id, '/') path
        7  FROM
        8      node parent
        9      LEFT JOIN edge hier ON parent.id = hier.child
       10  START WITH
       11      PARENT IS NULL
       12  CONNECT BY
       13      hier.parent = PRIOR parent.id;
             ID NAME                 ROOT_ID           LVL PATH
      --------- -------------------- ---------- ---------- ----------
              1 KBR                  KBR                 1 /1
              4 KBR H                KBR                 2 /1/4
              7 Nach H               KBR                 3 /1/4/7
              5 KBR N                KBR                 2 /1/5
              2 H                    H                   1 /2
              4 KBR H                H                   2 /2/4
              7 Nach H               H                   3 /2/4/7
              3 N                    N                   1 /3
              5 KBR N                N                   2 /3/5
              6 Dummy                Dummy               1 /6
      10 rows selected
      
      SQL> 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 2019-10-07
        • 1970-01-01
        • 2011-09-18
        • 1970-01-01
        相关资源
        最近更新 更多