【问题标题】:Sql Server 2008 Recursive Stored ProcSql Server 2008 递归存储过程
【发布时间】:2011-03-12 04:35:20
【问题描述】:

我需要在 SQL Server 2008 中创建一个存储过程,它将根据某个值更新表。这里的诀窍是我需要递归搜索表,直到找到我要查找的值,然后更新当前记录。例如,我有一个包含 3 列的员工表:

员工编号

经理ID

家庭身份证

对于表中的每个 EmployeeId,我想获取它的 ManagerId。然后,如果 ManagerID != 0,则获取当前 ManagerId 的 ManagerId(每个 ManagerId 将指向一个 EmployeeId)——继续此操作,直到我到达顶级经理(其中 ManagerId == 0)。

一旦找到顶级经理,我想用上述流程的最后一个 EmployeeId 的值更新启动流程的原始记录中的 FamilyId 列。

基本上我需要对表中的每条记录执行此操作。我正在尝试将 FamilyId 设置为层次结构中所有员工和经理的根经理的值。

我不确定是否应该使用光标或 CTE 来完成这项工作——或者只是在代码中完成。

非常感谢任何帮助。

谢谢!

【问题讨论】:

  • 你应该把这个问题发到 stackexchange dba experts site
  • @Sandy - 这是编程问题,而不是关于数据库管理的问题。
  • 从您的描述中看,FamilyId 对于任意“根”员工的所有“下属”不是都相同吗?如果是这样,为什么总是爬到树的顶端?

标签: tsql sql-server-2008 stored-procedures recursion


【解决方案1】:

您也可以使用递归 CTE。

;WITH Hierarchy
     As (SELECT EmployeeId AS _EmployeeId,
                ManagerId  AS _ManagerId,
                EmployeeId AS _FamilyId
         FROM   @Employee
         WHERE  ManagerId = 0
         UNION ALL
         SELECT e.EmployeeId,
                e.ManagerId,
                h._FamilyId
         FROM   @Employee e
                JOIN Hierarchy h
                  ON h._EmployeeId = e.ManagerId)
UPDATE @Employee
SET    FamilyId = _FamilyId
FROM   Hierarchy h
WHERE  EmployeeId = _EmployeeId  

【讨论】:

  • +1,绝对。我只是打算自己做。省了我的麻烦,谢谢。 :) 递归 CTE 似乎是为这项任务而生的。
  • @Andriy - 遗憾的是它不允许递归 CTE 本身是可更新的,因此它需要加入到原始表中(除非有某种方法可以做到这一点?)
  • @Martin:不能肯定地说,但我怀疑是否有这样的方法。鉴于 CTE 从多个表中读取,引擎将很难决定应该更新哪个表。
  • @Andriy - 实际上它可能不得不将整个表读入线轴以保护万圣节。
  • 感谢您的回复。我要试一试。使用 CTE 的递归次数是否受到限制?
【解决方案2】:

这是我的第一次尝试。我希望我理解你的要求。

declare @Employee table (
    EmployeeId int not null 
    , ManagerId int not null
    , FamilyId int null
)

--       1       6
--      / \     / \
--     2   3    7 8
--    / \
--    4 5

insert @Employee values (1, 0, null) 
insert @Employee values (2, 1, null) 
insert @Employee values (3, 1, null) 
insert @Employee values (4, 2, null) 
insert @Employee values (5, 2, null) 
insert @Employee values (6, 0, null) 
insert @Employee values (7, 6, null) 
insert @Employee values (8, 6, null) 

-- the data before the update       
select * from @Employee

-- initial update to get immediate managers
update Employee
set FamilyId = Manager.EmployeeId
from @Employee Employee
inner join @Employee Manager on Manager.EmployeeId = Employee.ManagerId

-- the data after the first update     
select * from @Employee

-- do more updates until done
while exists (
    select * 
    from @Employee 
    where (
        FamilyId is not null 
        and FamilyId not in (
            select EmployeeId from @Employee where ManagerId = 0
        )
    )
) 
begin
    update Employee
    set FamilyId = Manager.ManagerId
    from @Employee Employee
    inner join @Employee Manager on Manager.EmployeeId = Employee.FamilyId
    where (
        Employee.FamilyId is not null 
        and Employee.FamilyId not in (
            select EmployeeId from @Employee where ManagerId = 0
        )
    )
end

-- the data after all updates
select * from @Employee

我相信还有更聪明的方法

【讨论】:

  • ... 特别是,如果您使用 SQL 2008 的 HierarchyId,我很确定您可以在一行中完成此操作!
  • +1 格式很好的答案,我借用了你的表格人口代码!
  • @Martin - 为 CTE 解决方案和教我一个新术语:“万圣节保护”=)
【解决方案3】:

我怀疑我会为此调用用户定义函数 (UDF)。 UDF 会递归调用自身。

尝试谷歌搜索:递归 UDF sql server

此链接似乎提供了一个示例(尽管他们确实注意到从 SQL 2000 开始,您只能递归 32 级深度。) http://weblogs.sqlteam.com/jeffs/archive/2003/11/21/588.aspx

很抱歉,我现在没有更多时间来回答您的问题。如果明天还有问题,我会花更多时间来解释。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-26
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多