【问题标题】:Oracle SQL connect_by_root and subqueriesOracle SQL connect_by_root 和子查询
【发布时间】:2016-11-04 14:25:26
【问题描述】:

在过去的几天里,我一直在为我的作业寻找解决方案,但我似乎找不到答案。

简介:假设我们有一个名为 People 的表。每个人都有三个字段:namebosspositionname 是主键,position 只是一个字符串,boss 作为外键指向另一个人的name。它创建了一个普通的树,如 Person1 -> Person2 -> Person3 -> Person4 ,其中 Person4 将是最高老板, Person1 是根。为简单起见,我们假设没有人拥有超过 3 个老板,而 Person4 是头老板(唯一一个 boss 等于 null 的人。)

示例路径:

Person7 -> Person4

Person6 -> Person8 -> Person4

Person2 -> Person8 -> Person4

所以我的任务是:创建一个查询,以分层顺序显示每个老板的名称,其中position 等于“工人”或“经理”仅使用树操作(连接方式, connect_by_root 等)和子查询

输出表必须包含 5 列:

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

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

这是我目前的查询:

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 之类的复杂功能来使其工作?

【问题讨论】:

  • 只有maxmin 函数。并将... name else ' ' end ... 替换为... name else null end ...
  • 这些函数变成子查询了吗?

标签: sql oracle


【解决方案1】:

它应该像这样工作(我没有要检查的源数据):

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;

【讨论】:

  • 您缺少 group by 子句,而且这是一种旋转形式,我认为 OP 并不是特别想要这样做。
  • @Boneist 哦,谢谢!我试图在原始查询中进行最小的更正。
  • 工作就像一个魅力,现在我需要考虑一个解决方案。 max 函数在这里做什么?能够group by root_name 只是一种解决方法吗?它是否会执行类似“从 null 和名称中选择最大值”这样的操作,因此总是返回名称?
  • @Karatte “它会做类似“从 null 和名称中选择最大值”这样的事情,因此总是返回名称吗?” - 没错。
【解决方案2】:

完全可以通过使用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 生成到目前为止它所获得的所有值的路径。因此,在叶子行上,它将包含所有必要的值。然后我们可以解析这个生成的字符串以获取不包含路径分隔符的第一个、第二个等部分。

【讨论】:

    猜你喜欢
    • 2016-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    相关资源
    最近更新 更多