关于数据库:自引用表(Oracle)上的SQL递归查询

SQL recursive query on self referencing table (Oracle)

假设我有这个示例数据:

1
2
3
4
5
6
7
8
9
10
11
12
| Name     | ID | PARENT_ID |
-----------------------------
| a1       | 1  | NULL      |
| b2       | 2  | NULL      |
| c3       | 3  | NULL      |
| a1.d4    | 4  | 1         |
| a1.e5    | 5  | 1         |
| a1.d4.f6 | 6  | 4         |
| a1.d4.g7 | 7  | 4         |
| a1.e5.h8 | 8  | 5         |
| a2.i9    | 9  | 2         |
| a2.i9.j10| 10 | 9         |

我想选择从accountID=1开始的所有记录,因此预期结果是:

1
2
3
4
5
6
7
8
| Name     | ID | PARENT_NAME | PARENT_ID |
-------------------------------------------
| a1       | 1  | NULL        | NULL      |
| a1.d4    | 4  | a1          | 1         |
| a1.e5    | 5  | a1          | 1         |
| a1.d4.f6 | 6  | a1.d4       | 4         |
| a1.d4.g7 | 7  | a1.d4       | 4         |
| a1.e5.h8 | 8  | a1.e5       | 5         |

我目前可以进行递归选择,但随后无法从父引用访问数据,因此无法返回父名称。我使用的代码是(改编为简单的示例):

1
2
3
4
SELECT id, parent_id, name
FROM tbl
  START WITH id = 1
  CONNECT BY PRIOR id = parent_id

我应该使用什么SQL进行上述检索?

未来探索者的附加关键字:选择由同一表中的父键表示的分层数据的SQL


用途:

1
2
3
4
5
6
7
8
9
    SELECT t1.id,
           t1.parent_id,
           t1.name,
           t2.name AS parent_name,
           t2.id AS parent_id
      FROM tbl t1
 LEFT JOIN tbl t2 ON t2.id = t1.parent_id
START WITH t1.id = 1
CONNECT BY PRIOR t1.id = t1.parent_id


使用prior怎么样?

所以

1
2
3
4
SELECT id, parent_id, PRIOR name
   FROM tbl
START WITH id = 1
CONNECT BY PRIOR id = parent_id`

代码>

或者如果你想得到根名称

1
2
3
4
SELECT id, parent_id, CONNECT_BY_ROOT name
   FROM tbl
START WITH id = 1
CONNECT BY PRIOR id = parent_id


使用新的嵌套查询语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
WITH q(name, id, parent_id, parent_name) AS (
    SELECT
      t1.name, t1.id,
      NULL AS parent_id, NULL AS parent_name
    FROM t1
    WHERE t1.id = 1
  UNION ALL
    SELECT
      t1.name, t1.id,
      q.id AS parent_id, q.name AS parent_name
    FROM t1, q
    WHERE t1.parent_id = q.id
)
SELECT * FROM q

你想这样做吗?

1
2
3
SELECT id, parent_id, name,
 (SELECT Name FROM tbl WHERE id = t.parent_id) parent_name
FROM tbl t START WITH id = 1 CONNECT BY PRIOR id = parent_id

编辑另一个基于OMG的选项(但我认为它的性能是一样的):

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
           t1.id,
           t1.parent_id,
           t1.name,
           t2.name AS parent_name,
           t2.id AS parent_id
FROM
    (SELECT id, parent_id, name
    FROM tbl
    START WITH id = 1
    CONNECT BY prior id = parent_id) t1
    LEFT JOIN
    tbl t2 ON t2.id = t1.parent_id


这有点麻烦,但我相信这应该有效(没有额外的连接)。这假设您可以选择一个永远不会出现在相关字段中的字符作为分隔符。

你可以不用嵌套select来完成它,但是我发现这样做有点简单,它有四个对sys-connect-by-u-path的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT id,
       parent_id,
       CASE
         WHEN lvl <> 1
         THEN substr(name_path,
                     instr(name_path,'|',1,lvl-1)+1,
                     instr(name_path,'|',1,lvl)
                      -instr(name_path,'|',1,lvl-1)-1)
         END AS name
FROM (
  SELECT id, parent_id, sys_connect_by_path(name,'|') AS name_path, level AS lvl
  FROM tbl
  START WITH id = 1
  CONNECT BY PRIOR id = parent_id)