web-dev-qa-db-fra.com

SQL Server CTE et exemple de récursivité

Je n'utilise jamais CTE avec récursivité. Je viens de lire un article à ce sujet. Cet article affiche les informations sur les employés à l'aide de CTE et de la récursion du serveur SQL. Il montre essentiellement les employés et leur gestionnaire d'informations. Je ne suis pas capable de comprendre comment cette requête fonctionne. 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 est affichée: enter image description here

J'ai juste besoin de savoir comment il montre d'abord le gestionnaire, puis son subordonné dans une boucle. J'imagine que la première instruction SQL ne se déclenche qu'une seule fois et renvoie tous les identifiants d'employé.

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

Veuillez expliquer comment l'instruction SQL s'exécute dans une boucle interne et me dire é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, alors la valeur de N peut être incrémentée, mais seule la première valeur de N a été initialisée.

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

Le problème commence dès que j’ajoute deux directeurs et quelques employés supplémentaires sous le second.

Je souhaite afficher le premier détail du responsable et, dans les lignes suivantes, uniquement les détails de l'employé liés 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 telle manière avec des expressions CTE. S'il vous plaît dites-moi ce qu'il faut modifier dans mon SQL que j'ai donné ici afin de tirer les relations gestionnaire-employé. Merci.

Je veux que la sortie soit comme ça:

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...?

96
Thomas

Je n'ai pas testé votre code, j'ai simplement essayé de vous aider à comprendre comment il fonctionne dans les commentaires.

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 CTE récursif auquel je 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 assignée à N à chaque fois, alors la valeur de N peut être incrémentée mais seule la première fois que la valeur de N a été initialisée.

A1: Dans ce cas, N n'est pas une variable. N est 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 de création d'alias de colonnes dans un CTE dans T-SQL. J'ai inclus l'analogue d'un simple CTE dans Excel pour essayer de mieux illustrer 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 de CTE et de la récurrence de la relation d’employé au moment où j’ajoute deux responsables et que j’ajoute quelques employés supplémentaires sous le second responsable, puis un problème commence. Je veux afficher les détails du premier responsable et dans les lignes suivantes, seuls les détails de l'employé seront fournis aux subordonnés de 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
194
MarkD

Voudrais décrire brièvement une sémantique parallèle à 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 appelé ancre.

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

3: La condition de coin/fin. Ce qui est par défaut lorsqu'il n'y a plus de lignes/nuplets 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 pas directement à un autre fournisseur (-1).

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

UNION ALL renvoie tous les nuplets 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.

9
Vaibhav

Le processus d’exécution est vraiment déroutant avec un CTE récursif, j’ai trouvé la meilleure réponse à l’adresse https://technet.Microsoft.com/en-us/library/ms186243 (v = sql.105) .aspx et l’abrégé du processus d’exécution du CTE est présenté 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 ou les membres d'ancrage créant le premier jeu de résultats d'invocation ou de base (T0).
  3. Exécutez le ou les membres récursifs avec Ti en entrée et Ti + 1 en sortie.
  4. Répétez l'étape 3 jusqu'à ce qu'un jeu vide soit renvoyé.
  5. Renvoie le jeu de résultats. Ceci est une UNION ALL de T0 à Tn.
6
Pavan