2009-09-25 9 views
0

je la fonction d'une valeur de table récursive suivante dans MS SQL, afin de récupérer une hiérarchie d'objets de la base de données:Condition dans une requête récursive


WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr, Iteration) AS 
(
    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, 1 
    FROM Field 
    WHERE Field.ParentNum = @ParentNum 

    UNION ALL 

    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, tmpField.Iteration + 1 
    FROM Field INNER JOIN 
    tmpField on Field.ParentNum = tmpField.ChildNum 
) 
SELECT DISTINCT ParentNum AS ParentNum, ChildNum AS ChildNum, FieldNum, FieldDescr 
FROM tmpField 

Je veux modifier la façon suivante:

Dans la dernière itération, quand il n'y a plus « enfants », je veux le champ ChildNum d'avoir la valeur de FieldNum. Dans toutes les itérations précédentes, ChildNum doit avoir la valeur du champ ChildNum, comme c'est le cas maintenant.

Quelqu'un peut-il suggérer une méthode pour y parvenir, en utilisant la requête ci-dessus comme point de départ?

Veuillez noter: malgré son nom, le champ ChildNum ne fait référence à aucun enfant d'une rangée, mais il doit être interprété comme l'identifiant de cette ligne.

Répondre

2

quand il n'y a plus d'enfants, cela signifie que ChildNum est NULL si:

... 

UNION ALL 

    SELECT Field.ParentNum, 
     COALESCE(Field.ChildNum, Field.FieldNum) ChildNum, 
     Field.FieldNum, 
     ... 

EDIT: (commentaire suivant Daan)

ok, dans ce cas, nous pourrions vérifier ChildNum count 'enfants':

... 

    UNION ALL 

     SELECT F1.ParentNum, 
      CASE WHEN (SELECT COUNT(1) 
          FROM FIELD F2 
         WHERE F2.ParentNum = F1.ChildNum) = 0 
        THEN F1.FieldNum 
        ELSE F1.ChildNum 
      END ChildNum, 
      F1.FieldNum, F1.FieldDescr, tmpField.Iteration + 1 
     FROM Field F1 INNER JOIN 
     tmpField on F1.ParentNum = tmpField.ChildNum 

... 

EDIT2:

nous allons passer le contrôle à l'extérieur:

WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr, Iteration) AS 
(
    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, 1 
    FROM Field 
    WHERE Field.ParentNum = @ParentNum 

    UNION ALL 

    SELECT Field.ParentNum, Field.ChildNum, Field.FieldNum, Field.FieldDescr, tmpField.Iteration + 1 
    FROM Field INNER JOIN 
    tmpField on Field.ParentNum = tmpField.ChildNum 
) 
SELECT DISTINCT ParentNum AS ParentNum, 
       CASE WHEN EXISTS (SELECT NULL 
            FROM Field f 
            WHERE tmpField.ChildNum = f.ParentNum) 
         THEN tmpField.ChildNum 
         ELSE tmpField.FieldNum 
       END ChildNum, 
       FieldNum, 
       FieldDescr 
FROM tmpField 
+0

Non, malheureusement , ChildNum n'est pas null dans ce cas. Vous devriez lire ChildNum comme l'identifiant de la ligne courante, pas comme une référence à une autre ligne. La dénomination est gênante, je sais, mais c'est malheureusement hors de mon contrôle :) – Daan

+0

Votre édition ressemble à un bon moyen d'accomplir cela. Malheureusement, en essayant de modifier ma fonction, il donne maintenant l'erreur suivante: GROUP BY, HAVING, ou les fonctions d'agrégat ne sont pas autorisés dans la partie récursive d'une expression de table commune récursif 'tmpField'. Des suggestions pour une solution de contournement? – Daan

+0

Au lieu de CASE QUAND (SELECT COUNT (1) ...) = 0 ALORS numChamp AUTRE ChildNum FIN vous devez utiliser CASE QUAND exist (Select * from ...) ALORS ChildNum AUTRE numChamp END Le second fonctionne plus rapidement – Niikola

1

Cela devrait renvoyer des données dont vous avez besoin. Je retiré itération que vous ne l'utilisez pas plus tard

REJOIGNEZ VERSION

;WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr) AS 
(
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    WHERE f.ParentNum = @ParentNum 
    UNION ALL 
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    INNER JOIN tmpField on f.ParentNum = tmpField.ChildNum 
) 
SELECT t.ParentNum AS ParentNum, 
     Case When p.ParentNum is Null 
      Then t.FieldNum 
      Else t.ChildNum 
     End AS ChildNum, 
     t.FieldNum, 
     t.FieldDescr 
FROM tmpField t 
Left Join (Select distinct ParentNum From Field) p on t.ChildNum=p.ParentNum 

ou

VERSION sous-requête (modifié pour utiliser EXISTE au lieu de COUNT)

;WITH tmpField (ParentNum, ChildNum, FieldNum, FieldDescr) AS 
(
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    WHERE f.ParentNum = @ParentNum 
    UNION ALL 
    SELECT f.ParentNum, f.ChildNum, f.FieldNum, f.FieldDescr 
    FROM Field f 
    INNER JOIN tmpField on f.ParentNum = tmpField.ChildNum 
) 
SELECT t.ParentNum AS ParentNum, 
     Case When Exists(Select * from Field p Where t.ChildNum=p.ParentNum) 
      Then t.ChildNum 
      Else t.FieldNum 
     End AS ChildNum, 
     t.FieldNum, 
     t.FieldDescr 
FROM tmpField t 
+0

Les deux requêtes affichées ci-dessus perforent deux fois plus vite que la solution avec COUNT – Niikola

Questions connexes