2010-02-23 8 views
6

Sto lavorando con una struttura ad albero in MySQL che è rappresentata utilizzando il modello di insiemi nidificati.Come selezionare i bambini e gli antenati immediati tutti nella stessa query

Spero che alcuni di voi esperti di sql possano aiutarmi a creare una query SELECT.

Mi piacerebbe essere in grado di abbinare un set di nodi usando LIKE. Per ogni nodo che è abbinato, ho anche bisogno di un elenco delimitato da virgole degli antenati di quel nodo e di un elenco delimitato da virgole dei figli immediati di quel nodo.

Non sono sicuro di dove cominciare, se una cosa del genere è possibile anche in una singola query. (Attualmente sto realizzando questo con una query all'interno di un ciclo.) Quello che spero è un set di risultati che potrebbe assomigliare a questo ...

A partire dalla stringa "qu" e interrogare la tabella " Corpo "I get ...

Node  | Parent Nodes    | Immediate Children 
Quads  Leg, Lower Body, Muslces  Vastus Lateralus, Vastus Medialis, Rectus Femoris 
Obliques Core, Trunk, Muscles   Inner obliques, outer obliques 

Qualsiasi suggerimento su come eseguire questo senza query in loop sarebbe molto apprezzato.

+0

Io attualmente non ho tempo di scrivere una risposta completa, ma se si ha accesso al libro SQL Cookbook, il capitolo sulla query gerarchiche copre utilizzando MySQL per risolvere questo problema. Per MySQL, è necessario conoscere la profondità del ramo per utilizzare le query, ma questo non è un problema: sai quanto vuoi andare in profondità. La soluzione non è super-banale, ma è possibile. –

+0

@sheepsimulator - grazie, vedrò se riesco a rintracciare quel libro. – Travis

+0

sheepsimulator, non sono d'accordo. è banale da risolvere se sai quale profondità hai bisogno - il Santo Graal lo fa per profondità arbitraria. O meglio non santo graal, ma sai, la profondità arbitraria è ciò che rende questo problema interessante IMO –

risposta

0

mentre sono d'accordo con nickf che questo è sporco e cattivo, è ancora divertente, quindi ecco qui:

SELECT  base.left_id, base.ancestors, 
      GROUP_CONCAT(children.left_id) children 
FROM  (
      SELECT  base.left_id 
      ,   GROUP_CONCAT(ancestors.left_id) ancestors 
      FROM  nested_set base 
      LEFT JOIN nested_set ancestors 
      ON   base.left_id  BETWEEN ancestors.left_id 
              AND ancestors.right_id 
      WHERE  base.name LIKE '%criteria%' 
      GROUP BY base.left_id 
      ) base          
LEFT JOIN nested_set children 
ON   children.left_id BETWEEN base.left_id 
           AND base.right_id 
LEFT JOIN nested_set inbetween 
ON   inbetween.left_id BETWEEN base.left_id 
           AND base.right_id 
AND  children.left_id BETWEEN inbetween.left_id 
           AND inbetween.right_id  
WHERE  inbetween.left_id IS NULL 
GROUP BY base.left_id 

In sostanza, il trucco è quello di risolvere in due fasi: in primo luogo, a risolvere il problema antenati, e schiacciare gli antenati in una lista con, quindi, utilizzare questo risultato per risolverlo per i bambini.

La parte degli antenati è relativamente semplice, è la sottoquery nella clausola from nella mia soluzione. I bambini sono un po 'più difficili. Funziona prendendo tutti i discendenti e quindi richiedendo che non esistano nodi tra il nodo base e i discendenti, il che in pratica limita i discendenti solo ai bambini.

Esistono altre varianti a questa strategia per risolvere questo problema, ad esempio è possibile eseguire prima i bambini e risolvere gli antenati utilizzando una sottoquery nell'elenco SELECT.

+0

Grazie per la tua risposta. Trascorrerò del tempo a lavorare con il tuo snippet di codice sopra per vedere se riesco a farlo funzionare per me. Per inciso, suggerisci anche che questa sarebbe una cosa negativa da implementare realmente. È davvero una cattiva idea? Devo supporre che una soluzione come questa sarà * significativamente * più veloce del ciclo di query più piccole. Grazie ancora. – Travis

+0

Beh, il fatto è che il set annidato è una vera seccatura per avere ragione. Innanzi tutto per conservare i dati in questa struttura, quindi per interrogarli. Ciò che in particolare non mi piace è che un piccolo cambiamento in basso a sinistra nell'albero riscrive essenzialmente l'intero albero in alto ea destra di quel cambiamento locale: un incubo di scalabilità. Poi guardo gli alberi tipici con cui mi occupo: sono in genere piuttosto piccoli, per lo più letti e relativamente statici. Di solito finisco con una lista di adiacenze + codice app, o un percorso materializzato, o una tabella di chiusura a seconda di cosa ho bisogno al momento. –

+0

Ho dovuto cambiare un paio di piccole cose per farlo funzionare con il mio setup, ma funziona come un fascino. Poiché la velocità è una grande preoccupazione per me, userò questo. Grazie ancora. – Travis

0

In una query? Non mi preoccuperei L'SQL sarebbe orrendo e probabilmente nemmeno così efficiente. Dividi ogni bit in query logiche più piccole: prima trova tutti i nodi corrispondenti, poi per ognuno di essi, le informazioni aggiuntive che ti servono.

+0

Questo è quello che sto facendo attualmente. Inizio selezionando i nodi che corrispondono usando LIKE. Quindi faccio un ciclo e li interrogano separatamente per antenati e figli su ogni iterazione. A seconda delle dimensioni del set di risultati iniziale, possono esserci molte domande ... – Travis

+2

ok, capisco, ma allora? Preferirei ancora leggere/mantenere diverse query più piccole che eseguono una singola funzione ben compresa, piuttosto che un singolo behemoth che è illeggibile per tutti tranne i super guru. – nickf

+0

Beh, non sono un super guru. :) Sto solo cercando di ottimizzare il mio SQL. Ho sempre capito che non appena si avvia il ciclo delle query SQL, il disastro è dietro l'angolo. – Travis

0

Non sono sicuro al 100% di ciò che si desidera, ma se ho capito bene, è possibile ottenere ciò utilizzando uno schema di database normalizzato e sottoquery.

Ad esempio:

Tabella "nodi" tabella "node_parents"

La tabella "nodi" sarebbe memorizzare tutti i nodi, e "node_parents" sarebbe mappare le relazioni tra diversi nodi.

Quindi, quando si seleziona LIKE un determinato nodo, è possibile acquisire tutti i relativi genitori e figli da node_parents.

È possibile recuperare le informazioni aggiuntive utilizzando join o sottoquery.

+0

Il problema è che puoi gestire solo un livello con quell'approccio. –

+0

in secondo luogo, pensò, sì questo è disordinato, non sarebbe in grado di farlo usando solo 1 query afaik. avresti bisogno almeno di usare una stored procedure per fare il ciclo –

0

Questa domanda è molto più difficile di quanto mi aspettassi nel tuo altro post, ma non sono d'accordo con la risposta del primo post.

Sono abbastanza sicuro che è possibile con una singola query.

È necessario utilizzare SQUADRE e selezionare. Hai mai guardato la demo davvero buona sul sito mySQL sul modello di lista di adiacenza.

Poiché è possibile LIKE per il "nodo", è possibile utilizzare le query secondarie per la query SQL per ottenere tutti i genitori e il genitore. Una delle mie domande su cui ho fatto qualcosa del genere era assolutamente massiccia! Ma ha funzionato.

Date un'occhiata: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

Questo è solo un piccolo frammento di codice che mostra come i bambini immediati è trovato.

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth 
FROM nested_category AS node, 
    nested_category AS parent, 
    nested_category AS sub_parent, 
    (
     SELECT node.name, (COUNT(parent.name) - 1) AS depth 
     FROM nested_category AS node, 
     nested_category AS parent 
     WHERE node.lft BETWEEN parent.lft AND parent.rgt 
     AND node.name = 'PORTABLE ELECTRONICS' 
     GROUP BY node.name 
     ORDER BY node.lft 
    )AS sub_tree 
WHERE node.lft BETWEEN parent.lft AND parent.rgt 
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt 
    AND sub_parent.name = sub_tree.name 
GROUP BY node.name 
HAVING depth <= 1 
ORDER BY node.lft 
Problemi correlati