2016-11-04 127 views
0

在过去的几天里,我正在为我的任务开发解决方案,而我似乎无法找到答案。Oracle SQL connect_by_root和子查询

介绍:假设我们有一张叫的人。每个人有三个字段:name,bosspositionname是主键,position只是一个字符串,boss作为外键指向不同的人的name。它创建了一个像Person1 - > Person2 - > Person3 - > Person4这样的普通树,其中Person4是最高级的boss,Person1是root。为简单起见让我们假设没有人具有多于3个老板,Person4是头老板

实施例的路径(的唯一的人与boss等于null):

Person7 - > Person4

Person6 - > Person8 - > Person4

PERSON2 - > Person8 - > Person4

所以我的任务说:创建一个显示在分层ORD查询呃,每个老板的每个人的名字position等于“工人”,或只使用树操作(通过连接,CONNECT_BY_ROOT等)“经理” 和子

输出表必须由5列:

Name | Position | Boss 1 | Boss 2 | Boss 3 

如果任何boss列将为空,那么我应该插入一些空格。

这是我的查询到目前为止:

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     

请问有什么解决,而无需使用复杂的功能,如透视,使其工作

+0

只是'max'或'min'功能。并且用'... name else'结尾...'替换'... name else''end ...' – Dmitry

+0

这些函数放入子查询中? – Karatte

回答

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; 
+0

你错过了group by子句,再加上这是一种pivoting的形式,我不认为OP是特别想做的。 – Boneist

+1

@骨头人哦,谢谢!我试图在原始查询中做出最小的更正。 – Dmitry

+0

工程就像一个魅力,现在我需要考虑解决方案。这里有'max'函数可以做什么?这只是一种解决方法,能够'root by'root_name?它是否像“从null和name中选择max”这样的东西总是返回名称? – Karatte

0

有可能做到这一点而不需要任何枢轴等,仅通过使用CONNECT_BY_ROOTCONNECT_BY_ISLEAFSYS_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生成到目前为止所有值的路径。所以,在叶子行上,它将包含所有必要的值。然后我们可以解析这个生成的字符串以获取不包含路径分隔符的第一个,第二个等部分。