0

次の列を持つテーブルがあるとします。

従業員テーブル

employeeID int
employeeName varchar(50)
managerID int
totalOrganization int

managerIDはemployeeIDを参照します。totalOrganizationは現在、すべてのレコードで0です。

各行のtotalOrganizationを、その下の従業員の総数に更新したいと思います。

したがって、次のレコードを使用します。

employeeID     employeeName     managerID     totalOrganization
1              John Cruz        NULL          0
2              Mark Russell     1             0
3              Alice Johnson    1             0
4              Juan Valdez      3             0

クエリはtotalOrganizationsを次のように更新する必要があります。

employeeID     employeeName     managerID     totalOrganization
1              John Cruz        NULL          3
2              Mark Russell     1             0
3              Alice Johnson    1             1
4              Juan Valdez      3             0

私はある程度の組織を手に入れることができることを知っています。次のCTEを使用したチャート:

WITH OrgChart (employeeID, employeeName,managerID,level)
AS (
    SELECT employeeID,employeeName,0 as managerID,0 AS Level
    FROM Employees
    WHERE managerID IS NULL
    UNION ALL
    SELECT Employees.employeeID,Employees.employeeName,Employees.managerID,Level + 1
    FROM Employees INNER JOIN
    OrgChart ON Employees.managerID = OrgChart.employeeID
   )
SELECT employeeID,employeeName,managerID, level
FROM OrgChart;

データを解析するためにSQLの外部でルーチンを構築するのではなく、ストアドプロシージャを使用してEmployeesテーブルを更新する方法はありますか?

4

3 に答える 3

2

別のCTEを追加して従業員数を決定し、それをUpdateステートメントで使用できます。

WITH OrgChart (employeeID, employeeName,managerID,level)
AS (
    SELECT employeeID,employeeName,0 as managerID,0 AS Level
    FROM Employees
    WHERE managerID IS NULL
    UNION ALL
    SELECT Employees.employeeID,Employees.employeeName,Employees.managerID,Level + 1
    FROM Employees 
        INNER JOIN OrgChart 
            ON Employees.managerID = OrgChart.employeeID
   )
   , SubordinateCount As
   (
   Select ManagerId, Count(*) As Total
   From OrgChart
   Group By ManagerId
   )
Update Employees
Set TotalOrganization = SubordinateCount.Total
FROM SubordinateCount
    Join Employees As E
        On E.employeeId = SubordinateCount.ManagerId

添加

仕様の変更は、すべての部下の従業員の数が必要になることです。その秘訣は、各マネージャーへの従業員のパスを作成することです。それで、最初にここに私のテストデータがあります:

Insert Employees(EmployeeId, Name, ManagerId) Values(1, 'Alice', Null)
Insert Employees(EmployeeId, Name, ManagerId) Values(2, 'Bob', 1)
Insert Employees(EmployeeId, Name, ManagerId) Values(3, 'Charlie', 1)
Insert Employees(EmployeeId, Name, ManagerId) Values(4, 'Dan', 3)
Insert Employees(EmployeeId, Name, ManagerId) Values(5, 'Ellen', 3)
Insert Employees(EmployeeId, Name, ManagerId) Values(6, 'Fred', 5)
Insert Employees(EmployeeId, Name, ManagerId) Values(7, 'Gale', 6)
Insert Employees(EmployeeId, Name, ManagerId) Values(8, 'Harry', 6)

したがって、最初に、マネージャーへのパスを提供するクエリを記述します。

With 
    OrgChart As
    (
    Select E.EmployeeId, E.Name, Null As ManagerId, 0 AS Level
        , Cast( '/' + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100) ) As Path
    From dbo.Employees As E
    Where E.ManagerId Is Null
    Union All
    Select E.EmployeeID, E.Name, E.ManagerID, Level + 1
        , Cast( OrgChart.Path + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100))
    From dbo.Employees As E
        Join OrgChart 
            On OrgChart.EmployeeId = E.ManagerID
   )
Select *
From OrgChart 

それは以下を生み出します:


    EmployeeId  Name    ManagerId   Level   Path
    1           Alice       NULL        0       /1/
    2           Bob         1           1       /1/2/
    3           Charlie     1           1       /1/3/
    4           Dan         3           2       /1/3/4/
    5           Ellen       3           2       /1/3/5/
    6           Fred        5           3       /1/3/5/6/
    7           Gale        6           4       /1/3/5/6/7/
    8           Harry       6           4       /1/3/5/6/8/

ここで、特定の従業員が誰かのパスに存在するインスタンスをカウントする必要があります。

With 
    OrgChart As
    (
    Select E.EmployeeId, E.Name, Null As ManagerId, 0 AS Level
        , Cast( '/' + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100) ) As Path
    From dbo.Employees As E
    Where E.ManagerId Is Null
    Union All
    Select E.EmployeeID, E.Name, E.ManagerID, Level + 1
        , Cast( OrgChart.Path + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100))
    From dbo.Employees As E
        Join OrgChart 
            On OrgChart.EmployeeId = E.ManagerID
   )
    , OrgCounts As
    (
    Select O.EmployeeId, O.Name, O.ManagerId, O.Level, O.Path
        , (Select Count(*)
            From OrgChart As O1
            Where O1.Path Like '%/' + Cast(E.EmployeeId As varchar(10)) + '/%') - 1 As SubordinateTotal
    From Employees As E
        Join OrgChart As O
            On O.EmployeeId = E.EmployeeId
    )
Select O.EmployeeId, O.Name, O.ManagerId, O.Level, O.Path, O.SubordinateTotal
From OrgCounts

現在の従業員を除外するために、合計から1を引きます。適切な結果を提供するクエリが見つかったので、それを使用して更新を簡単に行うことができます。

With 
    OrgChart As
    (
    Select E.EmployeeId, E.Name, Null As ManagerId, 0 AS Level
        , Cast( '/' + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100) ) As Path
    From dbo.Employees As E
    Where E.ManagerId Is Null
    Union All
    Select E.EmployeeID, E.Name, E.ManagerID, Level + 1
        , Cast( OrgChart.Path + Cast(E.EmployeeId As varchar(10)) + '/' As varchar(100))
    From dbo.Employees As E
        Join OrgChart 
            On OrgChart.EmployeeId = E.ManagerID
   )
    , OrgCounts As
    (
    Select O.EmployeeId, O.Name, O.ManagerId, O.Level, O.Path
        , (Select Count(*)
            From OrgChart As O1
            Where O1.Path Like '%/' + Cast(E.EmployeeId As varchar(10)) + '/%') - 1 As SubordinateTotal
    From Employees As E
        Join OrgChart As O
            On O.EmployeeId = E.EmployeeId
    )
Update Employees
Set TotalOrganization = O.SubordinateTotal
From OrgCounts As O
    Join dbo.Employees As E
        On E.EmployeeId = O.EmployeeId
于 2010-06-07T21:43:24.853 に答える
2

数時間の実験の後、私は次のことを思いついた。望ましい結果が得られます。誰かがそれを改善する方法を見ますか?

CREATE TABLE #totalOrganization (employeeID int,managerID int,level int);
CREATE TABLE #countedOrganization (employeeID int,managerID int,orgCount int,level int);


WITH OrgChart (employeeID,managerID,level) 
AS ( 
    SELECT employeeID,0 as managerID,0 AS Level 
    FROM Emp
    WHERE managerID IS NULL 
    UNION ALL 
    SELECT Emp.employeeID,Emp.managerID,Level + 1 
    FROM Emp 
        INNER JOIN OrgChart  
            ON Emp.managerID = OrgChart.employeeID 
   )
INSERT INTO
    #totalOrganization
    SELECT 
        employeeID,managerID,level
    FROM
        OrgChart;

DECLARE @maxLevel int
SELECT 
    @maxLevel = MAX(level)
FROM
    #totalOrganization;

WHILE (@maxLevel > -1)
    BEGIN
        INSERT INTO
            #countedOrganization
            SELECT
                upline.employeeID,upline.managerID,SUM(CONVERT(INT,CASE WHEN downline.orgCount IS NULL THEN 0 ELSE downline.orgCount END)) + CONVERT(INT,CASE WHEN COUNT(downline.employeeID) IS NULL THEN 0 ELSE COUNT(downline.employeeID) END),upline.level
            FROM
                #totalOrganization AS upline LEFT OUTER JOIN
                #countedOrganization AS downline ON downline.managerID=upline.employeeID
            WHERE
                upline.level = @maxLevel
            GROUP BY
                upline.employeeID,upline.managerID,upline.level

        SET @maxLevel = @maxLevel - 1
    END

UPDATE
    Emp
SET
    totalOrg= CONVERT(INT,CASE WHEN orgCount IS NULL THEN 0 ELSE orgCount END)
FROM
    #countedOrganization INNER JOIN
    Emp ON #countedOrganization.employeeID=Emp.employeeID
于 2010-06-08T00:48:17.900 に答える
0

これは(もちろん)ストアドプロシージャ内で実行できます。ただし、このエラーのように、特定の従業員の部下とそのすべての部下(つまり、階層内の特定のアイテムの下にあるすべての子孫を集計)を合計することはできないため、単一の(CTE)ステートメントでは実行できないように見えます。メッセージ:

GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'Subordinates'.

そのため、SQLの外部で作成するルーチン(階層の最下位の「レベル」から開始し、すべての従業員の部下を数え、階層を繰り返しながら繰り返す)は、SQL内で作成する必要があります。

于 2010-06-07T22:11:13.100 に答える