过去几天我正在为我的任务制定解决方案,而我似乎无法找到答案。
简介:假设我们有一个名为人的表。每个人都有三个字段:name
,boss
和position
。 name
是主键,position
只是一个字符串,boss
作为外键指向不同的人name
。它创建了一个普通的树,如Person1 - > Person2 - > Person3 - > Person4,其中Person4是最高老板而Person1是根。为简单起见,我们假设没有人拥有超过3个老板,而Person4是首席老板(唯一一个boss
等于null
的人。)
示例路径:
Person7 - > Person4
Person6 - > Person8 - > Person4
Person2 - > Person8 - > Person4
所以我的作业说:创建一个查询,按照层级顺序显示每个人的名字,每个人position
等于“工人”或“经理”仅使用树操作(连接) by,connect_by_root等)和子查询
输出表必须包含5列:
Name | Position | Boss 1 | Boss 2 | Boss 3
如果任何boss列为null,那么我应该插入一些空格。
这是我目前的查询:
select
case
when l = 1 then name else ' ' end as "Name",
position,
case
when l = 2 then name else ' ' end as "Boss 1",
case
when l = 3 then name else ' ' end as "Boss 2",
case
when l = 4 then name else ' ' end as "Boss 3"
from (
select
connect_by_root position as position,
level as l,
name
from
People
connect by prior
boss = name
start with
position = 'Worker'
or position = 'Manager'
);
它有点诀窍,但树的每个级别都是一个新行,这是我必须避免的。我知道为什么这个查询产生这样的结果,但我不知道如何让它遍历树而不在每一步创建新行。
我的结果:
Name |Position|Boss 1|Boss 2|Boss 3
JOHN WORKER
WORKER HENRY
WORKER PETER
TERRY WORKER
WORKER PETER
ALICE WORKER
WORKER PETER
BILL MANAGER
MANAGER JAMES
MANAGER PETER
这是我想要达到的结果:
Name |Position|Boss 1|Boss 2|Boss 3
JOHN WORKER HENRY PETER
TERRY WORKER PETER
ALICE WORKER PETER
BILL MANAGER JAMES PETER
是否有任何解决方案没有使用像Pivot这样的复杂功能来使其工作?
答案 0 :(得分:0)
可以在不需要任何枢轴等的情况下完成此操作,纯粹使用CONNECT_BY_ROOT
,CONNECT_BY_ISLEAF
和SYS_CONNECT_BY_PATH
,以及明智地使用REGEXP_SUBSTR
:
WITH people AS (SELECT 'JOHN' name, 'WORKER' position, 'HENRY' boss FROM dual UNION ALL
SELECT 'HENRY' name, 'CFO' position, 'PETER' boss FROM dual UNION ALL
SELECT 'TERRY' name, 'WORKER' position, 'PETER' boss FROM dual UNION ALL
SELECT 'ALICE' name, 'WORKER' position, 'PETER' boss FROM dual UNION ALL
SELECT 'JAMES' name, 'CIO' position, 'PETER' boss FROM dual UNION ALL
SELECT 'FRED' name, 'MANAGER' position, NULL boss FROM dual UNION ALL
SELECT 'BILL' name, 'MANAGER' position, 'JAMES' boss FROM dual UNION ALL
SELECT 'PETER' name, 'CEO' position, 'FRED' boss FROM dual)
-- end of mimicking your people table with some sample data in it
-- you wouldn't need the above, just use the query below:
SELECT connect_by_root name AS name,
connect_by_root position AS position,
regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 1) boss1,
regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 2) boss2,
regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 3) boss3
FROM people
WHERE connect_by_isleaf = 1
CONNECT BY PRIOR boss = name
START WITH position IN ('WORKER', 'MANAGER');
NAME POSITION BOSS1 BOSS2 BOSS3
----- -------- ----- ----- -----
ALICE WORKER PETER FRED
BILL MANAGER JAMES PETER FRED
FRED MANAGER
JOHN WORKER HENRY PETER FRED
TERRY WORKER PETER FRED
CONNECT_BY_ISLEAF
确定该行是否为叶子行(1
)或不是(0
)。因此,就像您可以使用CONNECT_BY_ROOT
标识根值一样,您可以使用CONNECT_BY_ISLEAF
告诉哪一行是结束行。
SYS_CONNECT_BY_PATH
生成到目前为止所有值的路径。因此,在叶子行上,它将包含所有必需的值。然后我们可以解析这个生成的字符串,以获得不包含路径分隔符的第一个,第二个等部分。
答案 1 :(得分:0)
它应该像这样工作(我没有要检查的源数据):
select
root_name as "Name",
max(position),
max(case when l = 2 then name else null end) as "Boss 1",
max(case when l = 3 then name else null end) as "Boss 2",
max(case when l = 4 then name else null end) as "Boss 3"
from (
select
connect_by_root position as position,
connect_by_root name as root_name,
level as l,
name
from
People
connect by prior
boss = name
start with
position = 'Worker'
or position = 'Manager'
)
group by root_name;