MS SQL:在出现字符串之前,我可以重复加入吗?

时间:2019-06-04 14:30:22

标签: sql sql-server

查询一部分员工时,我正在尝试为其“汇总”到的SVP添加一个字段。

员工与SVP的距离可能为1至5或6度。麻烦的是,我们没有任何特定的层次结构指标可供参考。为此,我必须反复遍历该员工的经理,直到某个经理的经理的标题中标有“ SVP”为止。

如何编写查询来做到这一点?

从相反的方向,我找到了特定SVP的员工(示例中称为BM),说:“员工的经理是BM,或者员工的经理的经理是BM,或者员工的经理的经理是BM”,等等...

对于我的实例,我怀疑我会一次又一次地使用同一sys_user表,每次都跟随manager字段,直到到达标题中带有SVP的用户为止。

+--------+-------------------+-----------+--------+
| sys_id |      name         | title     | manager|
+--------+-------------------+-----------+--------+
| 555789 | Tina Belcher      | Contractor| 123456 | 
| 123456 | Bob Belcher       | Manager   | 654321 |
| 654321 | Calvin Fischoeder | SVP       | 997755 |
+--------+-------------------+-----------+--------+
SELECT su.Name
     , su.Title
     , dp.name
     , mg.name

FROM sys_user                   su
    LEFT JOIN cmn_department    dp
        ON dp.sys_id = su.department
    LEFT JOIN sys_user          mg
        ON mg.sys_id = su.manager

WHERE su.Title like ('%contractor%')

我感谢可以提供的任何帮助或提示。感谢您的光临,祝您度过愉快的一天。

2 个答案:

答案 0 :(得分:1)

您正在寻找递归CTE:

with cte as (
      select su.sys_id, su.name, su.title, su.manager, 1 as lev, 0 as hit_svp
      from sys_user su
      where su.Title like '%contractor%'
      union all
      select su.sys_id, su.name, su.title, su.manager, lev + 1,
             (case when su.title like '%SVP%' then 1 else 0 end) as hit_svp
      from sys_user su join
           cte
           on cte.manager = su.sys_id
      where cte.hit_svp = 0
     )
select . . .   -- whatever columns you want
from cte;      -- you may want additional joins here for other columns

答案 1 :(得分:1)

您的Sys_User表是一个邻接表,仅提供有关员工及其直接向谁报告的信息。邻接表是编码分层数据的一种方法。它们很不错,因为它们相对较快且紧凑,但是并不是编码分层关系的唯一方法。

要回答您提出的问题,您将受益于将数据重新编码到Closure表中,该表将每个员工映射到其所有直接和间接经理/报告人以及他们的离职程度,以及任何其他相关信息。但是,由于它表示多对多关系,因此您不希望为其加载太多额外的数据。幸运的是,由于使用了递归查询,您可以在运行中轻松创建一个。

要创建关闭表,请先使用0级关系填充该表,其中每个员工均被视为自己的经理/报告人。这样做的理由有点超出我的范围,但这与传递闭包的概念背后的数学有关(因此称为闭包表)。之后,您可以迭代(递归)添加每个其他报告程度。您可以从上至下或从下至上

这是自顶向下版本:

with closure(manager_id, report_id, degree, is_managing_SVP, is_reporting_svp) as (
  select sys_id
       , sys_id
       , 0
       , case when title like '%SVP%' then 1 else 0 end
       , case when title like '%SVP%' then 1 else 0 end
    from sys_user
  union all
  select cur.manager_id
       , nxt.sys_id
       , cur.degree+1
       , cur.is_managing_SVP
       , case when nxt.title like '%SVP%' then 1 else 0 end
    from closure cur
    join sys_user nxt
      on nxt.manager = cur.report_id
     and nxt.sys_id <> nxt.manager
)
select * from closure

这是自底向上版本:

with closure(manager_id, report_id, degree, is_managing_SVP, is_reporting_svp) as (
  select sys_id
       , sys_id
       , 0
       , case when title like '%SVP%' then 1 else 0 end
       , case when title like '%SVP%' then 1 else 0 end
    from sys_user
  union all
  select nxt.manager
       , cur.report_id
       , cur.degree+1
       , case when mgr.title like '%SVP%' then 1 else 0 end
       , cur.is_reporting_SVP
    from closure cur
    join sys_user nxt
      on nxt.sys_id = cur.manager_id
     and nxt.sys_id <> nxt.manager
    join sys_user mgr
      on mgr.sys_id = nxt.manager
)
select * from closure

如果要生成整个闭合表,使用哪个版本并不重要,但是,如果要优化查询并仅生成部分闭合表,则取决于是否要遍历在树上或下。

生成后,您可以使用封闭表来回答有关SVP的问题,例如每个承包商的SVP是谁:

select r.sys_id, r.name, r.title, c.degree
     , c.manager_id SVP_ID
     , m.name SVP_name
     , m.title SVP_title
  from sys_user r
  join closure c
    on c.report_id = r.sys_id
  join sys_user m
    on m.sys_id = c.manager_id
 where r.title like '%contractor%'
   and c.is_managing_svp = 1
sys_id | name         | title      | degree | SVP_ID | SVP_name          | SVP_title
-----: | :----------- | :--------- | -----: | -----: | :---------------- | :--------
555789 | Tina Belcher | Contractor |      2 | 654321 | Calvin Fischoeder | SVP      

或者向SVP提交的每份直接和间接报告都叫Calvin Fischoeder:

select m.sys_id manager_id
     , m.name
     , m.title
     , c.degree
     , r.sys_id report_id
     , r.name report_name
     , r.title report_title
  from sys_user m
  join closure c
    on c.manager_id = m.sys_id
  join sys_user r
    on r.sys_id = c.report_id
 where m.name = 'Calvin Fischoeder'
 order by degree, report_name
manager_id | name              | title | degree | report_id | report_name       | report_title
---------: | :---------------- | :---- | -----: | --------: | :---------------- | :-----------
    654321 | Calvin Fischoeder | SVP   |      0 |    654321 | Calvin Fischoeder | SVP         
    654321 | Calvin Fischoeder | SVP   |      1 |    123456 | Bob Belcher       | Manager     
    654321 | Calvin Fischoeder | SVP   |      2 |    555789 | Tina Belcher      | Contractor  

要查看所有正在使用的查询,请查看此 db<>fiddle