如何在SQL中找到谁是部门老板?

时间:2021-02-18 15:13:17

标签: sql sql-server select

我想创建一个存储过程来查找谁是部门的老板。 所有经理都包含在带有前缀“_Head”的部门中。

我有两个采用这种结构的表并使用 SQL Server 2014。

SQL Server 表:

PersonID | DepartID | PersonName
---------+----------+----------------------
  138    |   110    | RootManager
  248    |    90    | Manager1
   63    |    73    | User1
  218    |    73    | User2
  216    |    96    | Manager2
  161    |    93    | Manager3
  222    |    93    | Manager4
  118    |    23    | User3
  160    |    23    | User4
  259    |    86    | User5

更新:

PersonID - PK
Person.DepartID = Depart.DepartID - FK

出发

DepartID | ParentID | DepartName
---------+----------+--------------
   77    | NULL     | NULL
  100    | 77       | _Head of Root
   73    | 77       | Dep1
   90    | 73       | _Head of Dep1
   98    | 77       | Dep2
   96    | 98       | _Head of Dep2
   23    | 98       | SubDep2
   93    | 23       | _Head of SubDep2
   86    | 77       | Dep3

更新:

DepartID - PK
Depart.ParentID = Depart.DepartID - FK
Depart.DepartID = Person.DepartID - FK

“_Head of something”可以包含随机字符而没有以前的部门名称

例如:“_SubDep2 主管”可以是“_部门 XXX/YYY”

树视图:

ROOT [DepartID = 77, ParentID = NULL]
 |- _Head of Root [DepartID = 110, ParentID = 77]
    |- RootManager [PersonID = 138, DepartId = 110]
 |- Dep1 [DepartID = 73, ParentID = 77]
    |- _Head of Dep1 [DepartID = 90, ParentID = 73]
        |- Manager1 [PersonID = 248, DepartId = 90]
    |- User1 [PersonID = 63, DepartId = 73]
    |- User2 [PersonID = 218, DepartId = 73]
 |- Dep2 [DepartID = 98, ParentID = 77]
    |- _Head of Dep2 [DepartID = 96, ParentID = 98]
        |- Manager2 [PersonID = 216, DepartId = 96]
    |- SubDep2 [DepartID = 23, ParentID = 98]
        |- _Head of SubDep2 [DepartID = 93, ParentID = 23]
            |- Manager3 [PersonID = 161, DepartId = 93]
            |- Manager4 [PersonID = 222, DepartId = 93]
        |- User3 [PersonID = 118, DepartId = 23]
        |- User4 [PersonID = 160, DepartId = 23]
 |- Dep3 [DepartID = 86, ParentID = 77]
    |- User5 [PersonID = 259, DepartId = 86]

想要的结果:

  • User1,User2 = Manager1 [PersonID = 248]
  • User3,User4 = Manager3, Manager4 [PersonID = 161, 222]
  • User5 = RootManager [PersonID = 138]
  • Manager1、Manager2 = RootManager [PersonID = 138]
  • Manager3 = Manager2 [PersonID = 216]

我试过了:

带有 INNER JOIN 的 SQL 查询,但它仅适用于一级。

SQL 和 CTE,我合并了 Department 表,但我不能和 Person 表合并。

有人可以帮我解决这个话题吗?

非常感谢您的支持

1 个答案:

答案 0 :(得分:0)

我建议您重新审视和规范您的数据库设计。在停机期间,如果这是一个正在使用的数据库,使用视图作为抽象来查询数据可以提供无缝更改,直到您重新访问模型。模型更新完成后,只需更新视图,所有使用该视图的应用程序都不需要更改。

对于您当前的设计,以下查询将有助于根据识别部门负责人的模式确定经理:

方法 1 [推荐]

<块引用>
CREATE TABLE Person (
  PersonID INTEGER,
  DepartID INTEGER,
  PersonName VARCHAR(11)
);

INSERT INTO Person
  (PersonID, DepartID, PersonName)
VALUES
  ('138', '110', 'RootManager'),
  ('248', '90', 'Manager1'),
  ('63', '73', 'User1'),
  ('218', '73', 'User2'),
  ('216', '96', 'Manager2'),
  ('161', '93', 'Manager3'),
  ('222', '93', 'Manager4'),
  ('118', '23', 'User3'),
  ('160', '23', 'User4'),
  ('259', '86', 'User5');
  
  

CREATE TABLE Depart (
  DepartID INTEGER,
  ParentID VARCHAR(4),
  DepartName VARCHAR(16)
);

INSERT INTO Depart
  (DepartID, ParentID, DepartName)
VALUES
  ('77', NULL, NULL),
  ('100', '77', '_Head of Root'),
  ('73', '77', 'Dep1'),
  ('90', '73', '_Head of Dep1'),
  ('98', '77', 'Dep2'),
  ('96', '98', '_Head of Dep2'),
  ('23', '98', 'SubDep2'),
  ('93', '23', '_Head of SubDep2'),
  ('86', '77', 'Dep3');
GO

19 行受影响

<块引用> <块引用>
DROP PROCEDURE IF EXISTS sp_GetManager
GO
<块引用>
CREATE PROC sp_GetManager (@Name VARCHAR(20))
AS
BEGIN
DECLARE @ManagerResult TABLE(PersonId INTEGER, PersonName VARCHAR(20));

INSERT INTO @ManagerResult
SELECT 
    p.PersonId, 
    p.PersonName
FROM 
Person p 
WHERE p.DepartId in ( 
    SELECT d.DepartId from Depart d 
    WHERE d.DepartName IN (
       SELECT  CONCAT('_Head of ',d1.DepartName) FROM
       Depart d1
       INNER JOIN Person p1 on p1.DepartId = d1.DepartId
       WHERE p1.PersonName = @Name
    )
    
    
 )


IF EXISTS(
   Select PersonId from @ManagerResult
)
SELECT * from @ManagerResult
ELSE
SELECT p.PersonId, p.PersonName FROM Person p where p.PersonName='RootManager'
END
GO
<块引用>
exec sp_GetManager 'User1'
GO
PersonId | PersonName
-------: | :---------
     248 | Manager1  
<块引用>
exec sp_GetManager 'User2'
GO
PersonId | PersonName
-------: | :---------
     248 | Manager1  
<块引用>
exec sp_GetManager 'Manager3'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager
<块引用>
exec sp_GetManager 'User5'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager
<块引用>
exec sp_GetManager 'User3'
GO
PersonId | PersonName
-------: | :---------
     161 | Manager3  
     222 | Manager4  
<块引用>
exec sp_GetManager 'User4'
GO
PersonId | PersonName
-------: | :---------
     161 | Manager3  
     222 | Manager4  
<块引用>
exec sp_GetManager 'Manager1'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager
<块引用>
exec sp_GetManager 'Manager2'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager

db<>fiddle here

方法 2

这似乎更乏味,但如果您打算使用初始递归方法遍历树,那么此尝试可能会指导您根据外键遍历层次结构

<块引用>
select @@version;
GO
| (No column name)                                                                                                                                                                                                                                                           |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Microsoft SQL Server 2019 (RTM-CU6) (KB4563110) - 15.0.4053.23 (X64) <br>        Jul 25 2020 11:26:55 <br>        Copyright (C) 2019 Microsoft Corporation<br>        Express Edition (64-bit) on Windows Server 2019 Standard 10.0 <X64> (Build 17763: ) (Hypervisor)<br> |
<块引用>
CREATE TABLE Person (
  PersonID INTEGER,
  DepartID INTEGER,
  PersonName VARCHAR(11)
);

INSERT INTO Person
  (PersonID, DepartID, PersonName)
VALUES
  ('138', '110', 'RootManager'),
  ('248', '90', 'Manager1'),
  ('63', '73', 'User1'),
  ('218', '73', 'User2'),
  ('216', '96', 'Manager2'),
  ('161', '93', 'Manager3'),
  ('222', '93', 'Manager4'),
  ('118', '23', 'User3'),
  ('160', '23', 'User4'),
  ('259', '86', 'User5');
  
  

CREATE TABLE Depart (
  DepartID INTEGER,
  ParentID VARCHAR(4),
  DepartName VARCHAR(16)
);

INSERT INTO Depart
  (DepartID, ParentID, DepartName)
VALUES
  ('77', NULL, NULL),
  ('100', '77', '_Head of Root'),
  ('73', '77', 'Dep1'),
  ('90', '73', '_Head of Dep1'),
  ('98', '77', 'Dep2'),
  ('96', '98', '_Head of Dep2'),
  ('23', '98', 'SubDep2'),
  ('93', '23', '_Head of SubDep2'),
  ('86', '77', 'Dep3');
GO

19 行受影响

<块引用> <块引用>
DROP PROCEDURE IF EXISTS sp_GetManager
GO
<块引用>
CREATE PROC sp_GetManager(@Name VARCHAR(20))
AS
BEGIN

DECLARE @ManagerResult TABLE(PersonId INTEGER, PersonName VARCHAR(20), ManagementLevel INTEGER);
DECLARE @PersonId INTEGER, @PersonDepartmentId INTEGER;

SELECT @PersonId = p.PersonId , @PersonDepartmentId = p.DepartId  FROM Person p 

  WHERE p.PersonName  = @Name;

   
    WITH 
head_of_department AS  (
   SELECT d.DepartId, d.ParentId, d.DepartName, 0 as Level
   FROM Depart d WHERE d.DepartId = @PersonDepartmentId
   UNION ALL
   SELECT d.DepartId, d.ParentId, d.DepartName, Level+1 as Level
   FROM Depart d 
   INNER JOIN head_of_department hod on d.DepartId = hod.ParentId
   WHERE d.ParentId is NOT NULL  
),
including_sub_department AS (
   SELECT DISTINCT * FROM (
       SELECT * from head_of_department
       UNION ALL
       SELECT d.DepartId, d.ParentId, d.DepartName, hod.Level
       FROM Depart d 
       INNER JOIN head_of_department hod on hod.DepartId = d.ParentId
       
   ) t WHERE t.DepartName LIKE '_Head of%'
)
INSERT INTO @ManagerResult
select   p.PersonId, p.PersonName ,d.Level
from including_sub_department d
INNER JOIN Person p on d.DepartId = p.DepartId
WHERE 
 d.DepartId <> @PersonDepartmentId;

DECLARE @MinManagementLevel INTEGER;

IF EXISTS(
   Select PersonId from @ManagerResult
)
BEGIN
SELECT @MinManagementLevel=MIN(ManagementLevel) FROM @ManagerResult;
SELECT PersonId, PersonName from @ManagerResult WHERE ManagementLevel = @MinManagementLevel;
END
ELSE
SELECT p.PersonId, p.PersonName FROM Person p where p.PersonName='RootManager'

    
END
GO
<块引用>
exec sp_GetManager 'User1'
GO
PersonId | PersonName
-------: | :---------
     248 | Manager1  
<块引用>
exec sp_GetManager 'User2'
GO
PersonId | PersonName
-------: | :---------
     248 | Manager1  
<块引用>
exec sp_GetManager 'Manager3'
GO
PersonId | PersonName
-------: | :---------
     216 | Manager2  
<块引用>
exec sp_GetManager 'User5'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager
<块引用>
exec sp_GetManager 'User3'
GO
PersonId | PersonName
-------: | :---------
     161 | Manager3  
     222 | Manager4  
<块引用>
exec sp_GetManager 'User4'
GO
PersonId | PersonName
-------: | :---------
     161 | Manager3  
     222 | Manager4  
<块引用>
exec sp_GetManager 'Manager1'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager
<块引用>
exec sp_GetManager 'Manager2'
GO
PersonId | PersonName 
-------: | :----------
     138 | RootManager

db<>fiddle here