Hierarchical Queries
语法
connect by [nocycle] condition [start with condition]
start with condition connect by [nocycle] condition
condition
start with
指定层次查询的 root row
connect by
指定层次查询中 parent rows 和 cild rows 的关系
- NOCYCLE 参数指示 Oracle 数据库从查询中返回行,即使数据中存在 CONNECT BY 循环。 将此参数与 CONNECT_BY_ISCYCLE 伪列一起使用以查看哪些行包含循环。 有关详细信息,请参阅 CONNECT_BY_ISCYCLE 伪列。
- 在分层查询中,条件中的一个表达式必须使用 PRIOR 运算符限定以引用父行。 例如,
... PRIOR expr = expr
or
... expr = PRIOR expr
如果 CONNECT BY 条件是复合条件,则只有一个条件需要 PRIOR 运算符,尽管您可以有多个 PRIOR 条件。 例如:
CONNECT BY last_name != 'King' AND PRIOR employee_id = manager_id ...
CONNECT BY PRIOR employee_id = manager_id and
PRIOR account_mgr_id = customer_id ...
PRIOR 是一元运算符,与一元 + 和 - 算术运算符具有相同的优先级。 它为分层查询中当前行的父行计算紧随其后的表达式。
PRIOR 最常用于使用相等运算符比较列值时。 (PRIOR 关键字可以在运算符的任一侧。) PRIOR 使 Oracle 使用列中父行的值。 在 CONNECT BY 子句中理论上可以使用除等号 (=) 以外的运算符。 但是,这些其他运算符创建的条件可能会导致通过可能的组合的无限循环。 在这种情况下,Oracle 在运行时检测到循环并返回错误。
CONNECT BY 条件和 PRIOR 表达式都可以采用不相关子查询的形式。 但是,CURRVAL 和 NEXTVAL 不是有效的 PRIOR 表达式,因此 PRIOR 表达式不能引用序列。
您可以通过使用 CONNECT_BY_ROOT 运算符来进一步细化层次查询,以限定选择列表中的列。 此运算符不仅返回直接父行,而且返回层次结构中的所有祖先行,从而扩展了层次查询的 CONNECT BY [PRIOR] 条件的功能。
Oracle 按如下方式处理分层查询:
- 如果存在连接,则首先评估连接,无论连接是在 FROM 子句中指定还是使用 WHERE 子句谓词。
- 评估 CONNECT BY 条件。
- 评估任何剩余的 WHERE 子句谓词。
然后,Oracle 使用来自这些评估的信息通过以下步骤形成层次结构:
- Oracle 选择层次结构的根行——那些满足 START WITH 条件的行。
- Oracle 选择每个根行的子行。每个子行必须满足关于其中一个根行的 CONNECT BY 条件的条件。
- Oracle 选择连续几代的子行。 Oracle 首先选择步骤 2 中返回的行的子代,然后选择这些子代的子代,以此类推。 Oracle 总是通过评估与当前父行相关的 CONNECT BY 条件来选择子行。
- 如果查询包含没有连接的 WHERE 子句,则 Oracle 从层次结构中删除所有不满足 WHERE 子句条件的行。 Oracle 对每一行单独评估此条件,而不是删除不满足条件的行的所有子行。
- Oracle 按图 9-1 所示的顺序返回行。在图中,孩子出现在父母的下方。有关分层树的说明,请参见图 3-1。
为了找到父行的子行,Oracle 计算父行的 CONNECT BY 条件的 PRIOR 表达式和表中每一行的另一个表达式。 条件为真的行是父项的子项。 CONNECT BY 条件可以包含其他条件以进一步过滤查询选择的行。
如果 CONNECT BY 条件导致层次结构中出现循环,则 Oracle 返回错误。 如果一行既是另一行的父(或祖父母或直接祖先)又是子(或孙子或直接后代),则发生循环。
注意:在分层查询中,不要指定 ORDER BY 或 GROUP BY,因为它们会覆盖 CONNECT BY 结果的分层顺序。 如果要对同一父级的兄弟行进行排序,请使用 ORDER SIBLINGS BY 子句。 请参见 order_by_clause。
Hierarchical Query Examples
CONNECT BY Example
以下分层查询使用 CONNECT BY 子句来定义员工和经理之间的关系:
SELECT employee_id, last_name, manager_id
FROM employees
CONNECT BY PRIOR employee_id = manager_id;
EMPLOYEE_ID LAST_NAME MANAGER_ID
----------- ------------------------- ----------
101 Kochhar 100
108 Greenberg 101
109 Faviet 108
110 Chen 108
111 Sciarra 108
112 Urman 108
113 Popp 108
200 Whalen 101
203 Mavris 101
204 Baer 101
. . .
LEVEL Example
下一个示例与前面的示例类似,但使用 LEVEL 伪列来显示父行和子行:
SELECT employee_id, last_name, manager_id, LEVEL
FROM employees
CONNECT BY PRIOR employee_id = manager_id;
EMPLOYEE_ID LAST_NAME MANAGER_ID LEVEL
----------- ------------------------- ---------- ----------
101 Kochhar 100 1
108 Greenberg 101 2
109 Faviet 108 3
110 Chen 108 3
111 Sciarra 108 3
112 Urman 108 3
113 Popp 108 3
200 Whalen 101 2
203 Mavris 101 2
204 Baer 101 2
205 Higgins 101 2
206 Gietz 205 3
102 De Haan 100 1
...
START WITH Examples
下一个示例添加一个 START WITH 子句来指定层次结构的根行,并使用 SIBLINGS 关键字添加一个 ORDER BY 子句来保持层次结构内的顺序:
SELECT last_name, employee_id, manager_id, LEVEL
FROM employees
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY last_name;
LAST_NAME EMPLOYEE_ID MANAGER_ID LEVEL
------------------------- ----------- ---------- ----------
King 100 1
Cambrault 148 100 2
Bates 172 148 3
Bloom 169 148 3
Fox 170 148 3
Kumar 173 148 3
Ozer 168 148 3
Smith 171 148 3
De Haan 102 100 2
Hunold 103 102 3
Austin 105 103 4
Ernst 104 103 4
Lorentz 107 103 4
Pataballa 106 103 4
Errazuriz 147 100 2
Ande 166 147 3
Banda 167 147 3
...
在 hr.employees 表中,员工 Steven King 是公司的负责人,没有经理。 他的员工中有 John Russell,他是部门 80 的经理。如果您更新 employees 表以将 Russell 设置为 King 的经理,您会在数据中创建一个循环:
UPDATE employees SET manager_id = 145
WHERE employee_id = 100;
SELECT last_name "Employee",
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE level <= 3 AND department_id = 80
START WITH last_name = 'King'
CONNECT BY PRIOR employee_id = manager_id AND LEVEL <= 4;
ERROR:
ORA-01436: CONNECT BY loop in user data
CONNECT BY 条件中的 NOCYCLE 参数使 Oracle 尽管有循环仍返回行。 CONNECT_BY_ISCYCLE 伪列显示哪些行包含循环:
SELECT last_name "Employee", CONNECT_BY_ISCYCLE "Cycle",
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE level <= 3 AND department_id = 80
START WITH last_name = 'King'
CONNECT BY NOCYCLE PRIOR employee_id = manager_id AND LEVEL <= 4
ORDER BY "Employee", "Cycle", LEVEL, "Path";
Employee Cycle LEVEL Path
------------------------- ---------- ---------- -------------------------
Abel 0 3 /King/Zlotkey/Abel
Ande 0 3 /King/Errazuriz/Ande
Banda 0 3 /King/Errazuriz/Banda
Bates 0 3 /King/Cambrault/Bates
Bernstein 0 3 /King/Russell/Bernstein
Bloom 0 3 /King/Cambrault/Bloom
Cambrault 0 2 /King/Cambrault
Cambrault 0 3 /King/Russell/Cambrault
Doran 0 3 /King/Partners/Doran
Errazuriz 0 2 /King/Errazuriz
Fox 0 3 /King/Cambrault/Fox
...
CONNECT_BY_ISLEAF Example
以下语句显示了如何使用分层查询将列中的值转换为逗号分隔的列表:
SELECT LTRIM(SYS_CONNECT_BY_PATH (warehouse_id,','),',') FROM
(SELECT ROWNUM r, warehouse_id FROM warehouses)
WHERE CONNECT_BY_ISLEAF = 1
START WITH r = 1
CONNECT BY r = PRIOR r + 1
ORDER BY warehouse_id;
LTRIM(SYS_CONNECT_BY_PATH(WAREHOUSE_ID,','),',')
--------------------------------------------------------------------------------
1,2,3,4,5,6,7,8,9
CONNECT_BY_ROOT Examples
以下示例返回部门 110 中每个员工的姓氏、层次结构中该员工上方最高级别的每个经理、经理和员工之间的级别数以及两者之间的路径:
SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager",
LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE LEVEL > 1 and department_id = 110
CONNECT BY PRIOR employee_id = manager_id
ORDER BY "Employee", "Manager", "Pathlen", "Path";
Employee Manager Pathlen Path
--------------- --------------- ---------- ------------------------------
Gietz Higgins 1 /Higgins/Gietz
Gietz King 3 /King/Kochhar/Higgins/Gietz
Gietz Kochhar 2 /Kochhar/Higgins/Gietz
Higgins King 2 /King/Kochhar/Higgins
Higgins Kochhar 1 /Kochhar/Higgins
以下示例使用 GROUP BY 子句返回部门 110 中每个员工的总工资以及层次结构中该员工之上的所有员工:
SELECT name, SUM(salary) "Total_Salary" FROM (
SELECT CONNECT_BY_ROOT last_name as name, Salary
FROM employees
WHERE department_id = 110
CONNECT BY PRIOR employee_id = manager_id)
GROUP BY name
ORDER BY name, "Total_Salary";
NAME Total_Salary
------------------------- ------------
Gietz 8300
Higgins 20300
King 20300
Kochhar 20300
Hierarchical Query Operators
两个运算符 PRIOR 和 CONNECT_BY_ROOT 仅在分层查询中有效
PRIOR
在分层查询中,CONNECT BY 条件中的一个表达式必须由 PRIOR 运算符限定。 如果 CONNECT BY 条件是复合条件,则只有一个条件需要 PRIOR 运算符,尽管您可以有多个 PRIOR 条件。 PRIOR 计算层次查询中当前行的父行的紧随其后的表达式。
PRIOR 最常用于使用相等运算符比较列值时。 (PRIOR 关键字可以在运算符的任一侧。) PRIOR 使 Oracle 使用列中父行的值。 在 CONNECT BY 子句中理论上可以使用除等号 (=) 以外的运算符。 但是,这些其他运算符创建的条件可能会导致通过可能的组合的无限循环。 在这种情况下,Oracle 在运行时检测到循环并返回错误。 有关此运算符的更多信息(包括示例),请参阅分层查询。
CONNECT_BY_ROOT
CONNECT_BY_ROOT 是一元运算符,仅在分层查询中有效。 当您使用此运算符限定列时,Oracle 使用根行中的数据返回列值。 此运算符扩展了分层查询的 CONNECT BY [PRIOR] 条件的功能。
对 CONNECT_BY_ROOT 的限制
您不能在 START WITH 条件或 CONNECT BY 条件中指定此运算符。
Hierarchical Query Pseudocolumns 伪列
分层查询伪列仅在分层查询中有效。 分层查询伪列是:
- [CONNECT_BY_ISCYCLE Pseudocolumn]
- [CONNECT_BY_ISLEAF Pseudocolumn]
- [LEVEL Pseudocolumn]
要在查询中定义层次关系,您必须使用 CONNECT BY 子句。
CONNECT_BY_ISCYCLE Pseudocolumn
如果当前行有一个也是其祖先的子项,则 CONNECT_BY_ISCYCLE 伪列返回 1。 否则返回 0。
仅当您已指定 CONNECT BY 子句的 NOCYCLE 参数时,您才能指定 CONNECT_BY_ISCYCLE。 NOCYCLE 使 Oracle 能够返回查询的结果,否则该查询会因数据中的 CONNECT BY 循环而失败。
CONNECT_BY_ISLEAF Pseudocolumn
如果当前行是由 CONNECT BY 条件定义的树的叶子,则 CONNECT_BY_ISLEAF 伪列返回 1。 否则返回 0。此信息指示是否可以进一步扩展给定行以显示更多层次结构。
CONNECT_BY_ISLEAF Example
以下示例显示了 hr.employees 表的前三个级别,为每一行指示它是叶行(在 IsLeaf 列中用 1 表示)还是有子行(在 IsLeaf 列中用 0 表示):
SELECT last_name "Employee", CONNECT_BY_ISLEAF "IsLeaf",
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE LEVEL <= 3 AND department_id = 80
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id AND LEVEL <= 4
ORDER BY "Employee", "IsLeaf";
Employee IsLeaf LEVEL Path
------------------------- ---------- ---------- -------------------------
Abel 1 3 /King/Zlotkey/Abel
Ande 1 3 /King/Errazuriz/Ande
Banda 1 3 /King/Errazuriz/Banda
Bates 1 3 /King/Cambrault/Bates
Bernstein 1 3 /King/Russell/Bernstein
Bloom 1 3 /King/Cambrault/Bloom
Cambrault 0 2 /King/Cambrault
Cambrault 1 3 /King/Russell/Cambrault
Doran 1 3 /King/Partners/Doran
Errazuriz 0 2 /King/Errazuriz
Fox 1 3 /King/Cambrault/Fox
. . .
LEVEL Pseudocolumn
对于分层查询返回的每一行,LEVEL 伪列为根行返回 1,为根的子行返回 2,依此类推。 根行是倒排树中的最高行。 子行是任何非根行。 父行是具有子行的任何行。 叶行是任何没有子行的行。 图 3-1 显示了倒排树的节点及其 LEVEL 值。
SYS_CONNECT_BY_PATH
SYS_CONNECT_BY_PATH (column, char)
目的
SYS_CONNECT_BY_PATH 仅在分层查询中有效。 它返回列值从根到节点的路径,对于 CONNECT BY 条件返回的每一行,列值由 char 分隔。
column 和 char 都可以是任何数据类型 CHAR、VARCHAR2、NCHAR 或 NVARCHAR2。 返回的字符串是 VARCHAR2 数据类型,并且与列在同一字符集中。
例子
以下示例返回从员工 Kochhar 到 Kochhar 的所有员工(及其员工)的员工姓名路径:
SELECT LPAD(' ', 2*level-1)||SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
START WITH last_name = 'Kochhar'
CONNECT BY PRIOR employee_id = manager_id;
Path
------------------------------
/Kochhar/Greenberg/Chen
/Kochhar/Greenberg/Faviet
/Kochhar/Greenberg/Popp
/Kochhar/Greenberg/Sciarra
/Kochhar/Greenberg/Urman
/Kochhar/Higgins/Gietz
/Kochhar/Baer
/Kochhar/Greenberg
/Kochhar/Higgins
/Kochhar/Mavris
/Kochhar/Whalen
/Kochhar
例子
CREATE TABLE test_tree (
test_id INT NOT NULL,
pid INT,
test_val VARCHAR(10),
PRIMARY KEY (test_id)
);
INSERT INTO test_tree VALUES(1, 0, '.NET');
INSERT INTO test_tree VALUES(2, 1, 'C#');
INSERT INTO test_tree VALUES(3, 1, 'J#');
INSERT INTO test_tree VALUES(4, 1, 'ASP.NET');
INSERT INTO test_tree VALUES(5, 1, 'VB.NET');
INSERT INTO test_tree VALUES(6, 0, 'J2EE');
INSERT INTO test_tree VALUES(7, 6, 'EJB');
INSERT INTO test_tree VALUES(8, 6, 'Servlet');
INSERT INTO test_tree VALUES(9, 6, 'JSP');
INSERT INTO test_tree VALUES(10, 0, 'Database');
INSERT INTO test_tree VALUES(11, 10, 'DB2');
INSERT INTO test_tree VALUES(12, 10, 'MySQL');
INSERT INTO test_tree VALUES(13, 10, 'Oracle');
INSERT INTO test_tree VALUES(14, 10, 'SQL Server');
INSERT INTO test_tree VALUES(15, 13, 'PL/SQL');
INSERT INTO test_tree VALUES(16, 15, 'Function');
INSERT INTO test_tree VALUES(17, 15, 'Procedure');
INSERT INTO test_tree VALUES(18, 15, 'Package');
INSERT INTO test_tree VALUES(19, 15, 'Cursor');
INSERT INTO test_tree VALUES(20, 14, 'T-SQL');
SELECT
LEVEL,
test_id,
test_val,
SYS_CONNECT_BY_PATH(test_val, '\') AS "FullPath"
FROM
test_tree
START WITH
pid =0
CONNECT BY PRIOR test_id = pid
ORDER SIBLINGS BY test_val;
SELECT test_id, pid, test_val from test_tree
1 0 .NET
2 1 C#
3 1 J#
4 1 ASP.NET
5 1 VB.NET
6 0 J2EE
7 6 EJB
8 6 Servlet
9 6 JSP
10 0 Database
11 10 DB2
12 10 MySQL
13 10 Oracle
14 10 SQL Server
15 13 PL/SQL
16 15 Function
17 15 Procedure
18 15 Package
19 15 Cursor
20 14 T-SQL
SELECT level ,test_id, pid, test_val from test_tree CONNECT BY prior test_id= pid
order by 1,2
1 1 0 .NET
1 2 1 C#
1 3 1 J#
1 4 1 ASP.NET
1 5 1 VB.NET
1 6 0 J2EE
1 7 6 EJB
1 8 6 Servlet
1 9 6 JSP
1 10 0 Database
1 11 10 DB2
1 12 10 MySQL
1 13 10 Oracle
1 14 10 SQL Server
1 15 13 PL/SQL
1 16 15 Function
1 17 15 Procedure
1 18 15 Package
1 19 15 Cursor
1 20 14 T-SQL
2 2 1 C#
2 3 1 J#
2 4 1 ASP.NET
2 5 1 VB.NET
2 7 6 EJB
2 8 6 Servlet
2 9 6 JSP
2 11 10 DB2
2 12 10 MySQL
2 13 10 Oracle
2 14 10 SQL Server
2 15 13 PL/SQL
2 16 15 Function
2 17 15 Procedure
2 18 15 Package
2 19 15 Cursor
2 20 14 T-SQL
3 15 13 PL/SQL
3 16 15 Function
3 17 15 Procedure
3 18 15 Package
3 19 15 Cursor
3 20 14 T-SQL
4 16 15 Function
4 17 15 Procedure
4 18 15 Package
4 19 15 Cursor
SELECT level ,test_id, pid, test_val from test_tree start with test_id=10 CONNECT BY prior test_id= pid order by 1,2
1 10 0 Database
2 11 10 DB2
2 12 10 MySQL
2 13 10 Oracle
2 14 10 SQL Server
3 15 13 PL/SQL
3 20 14 T-SQL
4 16 15 Function
4 17 15 Procedure
4 18 15 Package
4 19 15 Cursor
标签:10,15,层次,PRIOR,查询,test,CONNECT,oracle,id
From: https://www.cnblogs.com/Heoric/p/16847630.html