【问题标题】:How to find who is boss of department in SQL?如何在SQL中找到谁是部门的老板?
【发布时间】:2021-02-18 15:13:17
【问题描述】:

我想创建一个存储过程来查找谁是部门的老板。 所有经理都包含在前缀为“_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”可以包含随机字符而没有先前的离开名称

例如:“_Head of SubDep2”可以是“_Head of Department 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 表合并。

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

非常感谢您的支持

【问题讨论】:

  • 看来您应该修复您的设计。用户所在的部门不应该是'_Head of Dep1',而应该是'Dep1',另外一列应该表示负责人是谁。可能该列应该实际上在您的部门表中,作为返回员工表的外键。您目前的设计正在打破常规形式,因为数据点包含两条信息;该人属于哪个部门,以及他们是否是该部门的负责人。
  • 修复数据模型!如果您不打算存储实际数据模型,则将“路径”存储到子部门。例如'/''/dept1''/debt1/subdept1'
  • @JarolPet 我已经提供了一个解决方案和小提琴来测试。让我知道这是否有效

标签: sql sql-server select


【解决方案1】:

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

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

方法 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
个人身份 |人名 --------: | :--------- 248 |经理1
exec sp_GetManager 'User2'
GO
个人身份 |人名 --------: | :--------- 248 |经理1
exec sp_GetManager 'Manager3'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器
exec sp_GetManager 'User5'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器
exec sp_GetManager 'User3'
GO
个人身份 |人名 --------: | :--------- 161 |经理3 222 |经理4
exec sp_GetManager 'User4'
GO
个人身份 |人名 --------: | :--------- 161 |经理3 222 |经理4
exec sp_GetManager 'Manager1'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器
exec sp_GetManager 'Manager2'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器

db小提琴here

方法2

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

select @@version;
GO
| (无列名) | | :------------------------------------------------ -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------- | | Microsoft SQL Server 2019 (RTM-CU6) (KB4563110) - 15.0.4053.23 (X64)
2020 年 7 月 25 日 11:26:55
版权所有 (C) 2019 Microsoft Corporation
Express Edition(64 位)在 Windows Server 2019 Standard 10.0 (内部版本 17763:)(管理程序)
|
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
个人身份 |人名 --------: | :--------- 248 |经理1
exec sp_GetManager 'User2'
GO
个人身份 |人名 --------: | :--------- 248 |经理1
exec sp_GetManager 'Manager3'
GO
个人身份 |人名 --------: | :--------- 216 |经理2
exec sp_GetManager 'User5'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器
exec sp_GetManager 'User3'
GO
个人身份 |人名 --------: | :--------- 161 |经理3 222 |经理4
exec sp_GetManager 'User4'
GO
个人身份 |人名 --------: | :--------- 161 |经理3 222 |经理4
exec sp_GetManager 'Manager1'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器
exec sp_GetManager 'Manager2'
GO
个人身份 |人名 --------: | :---------- 138 |根管理器

db小提琴here

【讨论】:

  • 非常感谢,您的解决方案有效。但是我有这个更复杂的部门,前缀为“_Head of some”,“_Head of”后面的名称与DepartName不同,在大多数情况下,我的名称为“_Head of Dep1 XXX/YYY”或“_Head of Department 1”和此项目仅用于更好地查看(分类)。我检查了数据库和表包含 PK 和 FK。我更新了我的第一个帖子,你能再检查一下吗?
  • @JarolPet 我已经更新了基于“_Head of”前缀遍历键和过滤器的方法 2
  • 非常感谢您的更新,现在它可以正常工作了....非常棒的工作!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-05
  • 1970-01-01
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多