2011-11-20 14 views
9

Supponiamo di avere una tabella con relazioni padre-figlio.Ricerca di tutti i figli per più genitori in una singola query SQL

 
parent child 
1  4 
1  5 
2  6 
3  7 
4  8 
6  9 
7  10 
8  11 

ora ho una query che restituisce una lista di persone (ad esempio, 1 e 2.) E voglio trovare tutti i loro figli, nipoti, ecc (in questo caso: 4, 5, 6, 8, 9, 11).

So che posso utilizzare le espressioni di tabella comuni per cercare in modo ricorsivo, ma mi chiedevo se potevo creare un'istruzione SQL per trovare tutti i discendenti contemporaneamente senza dover scorrere il set di input.

Modifica: scusa per non essere abbastanza chiaro. Sto cercando qualcosa di simile:

 
SELECT {Hierarchical relation} from table where parent in (1,2) 

che dovrebbe sfociare in una colonna singola uscita con le righe per 4, 5, 6, 8, 9, 11.

non ho perso con più interessato al relazione nell'output, solo il set completo di membri della famiglia per più famiglie.

+0

L'output deve mostrare la loro relazione (figlio, nipote, ecc.) O semplicemente discendere? – UnhandledExcepSean

+0

Potresti fornirci un esempio dell'output previsto nel formato tabella/riga-colonna proprio come hai illustrato la tabella con le relazioni genitore-figlio? – Nonym

+0

Nonym, ho aggiunto l'output previsto alla domanda. – MvdD

risposta

16

Qui è

---- PlainTable ---- 
parent idElement (child) 
Null 1 
1  4 
1  5 
2  6 
3  7 
4  8 
6  9 
7  10 
8  11 

WITH tableR (parent, idElement) 
AS 
(
-- Anchor member definition 
    SELECT e.parent, e.idElement 
    FROM PlainTable AS e 
    WHERE parent in (1,2) 
    UNION ALL 
-- Recursive member definition 
    SELECT e.parent, e.idElement 
    FROM PlainTable AS e 
    INNER JOIN tableR AS d 
     ON e.parent = d.idElement 
) 
-- Statement that executes the CTE 
SELECT idElement 
FROM tableR --inner join to plain table by id if needed 
+0

Questo CTE ricerca la relazione gerarchica come volevo, ma solo per un singolo input. Vedi la mia modifica nella domanda. – MvdD

+0

La prima query di CTE cerca nodi root, presumo che i nodi root abbiano NULL come padre. Changhe questa query per altri criteri. – danihp

+0

Prova la tua query nello studio di gestione SQL. Trova tutti i discendenti per il genitore IS NULL, che sono (1, 4, 5, 8 e 11) e quindi restituisce solo 1 da quel set di risultati. Come accennato nella domanda sopra, l'output dovrebbe essere una colonna con (1, 2, 4, 5, 6, 8, 9, 11), che sono tutti discendenti per entrambi i genitori 1 e 2. – MvdD

0

SQL Server 2008 ha costruito nelle caratteristiche per facilitare dati gerarchici: http://msdn.microsoft.com/en-us/magazine/cc794278.aspx

So che posso usare espressioni di tabella comuni per la ricerca in modo ricorsivo, ma io chiesto se ho potuto creare un'istruzione SQL per trovare tutti i discendenti a una volta senza dover iterare sul set di input.

Non sono sicuro di cosa intendi. La maggior parte (forse tutte?) Delle CTE può essere realizzata attraverso l'uso di sottoquery, ma l'utilizzo di sottoquery non sarebbe più rapido. Quando dici di non voler "scorrere" il set di input, sembra che tu stia parlando dell'uso dei cursori e, naturalmente, puoi farlo come operazione basata su set (usando CTE o sottoquery) ma non c'è modo per aggirare la ricorsione.

Modifica: Mi dispiace, non sto pensando direttamente ... ovviamente non è possibile eseguire la ricorsione con sottoquery normali, ma il punto continua a sostenere che anche se fosse possibile non sarebbe più veloce. Se ti piacerebbe vedere le strategie per fare la ricorsione senza CTE, prova a cercare qualcosa come "recursion sql 2000" dal momento che le CTE non erano in giro allora. Ecco alcuni esempi: http://www.mssqltips.com/sqlservertip/938/recursive-queries-with-sql-server-2000/. Naturalmente, la risposta alla tua domanda rimane la stessa però.

0

In attesa di un posto aggiornamento:

SELECT DISTINCT 
    p.parent AS parent 
, c.child AS child 
, IFNULL(g.child, 'NONE') AS grandchild_of_parent 
FROM parent_child as p 
LEFT JOIN parent_child AS c ON p.parent = c.parent 
LEFT JOIN parent_child AS g ON c.child = g.parent; 

risultati sarebbero simile a questa:

parent child grandchild_of_parent 
1  4  8 
1  5  NONE 
2  6  9 
3  7  10 
4  8  11 
6  9  NONE 
7  10  NONE 
8  11  NONE 

Tale ingenuo-ma-forse-più difficile da mantenere tipo di codice, ma poiché non ho familiarità con le funzionalità incorporate di SQL Server 2008 per gestire questo tipo di richiesta, mi limiterò a lanciare un campo lungo ...

MODIFICA:

Solo così è possibile vedere i risultati per te stesso, mentre si studia common table expressions e/o forse pivots ... questo sarà ottenere i risultati, ma solo fino ai pronipoti di 1 e 2.

-- A. Parents 1 and 2 
SELECT DISTINCT p.parent FROM parent_child AS p 
WHERE p.parent IN (1,2) 
UNION 
-- B : Children of A 
SELECT DISTINCT p.child FROM parent_child AS p 
WHERE p.parent IN (1,2) 
UNION 
-- C : Children of B, Grandchildren of A 
SELECT DISTINCT p.child FROM parent_child AS p 
WHERE p.parent IN (
    SELECT DISTINCT p.child FROM parent_child AS p 
    WHERE p.parent IN (1,2) 
) 
UNION 
-- D : Children of C, Great-Grandchildren of A 
SELECT DISTINCT p.child FROM parent_child AS p 
WHERE p.parent IN (
    SELECT DISTINCT p.child FROM parent_child AS p 
    WHERE p.parent IN (
    SELECT DISTINCT p.child FROM parent_child AS p 
    WHERE p.parent IN (1,2) 
) 
) 

Ancora una volta, ho suggerisco caldamente di studiare ciò che gli altri hanno pubblicato .. e di esaminare i link che hanno fornito. L'inelegante query che ti ho fornito non durerà a lungo-> sarà assolutamente FAIL una volta che avrai dei pronipoti.

+0

Sto cercando una singola colonna con tutti gli ID correlati. – MvdD

+0

BTW, IFNULL deve essere ISNULL nella query precedente per SQL Server. – MvdD

0

Ok, la soluzione di danihp mi ha messo sulla strada giusta. Questa è la soluzione mi è venuta:

DECLARE @Input TABLE (
    id int 
) 

INSERT INTO @Input VALUES (1),(2) 

;WITH Relations (parent, child) 
AS 
(
    SELECT e.parent, e.child 
     FROM RelationTable AS e 
     WHERE parent in (SELECT * FROM @Input) 
    UNION ALL 
     SELECT e.parent, e.child 
     FROM RelationTable AS e 
     INNER JOIN Relations AS d 
      ON e.parent = d.child 
) 
SELECT child 
FROM Relations 

Essa si traduce in un elenco di ID figlio (esclusi i 2 ids genitore come ho detto in precedenza in questione): 4,5,6,8,9, 11

+0

oh! Scusate! Il mio errore, "WHERE idElement in (1,2)" potrebbe apparire nella prima query non nell'ultima! Io lo aggiusterò. – danihp

0
---- PlainTable ---- 
parent idElement (child_id) 
    Null 1 
    1  4 
    1  5 
    2  6 
    3  7 
    4  8 
    6  9 
    7  10 
    8  11 

**Table value function to get Child ids at 4(any) Level of parent in the same table:-** 

    FUNCTION fc_get_Child_IDs(Parent) 
    DECLARE @tbl TABLE (ID int) 
    DECLARE @level int=4 
    DECLARE @i int=1 
    insert into @tbl values (Parent) 
    while @i < @level 
    BEGIN 

    INSERT into @tbl 
    select child_id from PlainTable where Parent in (select ID from @tbl) and child_id not in (select ID from @tbl) 
    set @i = @i + 1 
    END 
Problemi correlati