ORDINE Gerarchico Albero Genitore-Figlio


21

Devo seguire i dati in SQL Server 2008 R2. SQLFiddle

Schema:

CREATE TABLE [dbo]. [ICFilters] (
   [ICFilterID] [int] IDENTITY (1,1) NOT NULL,
   [ParentID] [int] NOT NULL DEFAULT 0,
   [FilterDesc] [varchar] (50) NOT NULL,
   [Attivo] [tinyint] NOT NULL DEFAULT 1,
 VINCITORE [PK_ICFilters] CHIAVE PRIMARIA CLUSTERATA 
 ([ICFilterID] ASC) CON 
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON
 ) SU [PRIMARIO]
) SU [PRIMARIO]

INSERT INTO [dbo]. [ICFilters] (ParentID, FilterDesc, Active)
Valori 
(0, "Tipo di prodotto", 1),
(1, 'ProdSubType_1', 1),
(1, 'ProdSubType_2', 1),
(1, 'ProdSubType_3', 1),
(1, 'ProdSubType_4', 1),
(2, 'PST_1.1', 1),
(2, 'PST_1.2', 1),
(2, 'PST_1.3', 1),
(2, 'PST_1.4', 1),
(2, 'PST_1.5', 1),
(2, 'PST_1.6', 1),
(2, 'PST_1.7', 0),
(3, 'PST_2.1', 1),
(3, 'PST_2.2', 0),
(3, 'PST_2.3', 1),
(3, 'PST_2.4', 1),
(14, 'PST_2.2.1', 1),
(14, 'PST_2.2.2', 1),
(14, 'PST_2.2.3', 1),
(3, 'PST_2.8', 1)

Tavolo:

| ICFILTERID | PARENTID | FILTERDESC | ATTIVO |
--------------------------------------------------
| 1 | 0 | Tipo di prodotto | 1 |
| 2 | 1 | ProdSubType_1 | 1 |
| 3 | 1 | ProdSubType_2 | 1 |
| 4 | 1 | ProdSubType_3 | 1 |
| 5 | 1 | ProdSubType_4 | 1 |
| 6 | 2 | PST_1.1 | 1 |
| 7 | 2 | PST_1.2 | 1 |
| 8 | 2 | PST_1.3 | 1 |
| 9 | 2 | PST_1.4 | 1 |
| 10 | 2 | PST_1.5 | 1 |
| 11 | 2 | PST_1.6 | 1 |
| 12 | 2 | PST_1.7 | 0 |
| 13 | 3 | PST_2.1 | 1 |
| 14 | 3 | PST_2.2 | 0 |
| 15 | 3 | PST_2.3 | 1 |
| 16 | 3 | PST_2.4 | 1 |
| 17 | 14 | PST_2.2.1 | 1 |
| 18 | 14 | PST_2.2.2 | 1 |
| 19 | 14 | PST_2.2.3 | 1 |
| 20 | 3 | PST_2.8 | 1 |

Ogni riga ha l'ID del suo genitore e del root parentid = 0. Le FilterDescs sono solo descrizioni di esempio, quindi non posso provare a analizzarle per l'ordinazione.

La domanda

È possibile selezionare tutte le righe in modo simile ad un albero? Se é cosi, come? Quando dico "simile ad un albero", intendo selezionare ricorsivamente il genitore seguito da tutti i suoi figli, quindi da tutti i figli di ognuno di questi e così via. Una prima traversata dell'albero di profondità.

Io e i miei amici abbiamo provato, ma non siamo riusciti a trovare soluzioni efficaci ma continueremo a provare. Sono abbastanza nuovo di sql, quindi forse questo può essere fatto facilmente e sto solo rendendo le cose più difficili del necessario.

Esempio di output (desiderato):

| ICFILTERID | PARENTID | FILTERDESC | ATTIVO |
--------------------------------------------------
| 1 | 0 | Tipo di prodotto | 1 |
| 2 | 1 | ProdSubType_1 | 1 |
| 6 | 2 | PST_1.1 | 1 |
| 7 | 2 | PST_1.2 | 1 |
| 8 | 2 | PST_1.3 | 1 |
| 9 | 2 | PST_1.4 | 1 |
| 10 | 2 | PST_1.5 | 1 |
| 11 | 2 | PST_1.6 | 1 |
| 12 | 2 | PST_1.7 | 0 |
| 3 | 1 | ProdSubType_2 | 1 |
| 13 | 3 | PST_2.1 | 1 |
| 14 | 3 | PST_2.2 | 0 |
| 17 | 14 | PST_2.2.1 | 1 |
| 18 | 14 | PST_2.2.2 | 1 |
| 19 | 14 | PST_2.2.3 | 1 |
| 15 | 3 | PST_2.3 | 1 |
| 16 | 3 | PST_2.4 | 1 |
| 20 | 3 | PST_2.8 | 1 |
| 4 | 1 | ProdSubType_3 | 1 |
| 5 | 1 | ProdSubType_4 | 1 |

Sarebbe meglio usare CTE
Kin Shah

1
Ecco un thread che mostra l'ordinamento dei risultati desiderato senza che i dati della tabella debbano essere caricati in un ordine specifico. Utilizza row_number () e partizione per creare un "percorso" che abilita l'ordinamento desiderato. ask.sqlservercentral.com/questions/48518/…

Risposte:


25

OK, abbastanza cellule cerebrali sono morte.

SQL Fiddle

WITH cte AS
(
  SELECT 
    [ICFilterID], 
    [ParentID],
    [FilterDesc],
    [Active],
    CAST(0 AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters]
  WHERE [ParentID] = 0
  UNION ALL
  SELECT 
    i.[ICFilterID], 
    i.[ParentID],
    i.[FilterDesc],
    i.[Active],  
    Level + CAST(i.[ICFilterID] AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters] i
  INNER JOIN cte c
    ON c.[ICFilterID] = i.[ParentID]
)

SELECT 
  [ICFilterID], 
  [ParentID],
  [FilterDesc],
  [Active]
FROM cte
ORDER BY [Level];

2
Questo è esattamente quello di cui avevo bisogno! Sono d'accordo che troppe cellule cerebrali sono morte per questo. Non ero chiaro in quello che volevo? In tal caso, modificherò la domanda per riferimento futuro. Lo stavo decisamente rendendo più difficile di quanto fosse necessario ...
Arcangelo

1
@ Archangel33 Hai fatto un buon lavoro nel definire il problema e ciò di cui hai bisogno. Inoltre, sqlfiddle aiuta davvero.
Travis,

2
+1 ma utilizzando [ICFilterID] [int] IDENTITY (1,1) per l'ordinamento funzionerà solo se gli articoli vengono inseriti nella sequenza corretta, ma un altro campo per l'ordinamento non è ancora implementato da OT
bummi

4
Non credo che questa sia una soluzione corretta al 100%. Altrimenti elenca tutte le righe con il loro livello corretto nella gerarchia, non le elenca nell'ordine in cui è stata posta la domanda. Sarebbe possibile elencare le righe nell'ordine corretto secondo la domanda? Questo è quello che sto cercando anch'io.

1
Questo risponde alla mia domanda poiché i dati forniti nella [FilterDesc]colonna sono fittizi e quell'ordine non è necessario / non importante. Seguendo la logica nella risposta di @Travis Gan tutto ciò che uno deve fare per ottenere questo ordine è aggiungerne un altro CASTal Level. per esempio. Level + CAST( CAST(i.[ICFilterID] AS varbinary(max)) AS LevelDiventa Level + CAST(i.[FilterDesc] AS varbinary(max)) + CAST(i.[ICFilterID] AS varbinary(max)) AS Level.
Arcangelo33

1

Quanto sopra non sembra funzionare correttamente per me. Immagina una configurazione a 2 tabelle con dati di tipo facebook. La tabella 1 ha PostId + altri campi. PostId è l'incremento automatico e ovviamente nella tua interfaccia, ordinerai DESC per avere l'ultimo post in alto.

Ora per la tabella dei commenti. Tabella 2 Questa tabella CommentId è la chiave primaria, il numero automatico. Nella tua interfaccia grafica, vuoi visualizzarlo ASC, in modo che quando leggi il thread, abbia senso. (più vecchio (numero più piccolo) in alto) Altre chiavi importanti nella tabella 2 sono: PostId (FK torna ai post) e ParentId (da FK a CommentId) dove ParentId sarà NULL se questo è il commento "root" su un Post. Se qualcuno RISPONDE su un commento, allora parentId verrà popolato con commentid.
Spero che voi ragazzi prendiate la deriva. Il CTE sarà simile a questo:

WITH  Comments
        AS ( SELECT  CommentId , ParentId, CAST(CommentId AS VARBINARY(MAX)) AS Sortkey, 0 AS Indent
             FROM    dbo.Comments
             WHERE   ParentId IS NULL AND PostId = 105
             UNION ALL
             SELECT  b.CommentId , b.ParentId,  c.Sortkey + CAST(b.CommentId AS varbinary(max))  AS Sortkey, c.Indent + 1 AS Indent
             FROM    dbo.Comments b
             INNER JOIN Comments c ON c.CommentId = b.ParentId
           )
   SELECT   *
   FROM     Comments
   ORDER BY Sortkey

Uscita campione

1   NULL    0x0000000000000001  0
5   1   0x00000000000000010000000000000001  1
6   5   0x000000000000000100000000000000010000000000000005  2
2   NULL    0x0000000000000002  0

Sul post 105 di F / B, c'erano due commenti (CommentIds 1 e 2) Qualcuno ha quindi risposto su Comment1 (CommentId 5, ParentId 1), e poi qualcun altro ha commentato quella risposta, quindi su Comment5 (CommentId 6, ParentId 6)

E viola, la sequenza è corretta, sotto il post, ora puoi mostrare i commenti nella sequenza giusta. Per indentare i post in modo che si formino e contorni come in Facebook (più profondo è il livello, più deve essere marginato da sinistra), ho anche una colonna chiamata Rientro. Le radici sono 0 e quindi nell'unione, abbiamo c.Indent + 1 AS Indent Nel codice, ora puoi moltiplicare il rientro con let assumiamo 32px e mostrare i commenti in una bella gerarchia e struttura.

Non vedo alcun problema nell'utilizzare la chiave primaria di incremento automatico CommentId come forza motrice per la creazione del mio SortKey, in quanto vi è una migliore modifica delle date di incasinamento (commentdate) rispetto alla confusione di una chiave gestita del database che esegue il seeding con +1


0
create table pc ( parent varchar(10), child varchar(10) )

insert into pc values('a','b');
insert into pc values('a','c');
insert into pc values('b','e');
insert into pc values('b','f');
insert into pc values('a','d');
Insert into pc values('b','g');
insert into pc values('c','h');
insert into pc values('c','i');
insert into pc values('d','j');
insert into pc values('f','k');
insert into pc values('x','y');
insert into pc values('y','z');
insert into pc values('m','n');

 DECLARE @parent varchar(10) = 'a';
 WITH cte AS
 (
  select null parent, @parent child, 0 as level
   union
  SELECT  a.parent, a.child , 1 as level
    FROM pc a
   WHERE a.parent = @parent
   UNION ALL
  SELECT a.parent, a.child , c.level +    1
  FROM pc a JOIN cte c ON a.parent = c.child
  )
  SELECT distinct parent, child , level
  FROM cte
  order by level, parent

Questo ti darà tutti i discendenti e il livello.
Spero che sia di aiuto :)

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.