【问题标题】:How to update tree view in SQL Server如何在 SQL Server 中更新树视图
【发布时间】:2022-10-23 18:44:07
【问题描述】:

这是我的 SQL Server 表

ID      Job                       ParentID    MyTeam
1       CEO                       NULL        
2       CFO                       1
3       CTO                       1
4       CMO                       1
5       Accounting Manager        2
6       Payroll Manager           2
7       Data Manager              3
8       Software Manager          3
9       Data Analyst              7
10      Data Engineer             7

我需要以这种方式填写 MyTeam 字段

每项工作都会有该工作管理的所有人

首席执行官团队将是CEO, CFO, CTO, CMO, Accounting Manager, Payroll Manager, Data Manager, Software Manager, Data Analyst, Data Engineer

首席财务官团队将是CFO, Accounting Manager, Payroll Manager

首席技术官团队将是CTO, Data Manager, Software Manager, Data Analyst, Data Engineer

我在这些数据上建立了一个循环,并将每个工作与其父级联系起来,依此类推

但这太慢了

是否有更快的更新语句来做到这一点?

【问题讨论】:

  • @KenWhite 不,它不是,它是不同的
  • @KenWhite 它只返回父母的子记录列表,不连接所有或分组或更新

标签: sql-server tsql sql-update


【解决方案1】:

这种方法使用循环,但应该比较快

  • 它基于作业中的级别数运行,而不是个人(因此,如果您像这里一样有 3 个级别,它会执行 3 个循环)
  • 读取和计算非常高效 - 它不需要进行大量的 JOIN 和排序和表扫描(除了第一次)

this dbfiddle 中有一些这种运行的例子,还有一些额外的报告

/*  Initial data setup  */

CREATE TABLE #Emps (ID int PRIMARY KEY, Job nvarchar(30), ParentID int);
INSERT INTO #Emps (ID, Job, ParentID) VALUES
(1,       N'CEO',                       NULL),
(2,       N'CFO',                       1),
(3,       N'CTO',                       1),
(4,       N'CMO',                       1),
(5,       N'Accounting Manager',        2),
(6,       N'Payroll Manager',           2),
(7,       N'Data Manager',              3),
(8,       N'Software Manager',          3),
(9,       N'Data Analyst',              7),
(10,      N'Data Engineer ',            7);

CREATE TABLE #EmpParents (RoundNum int, ID int, ParentID int, PRIMARY KEY (RoundNum, ID));

每个“轮次”的数据(例如,循环中的迭代次数)被插入到#EmpParents 表中。

方法是 a) 首先包括基础作业和父作业 b)然后对于父工作,确定他们的父母 重复 b) 直到找不到更多父作业。

它找到“父母的父母”的方法是查看循环中的最后一个“回合”,以获取该回合中每个作业的父 ID;在下一轮中,它使用这些新父母来确定下一级父母。

请注意,我使用 WHILE 循环来完成此操作,因为我认为它更易于阅读/理解。但是,您可以使用 CURSOR 或递归 CTE 来执行此操作 - 但无论如何它们都是循环的变体。

/*  Data calculations - Find parents, and parents of parents  */

DECLARE @RoundNum int;     -- Used for counting what iteration we're up to
DECLARE @NumInserted int;  -- User for recording number of rows inserted

-- Insert initial data (own job and parent job)
INSERT INTO #EmpParents (RoundNum, ID, ParentID)
    SELECT  0, ID, ID
    FROM    #Emps
  UNION ALL
    SELECT  1, ID, ParentID
    FROM    #Emps
    WHERE   ParentID IS NOT NULL;

SET @NumInserted = @@ROWCOUNT;
SET @RoundNum = 1;

-- Add 'parents of parents' until no more found
WHILE @NumInserted > 0
    BEGIN

    SET @RoundNum += 1;

    INSERT INTO #EmpParents (RoundNum, ID, ParentID)
        SELECT  @RoundNum, #EmpParents.ID, #Emps.ParentID
        FROM    #EmpParents
                INNER JOIN #Emps ON #EmpParents.ParentID = #Emps.ID
        WHERE   #EmpParents.RoundNum = @RoundNum - 1
                AND #Emps.ParentID IS NOT NULL;

    SET @NumInserted = @@ROWCOUNT;

    END

在最终报告中,请注意它使用最后一列(ParentID)作为主列,并找到与其对应的所有作业 ID。

/*  Reporting  */

SELECT      ParentEmp.Job, STRING_AGG(TeamEmp.Job, ', ') AS Team
    FROM    #EmpParents
            INNER JOIN #Emps AS ParentEmp ON #EmpParents.ParentID = ParentEmp.ID
            INNER JOIN #Emps AS TeamEmp ON #EmpParents.ID = TeamEmp.ID
    GROUP BY #EmpParents.ParentID, ParentEmp.Job
    ORDER BY #EmpParents.ParentID;

这是结果

Job                            Team
------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CEO                            CEO, CFO, CTO, CMO, Accounting Manager, Payroll Manager, Data Manager, Software Manager, Data Analyst, Data Engineer 
CFO                            Accounting Manager, Payroll Manager, CFO
CTO                            CTO, Data Manager, Software Manager, Data Analyst, Data Engineer 
CMO                            CMO
Accounting Manager             Accounting Manager
Payroll Manager                Payroll Manager
Data Manager                   Data Manager, Data Analyst, Data Engineer 
Software Manager               Software Manager
Data Analyst                   Data Analyst
Data Engineer                  Data Engineer 

最后一点:这里没有错误检查并假设一个层次结构(例如,你不能有一个父母的循环,例如,我的下属是我老板的老板)。您可能希望在循环/等中进行一些错误检查来控制它。一个简单的方法是,如果 RoundNum 达到一个不可能的不切实际的高数字(例如,50),那么它会因错误而中止。

【讨论】:

    【解决方案2】:

    您可以为此使用递归 CTE。

    首先,获取所有员工,并为每个员工使用递归获取其子行(如果有)。请注意,我们在这里没有进行任何循环检查,假设是没有循环。

    然后按您开始时使用的最高 ID 聚合它,并将其连接回主表。

    WITH cte AS (
        SELECT e.ID, e.Job, e.ID AS TopID
        FROM Employee e
    
        UNION ALL
    
        SELECT e.ID, e.Job, cte.TopID
        FROM cte
        JOIN Employee e ON e.ParentID = cte.ID
    )
    UPDATE e
    SET MyTeam = grouped.Jobs
    FROM Employee e
    JOIN (
        SELECT
          cte.TopID,
          STRING_AGG(cte.Job, ', ') AS Jobs
        FROM cte
        GROUP BY
          cte.TopID
    ) grouped ON grouped.TopID = e.ID;
    

    您还可以通过计算 CTE 中的 Level 列来强制排序

    WITH cte AS (
        SELECT e.ID, e.Job, e.ID AS TopID, 1 AS Level
        FROM Employee e
    
        UNION ALL
    
        SELECT e.ID, e.Job, cte.TopID, cte.Level + 1
        FROM cte
        JOIN Employee e ON e.ParentID = cte.ID
    )
    UPDATE e
    SET MyTeam = grouped.Jobs
    FROM Employee e
    JOIN (
        SELECT
          cte.TopID,
          STRING_AGG(cte.Job, ', ') WITHIN GROUP (ORDER BY cte.Level) AS Jobs
        FROM cte
        GROUP BY
          cte.TopID
    ) grouped ON grouped.TopID = e.ID;
    

    db<>fiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      相关资源
      最近更新 更多