Sql 如何从自参考表中确定每个人的结构
我有以下表格:Sql 如何从自参考表中确定每个人的结构,sql,hierarchy,self-join,pervasive-sql,Sql,Hierarchy,Self Join,Pervasive Sql,我有以下表格: Employees ------------- ClockNo int CostCentre varchar Department int 及 部门可以将其他部门作为父部门,这意味着存在无限层次结构。所有部门都属于一个成本中心,因此总是有一个CostCentreCode。如果parent=0则为顶级部门 员工必须有一个成本中心值,但可以有一个部门为0,这意味着他们不在一个部门 我想要尝试并生成的是一个查询,它将提供多达四个层次结构级别。像这样: EmployeesL
Employees
-------------
ClockNo int
CostCentre varchar
Department int
及
部门可以将其他部门作为父部门,这意味着存在无限层次结构。所有部门都属于一个成本中心,因此总是有一个CostCentreCode
。如果parent=0
则为顶级部门
员工必须有一个成本中心
值,但可以有一个部门
为0,这意味着他们不在一个部门
我想要尝试并生成的是一个查询,它将提供多达四个层次结构级别。像这样:
EmployeesLevels
-----------------
ClockNo
CostCentre
DeptLevel1
DeptLevel2
DeptLevel3
DeptLevel4
ClockNo CostCentre Level1 Level2 Level3 Level4
1 AAA
2 AAA 1 3
3 BBB
4 BBB 4
5 CCC
6 AAA 1
7 AAA 1 3 5
8 AAA 1 3 5 12 *
我已经成功地获得了一些东西来单独显示部门结构,但我无法解决如何在不创建重复的员工行的情况下将其链接到员工:
SELECT d1.Description AS lev1, d2.Description as lev2, d3.Description as lev3, d4.Description as lev4
FROM departments AS d1
LEFT JOIN departments AS d2 ON d2.parent = d1.departmentcode
LEFT JOIN departments AS d3 ON d3.parent = d2.departmentcode
LEFT JOIN departments AS d4 ON d4.parent = d3.departmentcode
WHERE d1.parent=0;
SQL创建结构和一些示例数据:
CREATE TABLE Employees(
ClockNo integer NOT NULL PRIMARY KEY,
CostCentre varchar(20) NOT NULL,
Department integer NOT NULL);
CREATE TABLE Departments(
DepartmentCode integer NOT NULL PRIMARY KEY,
CostCentreCode varchar(20) NOT NULL,
Parent integer NOT NULL
);
CREATE INDEX idx0 ON Employees (ClockNo);
CREATE INDEX idx1 ON Employees (CostCentre, ClockNo);
CREATE INDEX idx2 ON Employees (CostCentre);
CREATE INDEX idx0 ON Departments (DepartmentCode);
CREATE INDEX idx1 ON Departments (CostCentreCode, DepartmentCode);
INSERT INTO Employees VALUES (1, 'AAA', 0);
INSERT INTO Employees VALUES (2, 'AAA', 3);
INSERT INTO Employees VALUES (3, 'BBB', 0);
INSERT INTO Employees VALUES (4, 'BBB', 4);
INSERT INTO Employees VALUES (5, 'CCC', 0);
INSERT INTO Employees VALUES (6, 'AAA', 1);
INSERT INTO Employees VALUES (7, 'AAA', 5);
INSERT INTO Employees VALUES (8, 'AAA', 15);
INSERT INTO Departments VALUES (1, 'AAA', 0);
INSERT INTO Departments VALUES (2, 'AAA', 1);
INSERT INTO Departments VALUES (3, 'AAA', 1);
INSERT INTO Departments VALUES (4, 'BBB', 0);
INSERT INTO Departments VALUES (5, 'AAA', 3);
INSERT INTO Departments VALUES (12, 'AAA', 5);
INSERT INTO Departments VALUES (15, 'AAA', 12);
这给出了以下结构(方括号中的员工时钟编号):
查询应返回以下内容:
EmployeesLevels
-----------------
ClockNo
CostCentre
DeptLevel1
DeptLevel2
DeptLevel3
DeptLevel4
ClockNo CostCentre Level1 Level2 Level3 Level4
1 AAA
2 AAA 1 3
3 BBB
4 BBB 4
5 CCC
6 AAA 1
7 AAA 1 3 5
8 AAA 1 3 5 12 *
*
对于员工8,他们处于5级。理想情况下,我希望将其所有级别显示到级别4,但我很高兴在本例中仅显示成本中心。这里的主要挑战是,员工的部门可能需要显示在列Level1、Level2、Level3或level4中,具体取决于该部门在层次结构中有多少较高级别
我建议首先在内部查询中查询每个员工的部门级别数,然后使用该信息将部门代码放在正确的列中:
SELECT ClockNo, CostCentre,
CASE LevelCount
WHEN 1 THEN Dep1
WHEN 2 THEN Dep2
WHEN 3 THEN Dep3
ELSE Dep4
END Level1,
CASE LevelCount
WHEN 2 THEN Dep1
WHEN 3 THEN Dep2
WHEN 4 THEN Dep3
END Level2,
CASE LevelCount
WHEN 3 THEN Dep1
WHEN 4 THEN Dep2
END Level3,
CASE LevelCount
WHEN 4 THEN Dep1
END Level4
FROM (SELECT e.ClockNo, e.CostCentre,
CASE WHEN d2.DepartmentCode IS NULL THEN 1
ELSE CASE WHEN d3.DepartmentCode IS NULL THEN 2
ELSE CASE WHEN d4.DepartmentCode IS NULL THEN 3
ELSE 4
END
END
END AS LevelCount,
d1.DepartmentCode Dep1, d2.DepartmentCode Dep2,
d3.DepartmentCode Dep3, d4.DepartmentCode Dep4
FROM Employees e
LEFT JOIN departments AS d1 ON d1.DepartmentCode = e.Department
LEFT JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
LEFT JOIN departments AS d3 ON d3.DepartmentCode = d2.Parent
LEFT JOIN departments AS d4 ON d4.DepartmentCode = d3.Parent) AS Base
ORDER BY ClockNo
或者,您可以根据现有级别(由0到4个部门组成的链)对5种可能的场景进行简单的合并:
我建议您将获取员工和获取其部门层次结构的查询分开
为了获得部门的层次结构,我建议您使用如下递归CTE:
with DepartmentList (DepartmentCode, CostCentreCode, Parent) AS
(
SELECT
parentDepartment.DepartmentCode,
parentDepartment.CostCentreCode,
parentDepartment.Parent
FROM Departments parentDepartment
WHERE DepartmentCode = @departmentCode
UNION ALL
SELECT
childDepartment.DepartmentCode
childDepartment.CostCentreCode,
childDepartment.Parent,
FROM Departments childDepartment
JOIN DepartmentList
ON childDepartment.Parent = DepartmentList.DepartmentCode
)
SELECT * FROM DepartmentList
这不是你问题的直接答案,但这会给你选择和想法。希望这有帮助 选择[ClockNo]
SELECT [ClockNo]
, [CostCentre]
, CASE
WHEN Department <> 0 THEN dept.[Level1]
END AS [Level1]
, CASE
WHEN Department <> 0 THEN dept.[Level2]
END AS [Level2]
, CASE
WHEN Department <> 0 THEN dept.[Level3]
END AS [Level3]
, CASE
WHEN Department <> 0 THEN dept.[Level4]
END AS [Level4]
FROM [Employees] emp
LEFT JOIN
(
SELECT
CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d4.[DepartmentCode]
WHEN d3.[DepartmentCode] IS NOT NULL THEN d3.[DepartmentCode]
WHEN d2.[DepartmentCode] IS NOT NULL THEN d2.[DepartmentCode]
ELSE d1.[DepartmentCode]
END AS [Level1]
, CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d3.[DepartmentCode]
WHEN d3.[DepartmentCode] IS NOT NULL THEN d2.[DepartmentCode]
WHEN d2.[DepartmentCode] IS NOT NULL THEN d1.[DepartmentCode]
ELSE NULL
END AS [Level2]
, CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d2.[DepartmentCode]
WHEN d3.[DepartmentCode] IS NOT NULL THEN d1.[DepartmentCode]
ELSE NULL
END AS [Level3]
, CASE
WHEN d4.[DepartmentCode] IS NOT NULL THEN d1.[DepartmentCode]
ELSE NULL
END AS [Level4]
, d1.[DepartmentCode] AS [DepartmentCode]
, d1.[CostCentreCode] AS [CostCenter]
FROM [Departments] d1
LEFT JOIN
[Departments] d2
ON d1.[Parent] = d2.[DepartmentCode]
LEFT JOIN
[Departments] d3
ON d2.[Parent] = d3.[DepartmentCode]
LEFT JOIN
[Departments] d4
ON d3.[Parent] = d4.[DepartmentCode]
) AS dept
ON emp.[Department] = dept.[DepartmentCode]
ORDER BY emp.[ClockNo]
,[成本中心]
案例
当部门为0时,则为部门[级别1]
结束时为[Level1]
案例
当部门为0时,则为部门[Level2]
结束时为[Level2]
案例
当部门为0时,则为部门[级别3]
结束时为[Level3]
案例
当部门为0时,则为部门[Level4]
结束时为[Level4]
来自[雇员]环境管理计划
左连接
(
挑选
案例
当d4.[DepartmentCode]不为空时,则d4.[DepartmentCode]
当d3.[DepartmentCode]不为空时,则d3.[DepartmentCode]
当d2.[DepartmentCode]不为空时,则d2.[DepartmentCode]
其他d1.[部门代码]
结束时为[Level1]
案例
当d4.[DepartmentCode]不为空时,则d3.[DepartmentCode]
当d3.[DepartmentCode]不为空时,则d2.[DepartmentCode]
当d2.[DepartmentCode]不为空时,则d1.[DepartmentCode]
否则无效
结束时为[Level2]
案例
当d4.[DepartmentCode]不为空时,则d2.[DepartmentCode]
当d3.[DepartmentCode]不为空时,则d1.[DepartmentCode]
否则无效
结束时为[Level3]
案例
当d4.[DepartmentCode]不为空时,则d1.[DepartmentCode]
否则无效
结束时为[Level4]
,d1.【部门代码】为【部门代码】
,d1。[成本中心代码]作为[成本中心]
来自[部门]d1
左连接
[部门]d2
在d1上。[父项]=d2。[部门代码]
左连接
[部门]d3
在d2上。[父项]=d3。[部门代码]
左连接
[部门]d4
在d3上。[父项]=d4。[部门代码]
)作为部门
关于emp。[部门]=部门[部门代码]
由emp下达的命令。[时钟号]
尝试此查询。不确定它将如何在有了这个COALESCE
的大数据上显示自己的性能
其思想是构建一个派生的层次结构表,该表指向每个部门
lev1 lev2 lev3 lev4
1 NULL NULL NULL
1 2 NULL NULL
1 3 NULL NULL
1 3 5 NULL
4 NULL NULL NULL
然后使用最右边的部门与员工加入。以下是完整的查询:
SELECT
ClockNo,
CostCentre,
lev1,
lev2,
lev3,
lev4
FROM Employees
LEFT JOIN
(
SELECT
d1.DepartmentCode AS lev1,
NULL as lev2,
NULL as lev3,
NULL as lev4
FROM departments AS d1
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
NULL as lev3,
NULL as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
d3.DepartmentCode as lev3,
NULL as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
JOIN departments AS d3 ON d3.parent = d2.departmentcode
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
d3.DepartmentCode as lev3,
d4.DepartmentCode as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
JOIN departments AS d3 ON d3.parent = d2.departmentcode
JOIN departments AS d4 ON d4.parent = d3.departmentcode
WHERE d1.parent=0
) Department
ON COALESCE(Department.lev4, Department.lev3, Department.lev2, Department.lev1) = Employees.Department
ORDER BY ClockNo
当我们加入表时,当我们找到属于上一级员工的适当部门时,我们应该停止进一步遍历路径
当Employee.Department=0时,我们也有例外情况。在这种情况下,我们不应该加入任何部门,因为在这种情况下,部门是根
我们只需要选择包含某一级别员工部门的记录。
如果员工的部门级别大于4,我们应扩展所有4个级别的部门并按原样显示(即使无法达到所需的部门级别,并且在扩展的部门级别中未找到)
选择e.ClockNo,
e、 成本中心,
d1.部门代码为1级,
d2.部门代码为2级,
d3.部门代码为3级,
d4.部门代码为级别4
来自雇员
左键连接部门d1
在e.CostCentre=d1.CostCentreCode上
和d1.Parent=0
和((d1.DepartmentCode=0和e.Department=0)或e.Department 0)
左加入部门d2
在d2上。父项=d1。部门代码
和(d1.部门代码!=e.部门和e.部门0)
左连接部门d3
在d3上。父项=d2。部门代码
和(d2.部门代码!=e.部门和e.部门0)
左键连接部门d4
在d4上。父项=d3。部门代码
和(d3.部门代码!=e.部门和e.部门0)
其中e.Department=d1.DepartmentCode
或e.部门=d2.部门代码
或e.部门=d3.部门代码
或e.Department=d4.DepartmentCode
或e.Department=0
或(
(d1.A)
SELECT
ClockNo,
CostCentre,
lev1,
lev2,
lev3,
lev4
FROM Employees
LEFT JOIN
(
SELECT
d1.DepartmentCode AS lev1,
NULL as lev2,
NULL as lev3,
NULL as lev4
FROM departments AS d1
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
NULL as lev3,
NULL as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
d3.DepartmentCode as lev3,
NULL as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
JOIN departments AS d3 ON d3.parent = d2.departmentcode
WHERE d1.parent=0
UNION ALL
SELECT
d1.DepartmentCode AS lev1,
d2.DepartmentCode as lev2,
d3.DepartmentCode as lev3,
d4.DepartmentCode as lev4
FROM departments AS d1
JOIN departments AS d2 ON d2.parent = d1.departmentcode
JOIN departments AS d3 ON d3.parent = d2.departmentcode
JOIN departments AS d4 ON d4.parent = d3.departmentcode
WHERE d1.parent=0
) Department
ON COALESCE(Department.lev4, Department.lev3, Department.lev2, Department.lev1) = Employees.Department
ORDER BY ClockNo
select e.ClockNo,
e.CostCentre,
d1.DepartmentCode as Level1,
d2.DepartmentCode as Level2,
d3.DepartmentCode as Level3,
d4.DepartmentCode as Level4
from Employees e
left join Departments d1
on e.CostCentre=d1.CostCentreCode
and d1.Parent=0
and ((d1.DepartmentCode = 0 and e.Department = 0) or e.Department <> 0)
left join Departments d2
on d2.parent=d1.DepartmentCode
and (d1.DepartMentCode != e.Department and e.Department<>0)
left join Departments d3
on d3.parent=d2.DepartmentCode
and (d2.DepartMentCode != e.Department and e.Department<>0)
left join Departments d4
on d4.parent=d3.DepartmentCode
and (d3.DepartMentCode != e.Department and e.Department<>0)
where e.Department=d1.DepartmentCode
or e.Department=d2.DepartmentCode
or e.Department=d3.DepartmentCode
or e.Department=d4.DepartmentCode
or e.Department=0
or (
(d1.DepartmentCode is not null) and
(d2.DepartmentCode is not null) and
(d3.DepartmentCode is not null) and
(d4.DepartmentCode is not null)
)
order by e.ClockNo;
;WITH CTE (DepartmentCode, CostCentreCode, Parent, DepartmentLevel)
AS (
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, 1
FROM dbo.Departments AS D
WHERE D.Parent = 0
UNION ALL
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, C.DepartmentLevel + 1
FROM dbo.Departments AS D
INNER JOIN CTE AS C
ON C.DepartmentCode = D.Parent
AND C.CostCentreCode = D.CostCentreCode
)
SELECT *
INTO #DepartmentLevels
FROM CTE;
╔════════════════╦════════════════╦════════╦═════════════════╗
║ DepartmentCode ║ CostCentreCode ║ Parent ║ DepartmentLevel ║
╠════════════════╬════════════════╬════════╬═════════════════╣
║ 1 ║ AAA ║ 0 ║ 1 ║
║ 4 ║ BBB ║ 0 ║ 1 ║
║ 2 ║ AAA ║ 1 ║ 2 ║
║ 3 ║ AAA ║ 1 ║ 2 ║
║ 5 ║ AAA ║ 3 ║ 3 ║
╚════════════════╩════════════════╩════════╩═════════════════╝
;WITH CTE (DepartmentCode, CostCentreCode, Parent, DepartmentLevelCode)
AS (
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, D.DepartmentCode
FROM dbo.Departments AS D
UNION ALL
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, C.DepartmentLevelCode
FROM dbo.Departments AS D
INNER JOIN CTE AS C
ON C.Parent = D.DepartmentCode
)
SELECT *
FROM CTE;
╔════════════════╦════════════════╦════════╦═════════════════════╗
║ DepartmentCode ║ CostCentreCode ║ Parent ║ DepartmentLevelCode ║
╠════════════════╬════════════════╬════════╬═════════════════════╣
║ 1 ║ AAA ║ 0 ║ 1 ║
║ 2 ║ AAA ║ 1 ║ 2 ║
║ 3 ║ AAA ║ 1 ║ 3 ║
║ 4 ║ BBB ║ 0 ║ 4 ║
║ 5 ║ AAA ║ 3 ║ 5 ║
║ 3 ║ AAA ║ 1 ║ 5 ║
║ 1 ║ AAA ║ 0 ║ 5 ║
║ 1 ║ AAA ║ 0 ║ 3 ║
║ 1 ║ AAA ║ 0 ║ 2 ║
╚════════════════╩════════════════╩════════╩═════════════════════╝
;WITH CTE (DepartmentCode, CostCentreCode, Parent, DepartmentLevelCode)
AS (
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, D.DepartmentCode
FROM dbo.Departments AS D
UNION ALL
SELECT D.DepartmentCode, D.CostCentreCode, D.Parent, C.DepartmentLevelCode
FROM dbo.Departments AS D
INNER JOIN CTE AS C
ON C.Parent = D.DepartmentCode
)
SELECT E.ClockNo
, E.CostCentre
, C.Level1
, C.Level2
, C.Level3
, C.Level4
FROM dbo.Employees AS E
OUTER APPLY (
SELECT MAX(CASE WHEN DL.DepartmentLevel = 1 THEN C.DepartmentCode END)
, MAX(CASE WHEN DL.DepartmentLevel = 2 THEN C.DepartmentCode END)
, MAX(CASE WHEN DL.DepartmentLevel = 3 THEN C.DepartmentCode END)
, MAX(CASE WHEN DL.DepartmentLevel = 4 THEN C.DepartmentCode END)
FROM CTE AS C
INNER JOIN #DepartmentLevels AS DL
ON DL.DepartmentCode = C.DepartmentCode
WHERE C.DepartmentLevelCode = E.Department
) AS C(Level1, Level2, Level3, Level4);
╔═════════╦════════════╦════════╦════════╦════════╦════════╗
║ ClockNo ║ CostCentre ║ Level1 ║ Level2 ║ Level3 ║ Level4 ║
╠═════════╬════════════╬════════╬════════╬════════╬════════╣
║ 1 ║ AAA ║ ║ ║ ║ ║
║ 2 ║ AAA ║ 1 ║ 3 ║ ║ ║
║ 3 ║ BBB ║ ║ ║ ║ ║
║ 4 ║ BBB ║ 4 ║ ║ ║ ║
║ 5 ║ CCC ║ ║ ║ ║ ║
║ 6 ║ AAA ║ 1 ║ ║ ║ ║
║ 7 ║ AAA ║ 1 ║ 3 ║ 5 ║ ║
╚═════════╩════════════╩════════╩════════╩════════╩════════╝
SELECT e.ClockNo, e.CostCentre, Level1, Level2, Level3, Level4
FROM Employees e
LEFT JOIN
(SELECT
d1.departmentcode
, d1.CostCentreCode
, coalesce (d4.departmentcode, d3.departmentcode
, d2.departmentcode, d1.departmentcode) AS Level1
, case when d4.departmentcode is not null then d3.departmentcode
when d3.departmentcode is not null then d2.departmentcode
when d2.departmentcode is not null then d1.departmentcode end as Level2
, case when d4.departmentcode is not null then d2.departmentcode
when d3.departmentcode is not null then d1.departmentcode end as Level3
, case when d4.departmentcode is not null then d1.departmentcode end as Level4
FROM departments AS d1
LEFT JOIN departments AS d2 ON d1.parent = d2.departmentcode
LEFT JOIN departments AS d3 ON d2.parent = d3.departmentcode
LEFT JOIN departments AS d4 ON d3.parent = d4.departmentcode) d
ON d.DepartmentCode = e.Department AND d.CostCentreCode = e.CostCentre
;
, case when d4.Parent > 0 then NULL else
coalesce (d4.departmentcode, d3.departmentcode
, d2.departmentcode, d1.departmentcode) end AS Level1