2011-03-12 4 views
4

Je dois créer une procédure stockée dans SQL Server 2008 qui mettra à jour une table en fonction de certaines valeurs. L'astuce ici est que j'ai besoin de rechercher récursivement la table jusqu'à ce que je trouve la valeur que je recherche, puis mettre à jour l'enregistrement en cours. Par exemple, j'ai une table Employés qui contient 3 colonnes:Sql Server 2008 Processus stocké récursif

EMPLOYEEID

ManagerID

FamilyId

Pour chaque EmployeeId dans le tableau, je veux obtenir son ManagerID. Ensuite, si le ManagerID! = 0, allez chercher le ManagerId du ManagerId actuel (chaque ManagerId pointera vers un EmployeeId) - continuez avec ceci jusqu'à ce que j'arrive au gestionnaire de niveau supérieur (où ManagerId == 0).

Une fois que je trouve le gestionnaire de niveau supérieur, je veux mettre à jour la colonne FamilyId dans l'enregistrement original qui a démarré le processus avec la valeur du dernier EmployeeId du processus ci-dessus.

Fondamentalement, je dois le faire avec chaque enregistrement de la table. J'essaye de mettre le FamilyId à la valeur du gestionnaire de racine pour tous les employés et directeurs dans la hiérarchie.

Je ne suis pas sûr si je devrais utiliser un curseur ou un CTE pour faire ceci - ou juste le faire dans le code.

Toute aide est grandement appréciée.

Merci!

+0

vous devriez poster cette question dans StackExchange [experts dba site] (http://dba.stackexchange.com/) – CoderHawk

+0

@Sandy - C'est question de programmation non une question sur l'administration des bases de données. –

+0

Ne résulte-t-il pas de votre description que 'FamilyId' sera le même pour tous les 'subordonnés' d'un employé 'root' arbitraire? Si oui, pourquoi toujours aller en haut de l'arbre? –

Répondre

5

Vous pouvez également utiliser un CTE récursive.

;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

+1, absolument. J'allais juste faire ça moi-même.M'a sauvé la peine, merci. :) CCE récursif semble simplement né pour la tâche. –

+0

@Andriy - Dommage que le CTE récursif lui-même ne puisse pas être mis à jour de sorte qu'il nécessite une Rejoindre sur la table d'origine (à moins qu'il y ait un moyen de le faire?) –

+0

@Martin: Je ne suis pas sûr , mais je doute qu'il existe un tel moyen. Étant donné que le CTE lit à partir de plus d'une table, le moteur aurait du mal à décider quelle table devait être mise à jour. –

0

Je suppose que j'appellerais dans une fonction définie par l'utilisateur (UDF) pour ceci. L'UDF s'appellerait de manière récursive.

Essayez googler: sql server UDF récursive

Ce lien semble donner un exemple (bien qu'ils notent que de SQL 2000, vous ne pouvez récursion profonde 32 niveaux.) http://weblogs.sqlteam.com/jeffs/archive/2003/11/21/588.aspx

Désolé de ne pas avoir plus de temps à consacrer à votre question en ce moment. Si c'est toujours un problème demain, je consacrerai plus de temps à une explication.

2

Voici mon premier coup de poignard. J'espère avoir compris vos exigences.

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 

Je suis sûr qu'il existe des moyens plus intelligents

+0

... En particulier, si vous utilisiez hierarchyId de SQL 2008, je suis à peu près sûr que vous pourriez accomplir cela en une ligne! – anon

+0

+1 Bonne réponse formatée et j'ai emprunté le code de population de votre table! –

+0

@Martin - Et +1 à droite atcha pour la solution CTE et pour m'apprendre un nouveau terme: "Protection d'Halloween" =) – anon