SQL 查询到 return 匹配的行及其树状结构中的祖先
SQL query to return matched rows with their ancestors in tree-like structure
我有一个 Oracle 12c 数据库,其中 table 具有组织单位(部门等)的树状结构:
CREATE TABLE "OUS" (
"ID" NUMBER(38,0) NOT NULL ENABLE,
"NAME" VARCHAR2(255 CHAR) NOT NULL ENABLE,
"PARENT_ID" NUMBER(38,0),
PRIMARY KEY("ID"),
CONSTRAINT "OUS_HIERARCHY_FK" FOREIGN KEY ("PARENT_ID") REFERENCES "OUS" ("ID") ON DELETE CASCADE
);
所以,结构像
| id | name | parent_id |
| -: | ------------- | --------: |
| 1 | Root | (NULL) |
| 2 | Territorial 1 | 1 |
| 3 | Regional 1-1 | 2 |
| 4 | Alpha dept | 3 |
| 5 | Beta dept | 3 |
| 6 | Regional 1-2 | 2 |
| 7 | Gamma dept | 6 |
| 8 | Delta dept | 7 |
| 9 | Territorial 2 | 1 |
| 10 | Regional 2-1 | 9 |
| 11 | Epsilon dept | 10 |
| 12 | Zeta dept | 10 |
您可以使用 SQL 创建它,例如:
INSERT INTO ous (id, name, parent_id) VALUES ( 13, 'Root', NULL);
INSERT INTO ous (id, name, parent_id) VALUES ( 2, 'Territorial 1', 13);
INSERT INTO ous (id, name, parent_id) VALUES ( 1, 'Regional 1-1', 2);
INSERT INTO ous (id, name, parent_id) VALUES ( 5, 'Alpha dept', 1);
INSERT INTO ous (id, name, parent_id) VALUES ( 4, 'Beta dept', 1);
INSERT INTO ous (id, name, parent_id) VALUES ( 6, 'Regional 1-2', 2);
INSERT INTO ous (id, name, parent_id) VALUES ( 7, 'Gamma dept', 6);
INSERT INTO ous (id, name, parent_id) VALUES ( 8, 'Delta dept', 6);
INSERT INTO ous (id, name, parent_id) VALUES ( 9, 'Territorial 2', 13);
INSERT INTO ous (id, name, parent_id) VALUES ( 3, 'Regional 2-1', 9);
INSERT INTO ous (id, name, parent_id) VALUES ( 15, 'Epsilon dept', 3);
INSERT INTO ous (id, name, parent_id) VALUES ( 12, 'Zeta dept', 3);
我需要找到一些 OU,匹配给定的条件(如 name = 'Alpha' OR name = 'Epsilon
)并获得这些 OU 及其祖先的子树。
例如:
| id | name | parent_id |
| -: | ------------- | --------: |
| 1 | Root | (NULL) | ← Ancestor of Alpha and Epsilon
| 2 | Territorial 1 | 1 | ← Ancestor of Alpha
| 3 | Regional 1-1 | 2 | ← Ancestor of Alpha
| 4 | Alpha dept | 3 | ← Matches the WHERE clause!
| 9 | Territorial 2 | 1 | ← Ancestor of Epsilon
| 10 | Regional 2-1 | 9 | ← Ancestor of Epsilon
| 11 | Epsilon dept | 10 | ← Matches the WHERE clause!
我查看了各种 Hierarchical and recursive queries in SQL: Oracle Hierarchical queries and CTEs,但无法找出可以 return 我这样的结果的查询。
我正在使用 Oracle Database 12c。
我试过这样的查询:
SELECT ous.* FROM ous
WHERE name = 'Alpha' OR name = 'Epsilon'
START WITH
parent_id IS NULL
CONNECT BY
PRIOR id = parent_id
ORDER SIBLINGS BY name;
但是它 returns 0 行,因为 WHERE 应用于所有行(所以祖先被过滤)
我也试过了:
WITH RECURSIVE all_nodes (id, parent_id, name) AS (
SELECT ous.id, ous.parent_id, name FROM ous WHERE (name = 'Alpha' OR name = 'Epsilon')
UNION
SELECT ous.id, ous.parent_id, name FROM ous INNER JOIN all_nodes ON ous.parent_id = all_nodes.id
)
SELECT * FROM all_nodes INNER JOIN ous ON all_nodes.id = ous.id ORDER BY name;
但是 return 错误 SQL Error [905] [42000]: ORA-00905: keyword is missing
您可以使用递归 CTE 执行此操作:
with t(name, id, parent_id) as (
select name, id, parent_id
from ous
where name in ('alpha', 'epsilon')
union all
select ous.name, ous.id, ous.parent_id
from t join
ous
on ous.id = t.parent_id
)
select distinct t.id, t.name, t.parent
from t
order by t.id;
可能不需要select distinct
。
递归 CTE 具有标准 SQL 的优势,因此许多不同的数据库都支持该逻辑。
当然,您可以为此使用分层查询。问题是,如果你从多个叶子开始,在某个时候你会开始得到重复的行。您可以使用 "distinct" 删除重复项,但这会影响性能,尤其是在非常大的 table 上或者如果您开始时有太多叶子。归根结底,递归查询通常更难编写,但比分层查询更高效。
为了完整起见,这里是分层解决方案。使用 SCOTT 架构中的 EMP table 进行说明。首先显示直接分层查询(以及输出中的重复项),然后显示带有 "distinct."
的版本
select empno, mgr
from scott.emp
start with empno in (7902, 7788)
connect by prior mgr = empno
;
EMPNO MGR
---------- ----------
7788 7566
7566 7839
7839
7902 7566
7566 7839
7839
select distinct empno, mgr
from scott.emp
start with empno in (7902, 7788)
connect by prior mgr = empno
;
EMPNO MGR
---------- ----------
7839
7566 7839
7902 7566
7788 7566
我有一个 Oracle 12c 数据库,其中 table 具有组织单位(部门等)的树状结构:
CREATE TABLE "OUS" (
"ID" NUMBER(38,0) NOT NULL ENABLE,
"NAME" VARCHAR2(255 CHAR) NOT NULL ENABLE,
"PARENT_ID" NUMBER(38,0),
PRIMARY KEY("ID"),
CONSTRAINT "OUS_HIERARCHY_FK" FOREIGN KEY ("PARENT_ID") REFERENCES "OUS" ("ID") ON DELETE CASCADE
);
所以,结构像
| id | name | parent_id |
| -: | ------------- | --------: |
| 1 | Root | (NULL) |
| 2 | Territorial 1 | 1 |
| 3 | Regional 1-1 | 2 |
| 4 | Alpha dept | 3 |
| 5 | Beta dept | 3 |
| 6 | Regional 1-2 | 2 |
| 7 | Gamma dept | 6 |
| 8 | Delta dept | 7 |
| 9 | Territorial 2 | 1 |
| 10 | Regional 2-1 | 9 |
| 11 | Epsilon dept | 10 |
| 12 | Zeta dept | 10 |
您可以使用 SQL 创建它,例如:
INSERT INTO ous (id, name, parent_id) VALUES ( 13, 'Root', NULL);
INSERT INTO ous (id, name, parent_id) VALUES ( 2, 'Territorial 1', 13);
INSERT INTO ous (id, name, parent_id) VALUES ( 1, 'Regional 1-1', 2);
INSERT INTO ous (id, name, parent_id) VALUES ( 5, 'Alpha dept', 1);
INSERT INTO ous (id, name, parent_id) VALUES ( 4, 'Beta dept', 1);
INSERT INTO ous (id, name, parent_id) VALUES ( 6, 'Regional 1-2', 2);
INSERT INTO ous (id, name, parent_id) VALUES ( 7, 'Gamma dept', 6);
INSERT INTO ous (id, name, parent_id) VALUES ( 8, 'Delta dept', 6);
INSERT INTO ous (id, name, parent_id) VALUES ( 9, 'Territorial 2', 13);
INSERT INTO ous (id, name, parent_id) VALUES ( 3, 'Regional 2-1', 9);
INSERT INTO ous (id, name, parent_id) VALUES ( 15, 'Epsilon dept', 3);
INSERT INTO ous (id, name, parent_id) VALUES ( 12, 'Zeta dept', 3);
我需要找到一些 OU,匹配给定的条件(如 name = 'Alpha' OR name = 'Epsilon
)并获得这些 OU 及其祖先的子树。
例如:
| id | name | parent_id |
| -: | ------------- | --------: |
| 1 | Root | (NULL) | ← Ancestor of Alpha and Epsilon
| 2 | Territorial 1 | 1 | ← Ancestor of Alpha
| 3 | Regional 1-1 | 2 | ← Ancestor of Alpha
| 4 | Alpha dept | 3 | ← Matches the WHERE clause!
| 9 | Territorial 2 | 1 | ← Ancestor of Epsilon
| 10 | Regional 2-1 | 9 | ← Ancestor of Epsilon
| 11 | Epsilon dept | 10 | ← Matches the WHERE clause!
我查看了各种 Hierarchical and recursive queries in SQL: Oracle Hierarchical queries and CTEs,但无法找出可以 return 我这样的结果的查询。
我正在使用 Oracle Database 12c。
我试过这样的查询:
SELECT ous.* FROM ous
WHERE name = 'Alpha' OR name = 'Epsilon'
START WITH
parent_id IS NULL
CONNECT BY
PRIOR id = parent_id
ORDER SIBLINGS BY name;
但是它 returns 0 行,因为 WHERE 应用于所有行(所以祖先被过滤)
我也试过了:
WITH RECURSIVE all_nodes (id, parent_id, name) AS (
SELECT ous.id, ous.parent_id, name FROM ous WHERE (name = 'Alpha' OR name = 'Epsilon')
UNION
SELECT ous.id, ous.parent_id, name FROM ous INNER JOIN all_nodes ON ous.parent_id = all_nodes.id
)
SELECT * FROM all_nodes INNER JOIN ous ON all_nodes.id = ous.id ORDER BY name;
但是 return 错误 SQL Error [905] [42000]: ORA-00905: keyword is missing
您可以使用递归 CTE 执行此操作:
with t(name, id, parent_id) as (
select name, id, parent_id
from ous
where name in ('alpha', 'epsilon')
union all
select ous.name, ous.id, ous.parent_id
from t join
ous
on ous.id = t.parent_id
)
select distinct t.id, t.name, t.parent
from t
order by t.id;
可能不需要select distinct
。
递归 CTE 具有标准 SQL 的优势,因此许多不同的数据库都支持该逻辑。
当然,您可以为此使用分层查询。问题是,如果你从多个叶子开始,在某个时候你会开始得到重复的行。您可以使用 "distinct" 删除重复项,但这会影响性能,尤其是在非常大的 table 上或者如果您开始时有太多叶子。归根结底,递归查询通常更难编写,但比分层查询更高效。
为了完整起见,这里是分层解决方案。使用 SCOTT 架构中的 EMP table 进行说明。首先显示直接分层查询(以及输出中的重复项),然后显示带有 "distinct."
的版本select empno, mgr
from scott.emp
start with empno in (7902, 7788)
connect by prior mgr = empno
;
EMPNO MGR
---------- ----------
7788 7566
7566 7839
7839
7902 7566
7566 7839
7839
select distinct empno, mgr
from scott.emp
start with empno in (7902, 7788)
connect by prior mgr = empno
;
EMPNO MGR
---------- ----------
7839
7566 7839
7902 7566
7788 7566