2016-03-02 12 views
5

Ho una tabella comeCome posso scrivere una procedura che ottiene i livelli da I a J o un albero come questo?

 Users 
------------------------- 
id | ancestor_id | .... 
------------------------- 
1 | NULL  | .... 
2 |  1  | .... 
3 |  1  | .... 
4 |  3  | .... 
5 |  3  | .... 

che rappresentasse un albero come

level 1   1 
       /\ 
    level 2  2 3 
        /\ 
    level 3   4 5 

e voglio creare una procedura che restituisce il i ° attraverso j ° generazione di discendenti di un dato utente:

CREATE PROCEDURE DescendantsLevel 
    @user_id INT, 
    @i INT, 
    @j INT 
AS 
    .... 

Se @j è NULL, però, restituisce tutti i discendenti a partire dalla generazione @i.

Esempi:

EXEC DescendantLevel @user_id=1,@i=2,@j=NULL 

sarebbe tornato

------------------------- 
id | ancestor_id | .... 
------------------------- 
1 | NULL  | .... 
2 |  1  | .... 
3 |  1  | .... 
4 |  3  | .... 
5 |  3  | .... 

e

EXEC DescendantLevel @user_id=1,@i=1,@j=2 

sarebbe tornato

 Users 
------------------------- 
id | ancestor_id | .... 
------------------------- 
1 | NULL  | .... 
2 |  1  | .... 
3 |  1  | .... 

Molte domande, ho:

  • C'è un valore migliore rispetto NULL per rappresentare qualche concetto di "infinito" in SQL?
  • Come posso implementare la procedura che ho descritto?
  • Esiste un modo migliore per progettare il database al fine di semplificare la procedura?
+2

Ricerca di codici ricorsivi. Sarà il modo più semplice per risolvere questo problema se utilizzi questo design. Si potrebbe anche considerare il modello di insiemi nidificati come un'alternativa migliore all'elenco di adiacenza. –

+0

Se '@ i' è 2 nel tuo primo esempio, perché viene restituito l'id # 1? –

+0

Perché non aggiungere una colonna di livello? Rende la query davvero semplice. – maraca

risposta

2

Usando una CTE ricorsiva:

DECLARE @test TABLE (id INT NOT NULL, ancestor_id INT NULL) 

DECLARE 
    @id INT = 1, 
    @i INT = 1, 
    @j INT = 2 

INSERT INTO @test (id, ancestor_id) 
VALUES 
    (1, NULL), 
    (2, 1), 
    (3, 1), 
    (4, 3), 
    (5, 3) 

;WITH CTE_Tree AS 
(
    SELECT 
     id, 
     ancestor_id, 
     1 AS lvl, 
     id AS base 
    FROM 
     @test 
    WHERE 
     id = @id 
    UNION ALL 
    SELECT 
     C.id, 
     C.ancestor_id, 
     P.lvl + 1 AS lvl, 
     P.base AS base 
    FROM 
     CTE_Tree P 
    INNER JOIN @test C ON C.ancestor_id = P.id 
    WHERE 
     lvl <= COALESCE(@j, 9999) 
) 
SELECT 
    id, 
    ancestor_id 
FROM 
    CTE_Tree 
WHERE 
    lvl BETWEEN @i AND COALESCE(@j, 9999) 

Questa si basa su non più di 9999 livelli di ricorsione (in realtà il limite predefinito sulla ricorsione per SQL Server è 100, quindi più di 100 livelli e ti ottenere un errore).

Problemi correlati