sql >> Database >  >> RDS >> Mysql

Hoe maak je een MySQL hiërarchische recursieve query?

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 de from 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 de id . toevoegt naar de bovenliggende lijst, en dit zou alleen moeten gebeuren als de id voldoet aan de eerste voorwaarde. De length functie wordt alleen aangeroepen om ervoor te zorgen dat deze voorwaarde altijd waar is, zelfs als de pv 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.



  1. pip install mislukt met /usr/bin/clang:Geen dergelijk bestand of map

  2. Wijzig het datumformaat voor de huidige sessie in SQL Server

  3. Onmogelijk om PG gem op mijn mac te installeren met Mavericks

  4. Productbeoordeling - Stellaire reparatie voor MySQL