Voor MySQL 8+: gebruik de recursieve with
syntaxis.
Voor MySQL 5.x: gebruik inline variabelen, pad-ID's of self-joins.
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;
De waarde gespecificeerd in parent_id = 19
moet worden ingesteld op de id
van de ouder waarvan u alle nakomelingen wilt selecteren.
MySQL 5.x
Voor MySQL-versies die geen ondersteuning bieden voor Common Table Expressions (tot versie 5.7), kunt u dit bereiken met de volgende 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))
Hier is een viool .
Hier de waarde gespecificeerd in @pv := '19'
moet worden ingesteld op de id
van de ouder waarvan u alle nakomelingen wilt selecteren.
Dit werkt ook als een ouder meerdere . heeft kinderen. Het is echter vereist dat elk record voldoet aan de voorwaarde parent_id < id
, anders zijn de resultaten niet compleet.
Variabele toewijzingen binnen een zoekopdracht
Deze query gebruikt specifieke MySQL-syntaxis:variabelen worden toegewezen en gewijzigd tijdens de uitvoering. Er worden enkele veronderstellingen gemaakt over de volgorde van uitvoering:
- De
from
clausule wordt eerst beoordeeld. Dus dat is waar@pv
wordt geïnitialiseerd. - De
where
clausule wordt geëvalueerd voor elk record in de volgorde van ophalen uit defrom
aliassen. Dit is dus waar een voorwaarde wordt gesteld om alleen records op te nemen waarvan de ouder al geïdentificeerd was als zijnde in de stamboom (alle nakomelingen van de primaire ouder worden progressief toegevoegd aan@pv
). - De voorwaarden in deze
where
clausule worden in volgorde geëvalueerd en de evaluatie wordt onderbroken zodra de totale uitkomst zeker is. Daarom moet de tweede voorwaarde op de tweede plaats staan, omdat het deid
. toevoegt naar de bovenliggende lijst, en dit zou alleen moeten gebeuren als deid
voldoet aan de eerste voorwaarde. Delength
functie wordt alleen aangeroepen om ervoor te zorgen dat deze voorwaarde altijd waar is, zelfs als depv
string zou om de een of andere reden een valse waarde opleveren.
Al met al kan men deze veronderstellingen te riskant vinden om op te vertrouwen. De documentatie waarschuwt:
u krijgt misschien de resultaten die u verwacht, maar dit is niet gegarandeerd [...] de volgorde van evaluatie voor expressies met gebruikersvariabelen is niet gedefinieerd.
Dus hoewel het consistent werkt met de bovenstaande query, kan de evaluatievolgorde nog steeds veranderen, bijvoorbeeld wanneer u voorwaarden toevoegt of deze query gebruikt als een weergave of subquery in een grotere query. Het is een "functie" die in een toekomst zal worden verwijderd MySQL-release :
Eerdere releases van MySQL maakten het mogelijk om een waarde toe te kennen aan een gebruikersvariabele in andere instructies dan SET
. Deze functionaliteit wordt ondersteund in MySQL 8.0 voor achterwaartse compatibiliteit, maar kan in een toekomstige release van MySQL worden verwijderd.
Zoals hierboven vermeld, moet u vanaf MySQL 8.0 de recursieve with
. gebruiken syntaxis.
Efficiëntie
Voor zeer grote datasets kan deze oplossing traag worden, omdat de find_in_set
bewerking is niet de meest ideale manier om een getal in een lijst te vinden, zeker niet in een lijst die een grootte bereikt in dezelfde orde van grootte als het aantal geretourneerde records.
Alternatief 1:with recursive
, connect by
Steeds meer databases implementeren de SQL:1999 ISO-standaard WITH [RECURSIVE]
syntaxis
voor recursieve zoekopdrachten (bijv. 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+
). En vanaf versie 8.0 ondersteunt MySQL het ook
. Zie de bovenkant van dit antwoord voor de te gebruiken syntaxis.
Sommige databases hebben een alternatieve, niet-standaard syntaxis voor hiërarchische zoekopdrachten, zoals de CONNECT BY
clausule beschikbaar op Oracle
, DB2
, Informix , CUBRID
en andere databases.
MySQL versie 5.7 biedt een dergelijke functie niet. Wanneer uw database-engine deze syntaxis biedt of u kunt migreren naar een die dat wel doet, dan is dat zeker de beste optie om voor te gaan. Zo niet, overweeg dan ook de volgende alternatieven.
Alternatief 2:padstijl-ID's
Dingen worden een stuk eenvoudiger als u id
. zou toewijzen waarden die de hiërarchische informatie bevatten:een pad. In uw geval zou dit er bijvoorbeeld als volgt uit kunnen zien:
ID | NAAM |
---|---|
19 | categorie1 |
19/1 | categorie2 |
19/1/1 | categorie3 |
19/1/1/1 | categorie4 |
Dan uw select
zou er als volgt uitzien:
select id,
name
from products
where id like '19/%'
Alternatief 3:herhaalde self-joins
Als u een bovengrens weet voor hoe diep uw hiërarchieboom kan worden, kunt u een standaard sql
gebruiken vraag als volgt:
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;
Zie deze viool
De where
voorwaarde specificeert van welke ouder u de nakomelingen wilt ophalen. U kunt deze zoekopdracht indien nodig uitbreiden met meer niveaus.