以树状结构返回与其祖先匹配的行的SQL查询

以树状结构返回与其祖先匹配的行的SQL查询,sql,oracle,tree,common-table-expression,recursive-query,Sql,Oracle,Tree,Common Table Expression,Recursive Query,我有一个Oracle 12c数据库,其中有一个用于组织单位(部门等)的树状结构表: 所以,有这样的结构 | id | name | parent_id | | -: | ------------- | --------: | | 1 | Root | (NULL) | | 2 | Territorial 1 | 1 | | 3 | Regional 1-1 | 2 | | 4 | Alpha dept |

我有一个Oracle 12c数据库,其中有一个用于组织单位(部门等)的树状结构表:

所以,有这样的结构

| 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'或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!
我看了各种:和,但找不出一个可以返回这样一个结果的查询

我使用的是Oracle数据库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;
但它返回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;

但它返回错误
SQL错误[905][42000]:ORA-00905:关键字丢失

您可以使用递归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;
选择distinct
可能不是必需的


递归CTE具有标准SQL的优点,因此许多不同的数据库都支持该逻辑。

使用递归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;
选择distinct
可能不是必需的


递归CTE具有标准SQL的优点,因此许多不同的数据库都支持该逻辑。

当然,您可以使用分层查询来实现这一点。问题是,如果您从多个叶子开始,在某个时候您将开始获得重复的行。您可以使用“distinct”删除重复项,但这会影响性能,尤其是在非常大的表上,或者如果开始时有太多的叶子。归根结底,递归查询通常更难编写,但比分层查询效率更高

为了完整性,这里是分层解决方案。使用SCOTT模式中的EMP表进行说明。首先显示直接的分层查询(以及输出中的副本),然后显示带有“distinct”的版本


当然,您可以为此使用分层查询。问题是,如果您从多个叶子开始,在某个时候您将开始获得重复的行。您可以使用“distinct”删除重复项,但这会影响性能,尤其是在非常大的表上,或者如果开始时有太多的叶子。归根结底,递归查询通常更难编写,但比分层查询效率更高

为了完整性,这里是分层解决方案。使用SCOTT模式中的EMP表进行说明。首先显示直接的分层查询(以及输出中的副本),然后显示带有“distinct”的版本


谢谢你的快速回复。不幸的是,查询只返回匹配的行,没有它们的祖先。Oracle还说递归CTE必须有一个别名列表(ORA-32039),所以我将第一行更改为
,t(name,id,parent_id)为(
)。我发现:
ON
子句中的等式应该颠倒:
ON us.id=t.parent_id
。现在它工作了!如果ID没有像示例中那样从父级增长到子级(如Oracle语法中的
对兄弟姐妹排序),是否可以按层次结构和名称对结果排序?@Envek。我认为是这样。这需要在递归CTE中保留级别信息。你可以用适当的样本数据和期望的结果问另一个问题。在我的例子中,我使用的是
lft
列,我可以通过它来订购。因此,我将您的查询放在子查询中,在外部查询中使用
selectdistinct id
并按
lft
排序。感谢您的快速回复。不幸的是,查询只返回匹配的行,没有它们的祖先。Oracle还说递归CTE必须有一个别名列表(ORA-32039),所以我将第一行更改为
,t(name,id,parent_id)为(
)。我发现:
ON
子句中的等式应该颠倒:
ON us.id=t.parent_id
。现在它工作了!如果ID没有像示例中那样从父级增长到子级(如Oracle语法中的
对兄弟姐妹排序),是否可以按层次结构和名称对结果排序?@Envek。我认为是这样。这需要在递归CTE中保留级别信息。你可以用适当的样本数据和期望的结果问另一个问题。在我的例子中,我使用的是
lft
列,我可以通过它来订购。因此,我将您的查询放在子查询中,在外部查询中使用
selectdistinct id
并按
lft
排序。谢谢!我成功地使用query
SELECT DISTINCT OU.*从OU开始,名称如“Alpha%”或名称如“Epsilon%”按先前的父项连接\u id=id按名称排序兄弟但是记录的顺序很奇怪。有什么办法吗?我在问题中添加了
CREATE TABLE
INSERT
s。所需的顺序是什么?请注意,如果在SELECT子句中使用DISTINCT关键字,则在分层查询的ORDER BY子句中会有一些限制。例如,如果希望根位于顶部,可以按级别DESC排序,但要这样做,需要在SELECT子句中包含伪列级别。谢谢!我成功地使用query
SELECT DISTINCT OU.*从OU开始,名称如“Alpha%”或名称如“Epsilon%”按先前的父项连接\u id=id按名称排序兄弟但是记录的顺序很奇怪。有什么办法吗?我在问题中添加了
CREATE TABLE
INSERT
s。所需的顺序是什么?请注意,如果在SELECT子句中使用DISTINCT关键字,则在分层查询的ORDER BY子句中会有一些限制。例如,如果希望根位于顶部,可以按级别DESC排序,但要这样做,需要在SELECT子句中包含伪列级别。
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