Mysql
 sql >> Database >  >> RDS >> Mysql

Come creare una query ricorsiva gerarchica MySQL?

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 dal from 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. La length la funzione viene chiamata solo per assicurarsi che questa condizione sia sempre vera, anche se pv 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à.