【问题标题】:Select Query with Recursion选择递归查询
【发布时间】:2020-02-10 23:11:39
【问题描述】:

我有两个包含员工姓名 (TableB) 和员工层次结构 (TableA) 的表(TableA 中的 manager_id 可以是同一张表中的员工 ID)。

TableA
UniqueId    Employee_ID Manager_ID
1            101         102
2            102         103
3            103         104
4            105         106
5            106         null


TableB
Employee_ID  Employee_Name
101          First
102          Second
103          Third
104          Fourth
105          Fifth
106          Sixth

我需要如下输出:

Employee_ID Employee_Name   Transferred
101         First           True
102         Second          True
103         Third           True
105         Fifth           False
106         Sixth           False

每个员工的已调动列计算为 =

isTransferred(Employee_ID)
{
    If(Manager_ID  is null) return false;
    If(Manager_ID is present as employee_id in table A)
    {
       return isTransferred(manager_ID)
    }
    else
    {
       return true;
    }
}

有没有办法在一个 select 语句中得到结果?

【问题讨论】:

  • 为什么员工 104 不在结果集中?
  • @GMB Row with 104 as employee_id 在 TableA 中不存在。输出应该只包含 TableA 的employee_ids。
  • 第六个没有被转移,因为他们没有老板第五个没有转移,因为他们的老板(106)在表B中并且没有转移第三个是......哦,不,我们需要检查boss(104) 104,Fourth,因为boss不在TableB所以转了Third,因为boss(104)在TableB但是转了。好问题。我看到发布了一个答案,但我必须自己想出一个答案。
  • 我一直在玩这个来回,我真的认为你不会在 vanilla SQL 中发生这种情况,因为员工/经理树可以是任意深度的。我很想看看是否有人想出了一个可行的方法,但我现在看不到它正在发生。我稍后会玩它,看看我是否能找到解决方案或证明它无法完成。

标签: sql oracle recursive-query


【解决方案1】:

您可以使用递归 CTE,然后获取每个员工的最后一级“递归”。一旦你有了它,你只需检查最后一层的manager_id,看看它是否被转移了。

例如:

with
tablea as (
  select 1 as uniqueId, 101 as employee_id, 102 as manager_id from dual union all
  select 2  as uniqueId, 102 as employee_id, 103 as manager_id from dual union all
  select 3  as uniqueId, 103 as employee_id, 104 as manager_id from dual union all
  select 4  as uniqueId, 105 as employee_id, 106 as manager_id from dual union all
  select 5  as uniqueId ,106 as employee_id, null from dual 
),
tableb as (
  select 101 as employee_id, 'first' as employee_name from dual union all
  select 102 as employee_id, 'second' as employee_name from dual union all
  select 103 as employee_id, 'third' as employee_name from dual union all
  select 104 as employee_id, 'fourth' as employee_name from dual union all
  select 105 as employee_id, 'fifth' as employee_name from dual union all
  select 106 as employee_id, 'sixth' as employee_name from dual 
),
n (employee_id, employee_name, lvl, manager_id) as (
  select b.employee_id, b.employee_name, 1, a.manager_id
  from tablea a
  join tableb b on a.employee_id = b.employee_id
union all
  select
    n.employee_id, n.employee_name, lvl + 1, a.manager_id
  from n
  join tablea a on a.employee_id = n.manager_id
),
m (employee_id, max_lvl) as (
  select employee_id, max(lvl) from n group by employee_id
)
select n.employee_id, n.employee_name, 
  case when n.manager_id is not null then 'True' else 'False' end as transferred
from n
join m on n.employee_id = m.employee_id and n.lvl = m.max_lvl
order by n.employee_id

结果:

EMPLOYEE_ID  EMPLOYEE_NAME  TRANSFERRED
-----------  -------------  -----------
        101  first          True       
        102  second         True       
        103  third          True       
        105  fifth          False      
        106  sixth          False      

【讨论】:

  • 非常好。秘诀是递归表:n (employee_id, employee_name, lvl, manager_id) as ( select b.employee_id, b.employee_name, 1, a.manager_id from tablea a join tableb b on a.employee_id = b.employee_id union all select n.employee_id, n.employee_name, lvl + 1, a.manager_id from n join tablea a on a.employee_id = n.manager_id ),我们在创建 N 时从 N 中添加值,每次递增级别。感谢您提出一个简洁的问题和更简洁的解决方案。
【解决方案2】:

您可以通过一次遍历树来完成,如下所示:

  • 将管理链外部加入到员工中
  • 通过将 unique_id 映射到“不可能”值来查找没有经理的员工,例如-1
  • 使用此连接的结果沿着树向下走,从 manager 为 null 的行开始
  • 为第 2 步中的表达式获取根行的值;如果这是“不可能”的值,则 transfer => true
  • 过滤出不可能值的行

这给出了类似的东西:

with table_a ( UniqueId, Employee_ID, Manager_ID ) as (
  select 1, 101, 102 from dual union all 
  select 2, 102, 103 from dual union all 
  select 3, 103, 104 from dual union all
  select 4, 105, 106 from dual union all 
  select 5, 106, null from dual 
), Table_b ( Employee_ID, Employee_Name ) as (
  select 101, 'First' from dual union all
  select 102, 'Second' from dual union all
  select 103, 'Third' from dual union all
  select 104, 'Fourth' from dual union all
  select 105, 'Fifth' from dual union all
  select 106, 'Sixth' from dual
), rws as (
  select b.*, a.Manager_ID,
         nvl ( a.UniqueId, -1 ) transfer
  from   table_b b
  left join table_a a
  on   b.Employee_ID = a.Employee_ID
)
  select r.*, 
         case connect_by_root transfer
           when -1 then 'true'
           else 'false'
         end transferred
  from   rws r
  where  transfer > 0
  start  with manager_id is null
  connect by manager_id = prior employee_id;

EMPLOYEE_ID    EMPLOYEE_NAME    MANAGER_ID    TRANSFER    TRANSFERRED   
           103 Third                      104           3 true           
           102 Second                     103           2 true           
           101 First                      102           1 true           
           106 Sixth                   <null>           5 false          
           105 Fifth                      106           4 false 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多