Per MySQL 8+: usa il ricorsivo with
sintassi.
Per MySQL 5.x: usa variabili inline, ID percorso o auto join.
MySQL 8+
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from products
where parent_id = 19
union all
select p.id,
p.name,
p.parent_id
from products p
inner join cte
on p.parent_id = cte.id
)
select * from cte;
Il valore specificato in parent_id = 19
dovrebbe essere impostato su id
del genitore di cui vuoi selezionare tutti i discendenti.
MySQL 5.x
Per le versioni di MySQL che non supportano le Common Table Expressions (fino alla versione 5.7), puoi ottenere questo risultato con la seguente query:
select id,
name,
parent_id
from (select * from products
order by parent_id, id) products_sorted,
(select @pv := '19') initialisation
where find_in_set(parent_id, @pv)
and length(@pv := concat(@pv, ',', id))
Ecco un violino .
Qui, il valore specificato in @pv := '19'
dovrebbe essere impostato su id
del genitore di cui vuoi selezionare tutti i discendenti.
Questo funzionerà anche se un genitore ha più bambini. Tuttavia, è necessario che ogni record soddisfi la condizione parent_id < id
, altrimenti i risultati non saranno completi.
Assegnazioni di variabili all'interno di una query
Questa query utilizza una sintassi specifica di MySQL:le variabili vengono assegnate e modificate durante la sua esecuzione. Vengono fatte alcune ipotesi sull'ordine di esecuzione:
- Il
from
la clausola viene valutata per prima. Ecco dove@pv
viene inizializzato. - Il
where
La clausola viene valutata per ogni record nell'ordine di recupero dalfrom
alias. Quindi è qui che viene inserita una condizione per includere solo i record per i quali il genitore era già stato identificato come nell'albero discendente (tutti i discendenti del genitore primario vengono progressivamente aggiunti a@pv
). - Le condizioni in questo
where
clausole vengono valutate in ordine e la valutazione viene interrotta una volta che il risultato totale è certo. Pertanto la seconda condizione deve essere al secondo posto, poiché aggiunge l'id
all'elenco dei genitori, e questo dovrebbe accadere solo se l'id
supera la prima condizione. Lalength
la funzione viene chiamata solo per assicurarsi che questa condizione sia sempre vera, anche sepv
string per qualche motivo produrrebbe un valore falso.
Tutto sommato, si possono trovare queste ipotesi troppo rischiose su cui fare affidamento. La documentazione avverte:
potresti ottenere i risultati che ti aspetti, ma ciò non è garantito [...] l'ordine di valutazione per le espressioni che coinvolgono variabili utente non è definito.
Pertanto, anche se funziona in modo coerente con la query precedente, l'ordine di valutazione potrebbe comunque cambiare, ad esempio quando si aggiungono condizioni o si utilizza questa query come vista o sottoquery in una query più ampia. È una "funzione" che verrà rimossa in futuro Versione MySQL :
Le versioni precedenti di MySQL consentivano di assegnare un valore a una variabile utente in istruzioni diverse da SET
. Questa funzionalità è supportata in MySQL 8.0 per la compatibilità con le versioni precedenti, ma è soggetta a rimozione in una versione futura di MySQL.
Come detto sopra, da MySQL 8.0 in poi dovresti usare il ricorsivo with
sintassi.
Efficienza
Per set di dati molto grandi questa soluzione potrebbe rallentare, poiché find_in_set
operazione non è il modo più ideale per trovare un numero in un elenco, certamente non in un elenco che raggiunge una dimensione dello stesso ordine di grandezza del numero di record restituiti.
Alternativa 1:with recursive
, connect by
Sempre più database implementano lo SQL:1999 ISO standard WITH [RECURSIVE]
sintassi
per query ricorsive (ad es. Postgres 8.4+
, SQL Server 2005+
, DB2
, Oracle 11gR2+
, SQLite 3.8.4+
, Firebird 2.1+
, H2
, HyperSQL 2.1.0+
, Teradata , MariaDB 10.2.2+
). E a partire dalla versione 8.0, anche MySQL lo supporta
. Vedi la parte superiore di questa risposta per la sintassi da usare.
Alcuni database hanno una sintassi alternativa e non standard per le ricerche gerarchiche, come CONNECT BY
clausola disponibile su Oracle
, DB2
, Informix , CUBRID
e altri database.
MySQL versione 5.7 non offre tale funzionalità. Quando il tuo motore di database fornisce questa sintassi o puoi migrare a uno che lo fa, allora questa è sicuramente l'opzione migliore da scegliere. In caso contrario, considera anche le seguenti alternative.
Alternativa 2:identificatori di stile di percorso
Le cose diventano molto più semplici se assegni id
valori che contengono le informazioni gerarchiche:un percorso. Ad esempio, nel tuo caso potrebbe assomigliare a questo:
ID | NOME |
---|---|
19 | categoria1 |
19/1 | categoria2 |
19/1/1 | categoria3 |
19/1/1/1 | categoria4 |
Quindi il tuo select
sarebbe simile a questo:
select id,
name
from products
where id like '19/%'
Alternativa 3:Auto-join ripetuti
Se conosci un limite superiore per quanto può diventare profondo il tuo albero della gerarchia, puoi utilizzare uno standard sql
query in questo modo:
select p6.parent_id as parent6_id,
p5.parent_id as parent5_id,
p4.parent_id as parent4_id,
p3.parent_id as parent3_id,
p2.parent_id as parent2_id,
p1.parent_id as parent_id,
p1.id as product_id,
p1.name
from products p1
left join products p2 on p2.id = p1.parent_id
left join products p3 on p3.id = p2.parent_id
left join products p4 on p4.id = p3.parent_id
left join products p5 on p5.id = p4.parent_id
left join products p6 on p6.id = p5.parent_id
where 19 in (p1.parent_id,
p2.parent_id,
p3.parent_id,
p4.parent_id,
p5.parent_id,
p6.parent_id)
order by 1, 2, 3, 4, 5, 6, 7;
Vedi questo violino
Il where
condizione specifica di quale genitore si desidera recuperare i discendenti. Puoi estendere questa query con più livelli secondo necessità.