Exemple de CTE et de récursivité SQL Server

109

Je n'utilise jamais CTE avec récursivité. Je lisais juste un article là-dessus. Cet article montre les informations sur les employés à l'aide du serveur SQL CTE et de la récursivité. Il montre essentiellement les employés et leurs informations de manager. Je ne suis pas en mesure de comprendre le fonctionnement de cette requête. Voici la requête:

WITH
  cteReports (EmpID, FirstName, LastName, MgrID, EmpLevel)
  AS
  (
    SELECT EmployeeID, FirstName, LastName, ManagerID, 1
    FROM Employees
    WHERE ManagerID IS NULL
    UNION ALL
    SELECT e.EmployeeID, e.FirstName, e.LastName, e.ManagerID,
      r.EmpLevel + 1
    FROM Employees e
      INNER JOIN cteReports r
        ON e.ManagerID = r.EmpID
  )
SELECT
  FirstName + ' ' + LastName AS FullName,
  EmpLevel,
  (SELECT FirstName + ' ' + LastName FROM Employees
    WHERE EmployeeID = cteReports.MgrID) AS Manager
FROM cteReports
ORDER BY EmpLevel, MgrID

Ici, je poste sur la façon dont la sortie s'affiche: entrez la description de l'image ici

J'ai juste besoin de savoir comment cela montre d'abord le manager, puis son subordonné en boucle. Je suppose que la première instruction SQL ne se déclenche qu'une seule fois et qui renvoie tous les identifiants d'employés.

Et la deuxième requête se déclenche à plusieurs reprises, interrogeant la base de données sur laquelle l'employé existe avec l'ID de gestionnaire actuel.

Veuillez expliquer comment l'instruction sql s'exécute dans une boucle interne et indiquez-moi également l'ordre d'exécution sql. Merci.

MA 2ème phase de question

;WITH Numbers AS
(
    SELECT n = 1
    UNION ALL
    SELECT n + 1
    FROM Numbers
    WHERE n+1 <= 10
)
SELECT n
FROM Numbers

Q 1) Comment la valeur de N est-elle incrémentée? si la valeur est assignée à N à chaque fois, la valeur N peut être incrémentée mais seulement la première fois que la valeur N a été initialisée.

Q 2) CTE et récursivité des relations de travail:

Le moment où j'ajoute deux managers et que j'ajoute quelques employés supplémentaires sous le second manager, c'est là que le problème commence.

Je souhaite afficher le premier détail du responsable et dans les lignes suivantes uniquement les détails de l'employé qui se rapportent au subordonné de ce responsable.

Supposer

ID     Name      MgrID    Level
---    ----      ------   -----
1      Keith      NULL     1
2      Josh       1        2
3      Robin      1        2
4      Raja       2        3
5      Tridip     NULL     1
6      Arijit     5        2
7      Amit       5        2
8      Dev        6        3

Je veux afficher les résultats de cette manière avec des expressions CTE. Veuillez me dire ce qu'il faut modifier dans mon sql que j'ai donné ici afin de tirer les relations manager-employés. Merci.

Je veux que la sortie soit comme ceci:

ID          Name   MgrID       nLevel      Family
----------- ------ ----------- ----------- --------------------
1           Keith  NULL        1           1
3           Robin  1           2           1
2           Josh   1           2           1
4           Raja   2           3           1
5           Tridip NULL        1           2
7           Amit   5           2           2
6           Arijit 5           2           2
8           Dev    6           3           2

Est-ce possible...?

Thomas
la source

Réponses:

210

Je n'ai pas testé votre code, j'ai juste essayé de vous aider à comprendre comment il fonctionne en commentaire;

WITH
  cteReports (EmpID, FirstName, LastName, MgrID, EmpLevel)
  AS
  (
-->>>>>>>>>>Block 1>>>>>>>>>>>>>>>>>
-- In a rCTE, this block is called an [Anchor]
-- The query finds all root nodes as described by WHERE ManagerID IS NULL
    SELECT EmployeeID, FirstName, LastName, ManagerID, 1
    FROM Employees
    WHERE ManagerID IS NULL
-->>>>>>>>>>Block 1>>>>>>>>>>>>>>>>>
    UNION ALL
-->>>>>>>>>>Block 2>>>>>>>>>>>>>>>>>    
-- This is the recursive expression of the rCTE
-- On the first "execution" it will query data in [Employees],
-- relative to the [Anchor] above.
-- This will produce a resultset, we will call it R{1} and it is JOINed to [Employees]
-- as defined by the hierarchy
-- Subsequent "executions" of this block will reference R{n-1}
    SELECT e.EmployeeID, e.FirstName, e.LastName, e.ManagerID,
      r.EmpLevel + 1
    FROM Employees e
      INNER JOIN cteReports r
        ON e.ManagerID = r.EmpID
-->>>>>>>>>>Block 2>>>>>>>>>>>>>>>>>
  )
SELECT
  FirstName + ' ' + LastName AS FullName,
  EmpLevel,
  (SELECT FirstName + ' ' + LastName FROM Employees
    WHERE EmployeeID = cteReports.MgrID) AS Manager
FROM cteReports
ORDER BY EmpLevel, MgrID

L'exemple le plus simple d'un récursif auquel CTEje puisse penser pour illustrer son fonctionnement est;

;WITH Numbers AS
(
    SELECT n = 1
    UNION ALL
    SELECT n + 1
    FROM Numbers
    WHERE n+1 <= 10
)
SELECT n
FROM Numbers

Q 1) comment la valeur de N est incrémentée. si la valeur est affectée à N à chaque fois, la valeur N peut être incrémentée mais seulement la première fois que la valeur N a été initialisée .

A1:Dans ce cas, Nn'est pas une variable. Nest un alias. C'est l'équivalent de SELECT 1 AS N. C'est une syntaxe de préférence personnelle. Il existe 2 méthodes principales d'aliasing des colonnes dans un fichier CTEin T-SQL. J'ai inclus l'analogique simple CTEdans Exceld'essayer et d' illustrer de façon plus familière ce qui se passe.

--  Outside
;WITH CTE (MyColName) AS
(
    SELECT 1
)
-- Inside
;WITH CTE AS
(
    SELECT 1 AS MyColName
    -- Or
    SELECT MyColName = 1  
    -- Etc...
)

Excel_CTE

Q 2) maintenant ici à propos du CTE et de la récursivité de la relation d'employé au moment où j'ajoute deux gestionnaires et ajoute quelques employés de plus sous le deuxième gestionnaire, puis le problème commence. Je veux afficher les détails du premier responsable et dans les lignes suivantes, seuls les détails de l'employé viendront ceux qui sont subordonnés à ce responsable

A2:

Ce code répond-il à votre question?

--------------------------------------------
-- Synthesise table with non-recursive CTE
--------------------------------------------
;WITH Employee (ID, Name, MgrID) AS 
(
    SELECT 1,      'Keith',      NULL   UNION ALL
    SELECT 2,      'Josh',       1      UNION ALL
    SELECT 3,      'Robin',      1      UNION ALL
    SELECT 4,      'Raja',       2      UNION ALL
    SELECT 5,      'Tridip',     NULL   UNION ALL
    SELECT 6,      'Arijit',     5      UNION ALL
    SELECT 7,      'Amit',       5      UNION ALL
    SELECT 8,      'Dev',        6   
)
--------------------------------------------
-- Recursive CTE - Chained to the above CTE
--------------------------------------------
,Hierarchy AS
(
    --  Anchor
    SELECT   ID
            ,Name
            ,MgrID
            ,nLevel = 1
            ,Family = ROW_NUMBER() OVER (ORDER BY Name)
    FROM Employee
    WHERE MgrID IS NULL

    UNION ALL
    --  Recursive query
    SELECT   E.ID
            ,E.Name
            ,E.MgrID
            ,H.nLevel+1
            ,Family
    FROM Employee   E
    JOIN Hierarchy  H ON E.MgrID = H.ID
)
SELECT *
FROM Hierarchy
ORDER BY Family, nLevel

Un autre sql avec une structure arborescente

SELECT ID,space(nLevel+
                    (CASE WHEN nLevel > 1 THEN nLevel ELSE 0 END)
                )+Name
FROM Hierarchy
ORDER BY Family, nLevel
MarkD
la source
la requête récursive CTE ne renvoie pas le résultat comme je le souhaite. Je veux afficher le nom du premier gestionnaire, puis afficher tous ses subordonnés à nouveau afficher le deuxième nom du gestionnaire, puis afficher tous ses subordonnés. je veux que la sortie soit de cette façon. si possible, veuillez mettre à jour votre requête. merci
Thomas
Ajout de la colonne [Famille]. Vérifie maintenant.
MarkD
ici, je donne la sortie comme je veux afficher le résultat. s'il vous plaît vérifier et dites-moi est-ce possible ... si oui, faites les modifications nécessaires dans ur sql. merci pour vos efforts.
Thomas
pourquoi est-ce que ';' avant l'instruction WITH? "; AVEC" Merci
Drewdin
2
@ SiKni8 - le lien semble mort
MarkD
11

Voudrait esquisser un bref parallèle sémantique à une réponse déjà correcte.

En termes `` simples '', un CTE récursif peut être défini sémantiquement comme les parties suivantes:

1: La requête CTE. Aussi connu sous ANCHOR.

2: La requête CTE récursive sur le CTE dans (1) avec UNION ALL (ou UNION ou EXCEPT ou INTERSECT) donc le résultat final est renvoyé en conséquence.

3: La condition de coin / terminaison. Ce qui est par défaut lorsqu'il n'y a plus de lignes / tuples renvoyés par la requête récursive.

Un petit exemple qui rendra l'image claire:

;WITH SupplierChain_CTE(supplier_id, supplier_name, supplies_to, level)
AS
(
SELECT S.supplier_id, S.supplier_name, S.supplies_to, 0 as level
FROM Supplier S
WHERE supplies_to = -1    -- Return the roots where a supplier supplies to no other supplier directly

UNION ALL

-- The recursive CTE query on the SupplierChain_CTE
SELECT S.supplier_id, S.supplier_name, S.supplies_to, level + 1
FROM Supplier S
INNER JOIN SupplierChain_CTE SC
ON S.supplies_to = SC.supplier_id
)
-- Use the CTE to get all suppliers in a supply chain with levels
SELECT * FROM SupplierChain_CTE

Explication: La première requête CTE renvoie les fournisseurs de base (comme les feuilles) qui ne fournissent directement aucun autre fournisseur (-1)

La requête récursive de la première itération obtient tous les fournisseurs qui fournissent aux fournisseurs renvoyés par l'ANCRE. Ce processus continue jusqu'à ce que la condition renvoie des tuples.

UNION ALL renvoie tous les tuples sur le total des appels récursifs.

Un autre bon exemple peut être trouvé ici .

PS: Pour qu'un CTE récursif fonctionne, les relations doivent avoir une condition hiérarchique (récursive) sur laquelle travailler. Ex: elementId = elementParentId .. vous obtenez le point.

Vaibhav
la source
9

Le processus d'exécution est vraiment déroutant avec le CTE récursif, j'ai trouvé la meilleure réponse sur https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx et le résumé du processus d'exécution du CTE est comme ci-dessous.

La sémantique de l'exécution récursive est la suivante:

  1. Divisez l'expression CTE en membres d'ancrage et récursifs.
  2. Exécutez le (s) membre (s) d'ancrage créant le premier appel ou jeu de résultats de base (T0).
  3. Exécutez le (s) membre (s) récursif (s) avec Ti comme entrée et Ti + 1 comme sortie.
  4. Répétez l'étape 3 jusqu'à ce qu'un jeu vide soit renvoyé.
  5. Renvoie le jeu de résultats. C'est une UNION ALL de T0 à Tn.
Pavan
la source
-4
    --DROP TABLE #Employee
    CREATE TABLE #Employee(EmpId BIGINT IDENTITY,EmpName VARCHAR(25),Designation VARCHAR(25),ManagerID BIGINT)

    INSERT INTO #Employee VALUES('M11M','Manager',NULL)
    INSERT INTO #Employee VALUES('P11P','Manager',NULL)

    INSERT INTO #Employee VALUES('AA','Clerk',1)
    INSERT INTO #Employee VALUES('AB','Assistant',1)
    INSERT INTO #Employee VALUES('ZC','Supervisor',2)
    INSERT INTO #Employee VALUES('ZD','Security',2)


    SELECT * FROM #Employee (NOLOCK)

    ;
    WITH Emp_CTE 
    AS
    (
        SELECT EmpId,EmpName,Designation, ManagerID
              ,CASE WHEN ManagerID IS NULL THEN EmpId ELSE ManagerID END ManagerID_N
        FROM #Employee  
    )
    select EmpId,EmpName,Designation, ManagerID
    FROM Emp_CTE
    order BY ManagerID_N, EmpId
Vishal Motwani
la source
1
Il s'agit d'une réponse de code uniquement qui ne répond même pas à la question, car elle ne contient pas de CTE récursif .
Dragomok