SQL 递归子查询分解
SQL Recursive Sub Query Factoring
使用以下信息考虑 table。
Table数据:
Code_ID
Name
Head_Name
Head_Rank
Report_To_Code
1
ABC
XYZ
07
2
DEF
BBB
01
1
3
GHI
ZZZ
02
1
4
JFK
XXX
10
2
样本:
CREATE TABLE TEST_01
( CODE_ID NUMBER(5),
NAME VARCHAR2(3),
HEAD_NAME VARCHAR2(3),
HEAD_RANK VARCHAR2(2),
REPORT_TO_CODE NUMBER(5)
);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (1, 'ABC', 'XYZ', '07', null);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (2, 'DEF', 'BBB', '01', 1);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (3, 'GHI', 'ZZZ', '02', 1);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (4, 'JFK', 'XXX', '10', 2);
commit;
我们的最终输出需要存储为层次结构,如下所示。
Code_ID
Name
Head_Name
Head_Rank
L1_Code
L2_Code
L3_Code
..
L12_Code
R_ID
LVL_CNT
1
ABC
XYZ
07
1
0
1
ABC
XYZ
07
2
2
1
1
ABC
XYZ
07
2
4
4
2
1
ABC
XYZ
07
3
3
1
2
DEF
BBB
01
2
0
2
DEF
BBB
01
4
4
1
3
GHI
ZZZ
02
3
0
4
JFK
XXX
10
4
0
没有人向3或4汇报,所以他们只有一级。
此处,R_ID -> 采用层次结构的最后一个 CODE_ID。
LVL_CNT -> 获取 R_ID 相对于主 CODE_ID.
的最大分层计数
所有主代码的reportees,需要用level count来追溯。最大值可以是 12.
我们已经使用多个联合进行开发,但是由于我们每月要处理大量数据,所以这个过程非常缓慢。
示例代码:
SELECT
T01.CODE_ID
, T01.NAME
, T01.HEAD_NAME
, T01.HEAD_RANK
, NULL as L1_CODE
, NULL as L2_CODE
, NULL as L3_CODE
, T01.CODE_ID
, 0
FROM test_01 T01
UNION
SELECT
T01.REPORT_TO_CODE
, TT.NAME
, TT.HEAD_NAME
, TT.HEAD_RANK
, T01.CODE_ID as L1_CODE
, NULL as L2_CODE
, NULL as L3_CODE
, T01.CODE_ID
, 1
FROM test_01 T01
, test_01 TT
WHERE T01.REPORT_TO_CODE = TT.CODE_ID
AND T01.REPORT_TO_CODE <> T01.CODE_ID
UNION
SELECT
T01.REPORT_TO_CODE
, TT.NAME
, TT.HEAD_NAME
, TT.HEAD_RANK
, T01.CODE_ID as L1_CODE
, T02.CODE_ID as L2_CODE
, NULL as L3_CODE
, T02.CODE_ID
, 2
FROM test_01 T01
, test_01 T02
, test_01 TT
WHERE T01.REPORT_TO_CODE = TT.CODE_ID
AND T02.REPORT_TO_CODE = T01.CODE_ID
AND T01.REPORT_TO_CODE <> T01.CODE_ID
AND T02.REPORT_TO_CODE <> T02.CODE_ID
UNION
SELECT
T01.REPORT_TO_CODE
, TT.NAME
, TT.HEAD_NAME
, TT.HEAD_RANK
, T01.CODE_ID as L1_CODE
, T02.CODE_ID as L2_CODE
, T03.CODE_ID as L3_CODE
, T03.CODE_ID
, 3
FROM test_01 T01
, test_01 T02
, test_01 T03
, test_01 TT
WHERE T01.REPORT_TO_CODE = TT.CODE_ID
AND T02.REPORT_TO_CODE = T01.CODE_ID
AND T03.REPORT_TO_CODE = T02.CODE_ID
AND T01.REPORT_TO_CODE <> T01.CODE_ID
AND T02.REPORT_TO_CODE <> T02.CODE_ID
AND T03.REPORT_TO_CODE <> T03.CODE_ID
我想知道是否有任何更简单的方法可以使用 connect by prior 或 join with less union 来获得相同的结果,我将非常高兴和感激。
您可以使用递归子查询分解子句:
WITH rsqfc ( code_id, name, head_name, head_rank, report_to_code, l1_code, l2_code, l3_code, r_id, lvl_cnt ) AS (
SELECT code_id,
name,
head_name,
head_rank,
report_to_code,
CAST( NULL AS NUMBER ),
CAST( NULL AS NUMBER ),
CAST( NULL AS NUMBER ),
code_id,
0
FROM table_name
UNION ALL
SELECT r.code_id,
r.name,
r.head_name,
r.head_rank,
r.report_to_code,
CASE lvl_cnt
WHEN 0
THEN t.code_id
ELSE r.l1_code
END,
CASE lvl_cnt
WHEN 1
THEN t.code_id
ELSE r.l2_code
END,
CASE lvl_cnt
WHEN 2
THEN t.code_id
ELSE r.l3_code
END,
t.code_id,
lvl_cnt + 1
FROM table_name t
INNER JOIN rsqfc r
ON ( r.r_id = t.report_to_code )
)
SELECT *
FROM rsqfc
ORDER BY code_id, l1_code NULLS FIRST, l2_code NULLS FIRST, l3_code NULLS FIRST;
其中,对于示例数据:
CREATE TABLE table_name ( Code_ID, Name, Head_Name, Head_Rank, Report_To_Code ) AS
SELECT 1, 'ABC', 'XYZ', '07', NULL FROM DUAL UNION ALL
SELECT 2, 'DEF', 'BBB', '01', 1 FROM DUAL UNION ALL
SELECT 3, 'GHI', 'ZZZ', '02', 1 FROM DUAL UNION ALL
SELECT 4, 'JFK', 'XXX', '10', 2 FROM DUAL;
输出:
CODE_ID | NAME | HEAD_NAME | HEAD_RANK | REPORT_TO_CODE | L1_CODE | L2_CODE | L3_CODE | R_ID | LVL_CNT
------: | :--- | :-------- | :-------- | -------------: | ------: | ------: | ------: | ---: | ------:
1 | ABC | XYZ | 07 | null | null | null | null | 1 | 0
1 | ABC | XYZ | 07 | null | 2 | null | null | 2 | 1
1 | ABC | XYZ | 07 | null | 2 | 4 | null | 4 | 2
1 | ABC | XYZ | 07 | null | 3 | null | null | 3 | 1
2 | DEF | BBB | 01 | 1 | null | null | null | 2 | 0
2 | DEF | BBB | 01 | 1 | 4 | null | null | 4 | 1
3 | GHI | ZZZ | 02 | 1 | null | null | null | 3 | 0
4 | JFK | XXX | 10 | 2 | null | null | null | 4 | 0
db<>fiddle here
使用以下信息考虑 table。 Table数据:
Code_ID | Name | Head_Name | Head_Rank | Report_To_Code |
---|---|---|---|---|
1 | ABC | XYZ | 07 | |
2 | DEF | BBB | 01 | 1 |
3 | GHI | ZZZ | 02 | 1 |
4 | JFK | XXX | 10 | 2 |
样本:
CREATE TABLE TEST_01
( CODE_ID NUMBER(5),
NAME VARCHAR2(3),
HEAD_NAME VARCHAR2(3),
HEAD_RANK VARCHAR2(2),
REPORT_TO_CODE NUMBER(5)
);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (1, 'ABC', 'XYZ', '07', null);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (2, 'DEF', 'BBB', '01', 1);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (3, 'GHI', 'ZZZ', '02', 1);
insert into test_01 (CODE_ID, NAME, HEAD_NAME, HEAD_RANK, REPORT_TO_CODE)
values (4, 'JFK', 'XXX', '10', 2);
commit;
我们的最终输出需要存储为层次结构,如下所示。
Code_ID | Name | Head_Name | Head_Rank | L1_Code | L2_Code | L3_Code | .. | L12_Code | R_ID | LVL_CNT |
---|---|---|---|---|---|---|---|---|---|---|
1 | ABC | XYZ | 07 | 1 | 0 | |||||
1 | ABC | XYZ | 07 | 2 | 2 | 1 | ||||
1 | ABC | XYZ | 07 | 2 | 4 | 4 | 2 | |||
1 | ABC | XYZ | 07 | 3 | 3 | 1 | ||||
2 | DEF | BBB | 01 | 2 | 0 | |||||
2 | DEF | BBB | 01 | 4 | 4 | 1 | ||||
3 | GHI | ZZZ | 02 | 3 | 0 | |||||
4 | JFK | XXX | 10 | 4 | 0 |
没有人向3或4汇报,所以他们只有一级。 此处,R_ID -> 采用层次结构的最后一个 CODE_ID。 LVL_CNT -> 获取 R_ID 相对于主 CODE_ID.
的最大分层计数所有主代码的reportees,需要用level count来追溯。最大值可以是 12.
我们已经使用多个联合进行开发,但是由于我们每月要处理大量数据,所以这个过程非常缓慢。 示例代码:
SELECT
T01.CODE_ID
, T01.NAME
, T01.HEAD_NAME
, T01.HEAD_RANK
, NULL as L1_CODE
, NULL as L2_CODE
, NULL as L3_CODE
, T01.CODE_ID
, 0
FROM test_01 T01
UNION
SELECT
T01.REPORT_TO_CODE
, TT.NAME
, TT.HEAD_NAME
, TT.HEAD_RANK
, T01.CODE_ID as L1_CODE
, NULL as L2_CODE
, NULL as L3_CODE
, T01.CODE_ID
, 1
FROM test_01 T01
, test_01 TT
WHERE T01.REPORT_TO_CODE = TT.CODE_ID
AND T01.REPORT_TO_CODE <> T01.CODE_ID
UNION
SELECT
T01.REPORT_TO_CODE
, TT.NAME
, TT.HEAD_NAME
, TT.HEAD_RANK
, T01.CODE_ID as L1_CODE
, T02.CODE_ID as L2_CODE
, NULL as L3_CODE
, T02.CODE_ID
, 2
FROM test_01 T01
, test_01 T02
, test_01 TT
WHERE T01.REPORT_TO_CODE = TT.CODE_ID
AND T02.REPORT_TO_CODE = T01.CODE_ID
AND T01.REPORT_TO_CODE <> T01.CODE_ID
AND T02.REPORT_TO_CODE <> T02.CODE_ID
UNION
SELECT
T01.REPORT_TO_CODE
, TT.NAME
, TT.HEAD_NAME
, TT.HEAD_RANK
, T01.CODE_ID as L1_CODE
, T02.CODE_ID as L2_CODE
, T03.CODE_ID as L3_CODE
, T03.CODE_ID
, 3
FROM test_01 T01
, test_01 T02
, test_01 T03
, test_01 TT
WHERE T01.REPORT_TO_CODE = TT.CODE_ID
AND T02.REPORT_TO_CODE = T01.CODE_ID
AND T03.REPORT_TO_CODE = T02.CODE_ID
AND T01.REPORT_TO_CODE <> T01.CODE_ID
AND T02.REPORT_TO_CODE <> T02.CODE_ID
AND T03.REPORT_TO_CODE <> T03.CODE_ID
我想知道是否有任何更简单的方法可以使用 connect by prior 或 join with less union 来获得相同的结果,我将非常高兴和感激。
您可以使用递归子查询分解子句:
WITH rsqfc ( code_id, name, head_name, head_rank, report_to_code, l1_code, l2_code, l3_code, r_id, lvl_cnt ) AS (
SELECT code_id,
name,
head_name,
head_rank,
report_to_code,
CAST( NULL AS NUMBER ),
CAST( NULL AS NUMBER ),
CAST( NULL AS NUMBER ),
code_id,
0
FROM table_name
UNION ALL
SELECT r.code_id,
r.name,
r.head_name,
r.head_rank,
r.report_to_code,
CASE lvl_cnt
WHEN 0
THEN t.code_id
ELSE r.l1_code
END,
CASE lvl_cnt
WHEN 1
THEN t.code_id
ELSE r.l2_code
END,
CASE lvl_cnt
WHEN 2
THEN t.code_id
ELSE r.l3_code
END,
t.code_id,
lvl_cnt + 1
FROM table_name t
INNER JOIN rsqfc r
ON ( r.r_id = t.report_to_code )
)
SELECT *
FROM rsqfc
ORDER BY code_id, l1_code NULLS FIRST, l2_code NULLS FIRST, l3_code NULLS FIRST;
其中,对于示例数据:
CREATE TABLE table_name ( Code_ID, Name, Head_Name, Head_Rank, Report_To_Code ) AS
SELECT 1, 'ABC', 'XYZ', '07', NULL FROM DUAL UNION ALL
SELECT 2, 'DEF', 'BBB', '01', 1 FROM DUAL UNION ALL
SELECT 3, 'GHI', 'ZZZ', '02', 1 FROM DUAL UNION ALL
SELECT 4, 'JFK', 'XXX', '10', 2 FROM DUAL;
输出:
CODE_ID | NAME | HEAD_NAME | HEAD_RANK | REPORT_TO_CODE | L1_CODE | L2_CODE | L3_CODE | R_ID | LVL_CNT ------: | :--- | :-------- | :-------- | -------------: | ------: | ------: | ------: | ---: | ------: 1 | ABC | XYZ | 07 | null | null | null | null | 1 | 0 1 | ABC | XYZ | 07 | null | 2 | null | null | 2 | 1 1 | ABC | XYZ | 07 | null | 2 | 4 | null | 4 | 2 1 | ABC | XYZ | 07 | null | 3 | null | null | 3 | 1 2 | DEF | BBB | 01 | 1 | null | null | null | 2 | 0 2 | DEF | BBB | 01 | 1 | 4 | null | null | 4 | 1 3 | GHI | ZZZ | 02 | 1 | null | null | null | 3 | 0 4 | JFK | XXX | 10 | 2 | null | null | null | 4 | 0
db<>fiddle here