SQL Server CTE ed esempio di ricorsione


109

Non uso mai CTE con la ricorsione. Stavo solo leggendo un articolo su di esso. Questo articolo mostra le informazioni sui dipendenti con l'aiuto di Sql server CTE e ricorsione. Fondamentalmente mostra i dipendenti e le informazioni del loro manager. Non sono in grado di capire come funziona questa query. Ecco la domanda:

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

Qui sto postando su come viene mostrato l'output: inserisci qui la descrizione dell'immagine

Ho solo bisogno di sapere come si sta mostrando prima il manager e poi il suo subordinato in un loop. Immagino che la prima istruzione sql si attivi solo una volta e che restituisca tutti gli ID dei dipendenti.

E la seconda query si attiva ripetutamente, interrogando il database su cui esiste il dipendente con l'ID gestore corrente.

Spiega come viene eseguita l'istruzione sql in un ciclo interno e dimmi anche l'ordine di esecuzione sql. Grazie.

La mia seconda fase della domanda

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

Q 1) come viene incrementato il valore di N? se il valore viene assegnato ogni volta a N, allora N valore può essere incrementato ma solo la prima volta che N valore è stato inizializzato.

D 2) CTE e ricorsione dei rapporti con i dipendenti:

Nel momento in cui aggiungo due manager e aggiungo altri dipendenti sotto il secondo manager è dove inizia il problema.

Desidero visualizzare il primo dettaglio del manager e nelle righe successive solo i dettagli del dipendente che si riferiscono al subordinato di quel manager.

supporre

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

Voglio visualizzare i risultati in questo modo con le espressioni CTE. Per favore dimmi cosa modificare nel mio sql che ho fornito qui per tirare le relazioni manager-dipendente. Grazie.

Voglio che l'output sia così:

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

È possibile...?

Risposte:


210

Non ho testato il tuo codice, ho solo cercato di aiutarti a capire come funziona nei commenti;

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'esempio più semplice di ricorsivo a CTEcui posso pensare per illustrarne il funzionamento è;

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

Q 1) come viene incrementato il valore di N. se il valore viene assegnato a N ogni volta, allora N valore può essere incrementato ma solo la prima volta che N valore è stato inizializzato .

A1:In questo caso, Nnon è una variabile. Nè un alias. È l'equivalente di SELECT 1 AS N. È una sintassi delle preferenze personali. Esistono 2 metodi principali di aliasing delle colonne in un file CTEin T-SQL. Ho incluso l'analogo di una semplice CTEin Excelper cercare di illustrare in modo più familiare ciò che sta accadendo.

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

Excel_CTE

D 2) ora qui su CTE e ricorsione della relazione con i dipendenti nel momento in cui aggiungo due manager e aggiungo altri pochi dipendenti sotto il secondo manager, quindi il problema inizia. Voglio visualizzare i dettagli del primo manager e nelle righe successive verranno visualizzati solo i dettagli dei dipendenti quelli che sono subordinati a quel manager

A2:

Questo codice risponde alla tua domanda?

--------------------------------------------
-- 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 altro sql con struttura ad albero

SELECT ID,space(nLevel+
                    (CASE WHEN nLevel > 1 THEN nLevel ELSE 0 END)
                )+Name
FROM Hierarchy
ORDER BY Family, nLevel

la query ricorsiva CTE non restituisce il risultato nel modo desiderato. voglio visualizzare il nome del primo manager e quindi visualizzare di nuovo tutti i suoi subordinati visualizzare di nuovo il nome del secondo manager e quindi visualizzare tutti i suoi subordinati. Voglio che l'output sia in questo modo. se possibile aggiorna la tua query. grazie
Thomas

Colonna [Famiglia] aggiunta. Controlla ora.
MarkD

qui do l'output nel modo in cui voglio visualizzare il risultato. per favore controlla e dimmi se è possibile ... se sì, fai le modifiche necessarie in ur sql. grazie per il tuo impegno.
Thomas,

perché è il ';' prima dell'istruzione WITH? "; CON" Grazie
Drewdin

2
@ SiKni8 - il collegamento sembra essere morto
MarkD

11

Vorrei delineare un breve parallelo semantico a una risposta già corretta.

In termini "semplici", un CTE ricorsivo può essere definito semanticamente come le seguenti parti:

1: la query CTE. Conosciuto anche come ANCHOR.

2: la query CTE ricorsiva sulla CTE in (1) con UNION ALL (o UNION o EXCEPT o INTERSECT) in modo che venga restituito il risultato finale.

3: La condizione di corner / terminazione. Che è per impostazione predefinita quando non ci sono più righe / tuple restituite dalla query ricorsiva.

Un breve esempio che renderà l'immagine chiara:

;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

Spiegazione: la prima query CTE restituisce i fornitori di base (come le foglie) che non forniscono direttamente a nessun altro fornitore (-1)

La query ricorsiva nella prima iterazione ottiene tutti i fornitori che forniscono ai fornitori restituiti da ANCHOR. Questo processo continua finché la condizione non restituisce le tuple.

UNION ALL restituisce tutte le tuple sulle chiamate ricorsive totali.

Un altro buon esempio può essere trovato qui .

PS: affinché un CTE ricorsivo funzioni, le relazioni devono avere una condizione gerarchica (ricorsiva) su cui lavorare. Es: elementId = elementParentId .. hai capito.


9

Il processo di esecuzione è davvero confuso con CTE ricorsivo, ho trovato la risposta migliore su https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx e l'abstract del processo di esecuzione CTE è come sotto.

La semantica dell'esecuzione ricorsiva è la seguente:

  1. Suddividi l'espressione CTE in membri di ancoraggio e ricorsivi.
  2. Eseguire i membri di ancoraggio creando la prima chiamata o il set di risultati di base (T0).
  3. Esegui i membri ricorsivi con Ti come input e Ti + 1 come output.
  4. Ripetere il passaggio 3 finché non viene restituito un set vuoto.
  5. Restituisce il set di risultati. Questa è un'UNIONE TUTTA da T0 a Tn.

-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

1
Questa è una risposta di solo codice che non risponde nemmeno alla domanda, poiché non contiene CTE ricorsivo .
Dragomok
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.