2010-07-01 13 views
6

Sto cercando di eseguire ciò che ritengo sia una ricorsione difficile utilizzando un CTE è SQL Server 2008. Non riesco a spiegarmi come funziona.Ricorsione CTE di SQL Server 2008

Negli esempi seguenti è possibile assumere una profondità fissa di 3 ... nulla sarà mai inferiore a quello. Nella vita reale, la profondità è "più profonda" ma ancora fissa. Nell'esempio ho cercato di semplificarlo.

I miei dati di input sono come i seguenti.

ID  PARENT_ID  NAME   DEPTH 
------------------------------------------ 
1  NULL   A    1 
2  1    B    2 
3  2    C    3 
4  1    D    2 

L'output del mio CTE dovrebbe essere la seguente tabella.

LEVEL1_ID LEVEL2_ID LEVEL3_ID LEVEL1_NAME LEVEL2_NAME LEVEL3_NAME 
-------------------------------------------------------------------------------- 
1   NULL   NULL   A    NULL   NULL 
1   2   NULL   A    B    NULL 
1   2   3   A    B    C 
1   4   NULL   A    D    NULL 

Se posso ottenere le colonne ID nell'output posso certamente mappare nomi in una tabella di ricerca.

Sono aperto ad altri modi per eseguire anche questo, incluso l'utilizzo di SSIS.

+0

non riuscivo a capire quale logica si sta applicando, cercando in vostra uscita. Penso che l'ultima riga dovrebbe essere '/ B/D' perché l'id genitore di' D' è 2, ad esempio 'B' – IsmailS

+0

ha appena aggiornato la domanda. scusa. – thomas

+0

Ancora lo stesso. Nella riga n. 4, perché hai bisogno di Level1_ID come '1' quando il suo id genitore è 2? – IsmailS

risposta

9

non è poi così difficile da fare:

;WITH cte AS 
(
    SELECT CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY ID 

mi dà un output:

/A 
/A/B 
/A/B/C 
/A/D 

Come un lato nota: la "profondità" potrebbe essere facilmente calcolato dal CTE e non hanno necessariamente bisogno di memorizzare che nella tabella (vedere la colonna Level ho aggiunto):

;WITH cte AS 
(
    SELECT 
     CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID, 
     1 AS 'Level' 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT 
     CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID, 
     cte.Level + 1 AS 'Level' 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY Level, ID 
+1

questo è fantastico. Grazie! Ho cambiato la domanda un po 'ma questo è eccellente. Posso certamente usare questo esempio. – thomas

+0

Grandi marc_s! Così veloce!!!! – IsmailS

+0

Con questo codice non si ha il problema di unire i livelli più volte? Ad esempio durante la prima corsa della corsa ricorsiva si uniranno i livelli 1 con i livelli 2, durante la 2a corsa si uniranno di nuovo i livelli 1 con i livelli 2 e i livelli 2 con i livelli 3, e così via ... le file duplicate vengono "rimossi" dall'operatore UNION, ma eseguirà molti duplicati. – munissor

0

Non ricordo che tu possa fare una subquery in una cte.

Non ho una copia di sql server qui, ma si può provare con questo codice:

WITH cte(id, path, level) 
AS 
(
    SELECT id, '/' + name, level 
    FROM yourtable 
    WHERE level = 1 

    UNION ALL 

    SELECT y.id, c.name + '/' + y.name, y.level 
    FROM yourtable y INNER JOIN 
    cte c ON c.id = y.parent_id 
    WHERE level = (SELECT max(level)+1 from cte) 
) 
SELECT path from cte 
+0

Ho ricevuto solo un sacco di errori: Msg 207, Level 16, State 1, Line 6 Nome colonna 'livello' non valido. messaggio 207, livello 16, stato 1, riga 4 Nome colonna "livello" non valido. messaggio 253, livello 16, stato 1, riga 1 Il membro ricorsivo di un'espressione di tabella comune 'cte' ha più riferimenti ricorsivi. –

+1

Ok, quindi non puoi usare una subquery;) – munissor

-1
;WITH Vals AS (
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE NULL END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE NULL END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE NULL END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE NULL END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE NULL END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE NULL END 'LEVEL3_NAME', 
       ID 'PRMID'     
     FROM #Table1 
     WHERE parentId IS NULL 
     UNION ALL 
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE LEVEL1_ID END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE LEVEL2_ID END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE LEVEL3_ID END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE LEVEL1_NAME END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE LEVEL2_NAME END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE LEVEL3_NAME END 'LEVEL3_NAME', 
       ID 'PRMID'        
     FROM #Table1 inner join Vals on #Table1.parentId=PRMID 

) 

SELECT * from Vals