7

次のテーブルがあります。

Employees
-------------
ClockNo     int
CostCentre  varchar
Department  int

Departments
-------------
DepartmentCode  int
CostCentreCode  varchar
Parent          int

部門は、無限の階層があることを意味する親として他の部門を持つことができます。すべての部門はコスト センターに属しているため、常にCostCentreCode. parent = 0トップレベルの部署なら

従業員には値が必要ですが、部門に属していないことを意味する 0の場合がありますCostCentreDepartment

私が試して生成したいのは、最大 4 レベルの階層を提供するクエリです。このような:

EmployeesLevels
-----------------
ClockNo
CostCentre
DeptLevel1
DeptLevel2
DeptLevel3
DeptLevel4

部門構造を独自に表示するものを取得することができましたが、重複する従業員行を作成せずにこれを従業員にリンクする方法を見つけることができません:

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);

これにより、次の構造が得られます (角括弧内の従業員の時計番号)。

Root
  |
  |---AAA                   [1]
  |    \---1                [6]
  |       |---2     
  |       \---3             [2]
  |          \---5          [7]
  |             \---12
  |                \---15   [8]
  |
  |---BBB                   [3]
  |    \---4                [4]
  |
  \---CCC                   [5]

クエリは次を返す必要があります。

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 までのすべてのレベルを表示したいのですが、この場合は CostCentre を表示するだけで十分です

4

7 に答える 7

4

テーブルを結合するとき、前のレベルで従業員に属する適切な部門が見つかったら、パスのそれ以上のトラバーサルを停止する必要があります。

また、Employee.Department=0 の例外的なケースもあります。この場合、部門はルートであるため、どの部門にも参加しないでください。

いずれかのレベルで従業員の部門を含むレコードのみを選択する必要があります。従業員の部門レベルが 4 より大きい場合、4 つのレベルの部門すべてを展開し、そのまま表示する必要があります (目的の部門レベルに到達できず、展開された部門内に見つからなかった場合でも)。

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;
于 2016-04-18T11:17:52.273 に答える
2

ここでの主な課題は、従業員の部署を列Level1Level2Level3、または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

SQL フィドル

UNION ALLまたは、既存のレベル (0 から 4 の部門のチェーン) に関して、5 つの可能なシナリオのプレーンを実行することもできます。

SELECT     ClockNo, CostCentre,       d4.DepartmentCode Level1,
           d3.DepartmentCode Level2,  d2.DepartmentCode Level3,
           d1.DepartmentCode Level4
FROM       Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
INNER JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
INNER JOIN departments AS d3 ON d3.DepartmentCode = d2.Parent
INNER JOIN departments AS d4 ON d4.DepartmentCode = d3.Parent
UNION ALL
SELECT     ClockNo, CostCentre, d3.DepartmentCode,
           d2.DepartmentCode,   d1.DepartmentCode, NULL
FROM       Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
INNER JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
INNER JOIN departments AS d3 ON d3.DepartmentCode = d2.Parent
WHERE      d3.Parent = 0
UNION ALL
SELECT     ClockNo, CostCentre, d2.DepartmentCode,
           d1.DepartmentCode,   NULL, NULL
FROM       Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
INNER JOIN departments AS d2 ON d2.DepartmentCode = d1.Parent
WHERE      d2.Parent = 0
UNION ALL
SELECT     ClockNo, CostCentre, d1.DepartmentCode Level1,
           NULL, NULL, NULL
FROM       Employees e
INNER JOIN departments AS d1 ON d1.DepartmentCode = e.Department
WHERE      d1.Parent = 0
UNION ALL
SELECT     ClockNo, CostCentre, NULL, NULL, NULL, NULL
FROM       Employees e
WHERE      e.Department = 0
ORDER BY   ClockNo

SQL フィドル

于 2016-04-18T09:36:36.873 に答える
2
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]
于 2016-04-18T10:58:56.927 に答える
0

そのため、これを行うために次の 2 つの手順を実行しました。

  1. deparments のレベルを再帰的に生成する必要がありました
  2. 可能なすべての親ノードを生成して、ピボット ビューで表示できるようにします

この再帰クエリは DepartmentLevels を構築します。

;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 ║
╚════════════════╩════════════════╩════════╩═════════════════════╝

これで、これら 3 つのバディを table と組み合わせて、Employees目的の出力を得ることができます。

;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      ║        ║
╚═════════╩════════════╩════════╩════════╩════════╩════════╝

このクエリは、に基づいて対応するものを見つけ、DepartmentLevelCodeに基づいDepartmentCodeてピボットしDepartmentLevelます。うまくいけば、それは正しいです。

于 2016-04-18T11:55:00.107 に答える