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

Hvordan opretter man en MySQL hierarkisk rekursiv forespørgsel?

Til MySQL 8+: brug den rekursive med syntaks.
For MySQL 5.x: bruge inline-variabler, sti-id'er eller selv-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;

Værdien angivet i parent_id =19 skal indstilles til id af den forælder, du vil vælge alle efterkommere af.

MySQL 5.x

For MySQL-versioner, der ikke understøtter almindelige tabeludtryk (op til version 5.7), vil du opnå dette med følgende forespørgsel:

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))

Her er en violin .

Her er værdien angivet i @pv :='19' skal indstilles til id af den forælder, du vil vælge alle efterkommere af.

Dette vil også fungere, hvis en forælder har flere børn. Det er dog påkrævet, at hver post opfylder betingelsen parent_id , ellers vil resultaterne ikke være fuldstændige.

Variable tildelinger i en forespørgsel

Denne forespørgsel bruger specifik MySQL-syntaks:variabler tildeles og ændres under udførelsen. Nogle antagelser er lavet om rækkefølgen af ​​udførelse:

  • fra klausul vurderes først. Så det er her @pv bliver initialiseret.
  • hvor klausulen evalueres for hver post i rækkefølgen for hentning fra fra aliaser. Så det er her, en betingelse sættes til kun at inkludere poster, for hvilke forælderen allerede var identificeret som værende i efterkommertræet (alle efterkommere af den primære forælder tilføjes gradvist til @pv ).
  • Betingelserne i denne hvor klausulen evalueres i rækkefølge, og evalueringen afbrydes, når det samlede resultat er sikkert. Derfor skal den anden betingelse være på andenpladsen, da den tilføjer id til forældrelisten, og dette bør kun ske, hvis id opfylder den første betingelse. længden funktionen kaldes kun for at sikre, at denne betingelse altid er sand, selv hvis pv streng ville af en eller anden grund give en falsk værdi.

Alt i alt kan man finde disse antagelser for risikable til at stole på. dokumentationen advarer:

du får muligvis de resultater, du forventer, men dette er ikke garanteret [...] rækkefølgen af ​​evaluering for udtryk, der involverer brugervariable, er udefineret.

Så selvom det fungerer i overensstemmelse med ovenstående forespørgsel, kan evalueringsrækkefølgen stadig ændre sig, for eksempel når du tilføjer betingelser eller bruger denne forespørgsel som en visning eller underforespørgsel i en større forespørgsel. Det er en "funktion", som vil blive fjernet i en fremtid MySQL-udgivelse :

Tidligere udgivelser af MySQL gjorde det muligt at tildele en værdi til en brugervariabel i andre sætninger end SET . Denne funktionalitet understøttes i MySQL 8.0 for bagudkompatibilitet, men kan fjernes i en fremtidig udgivelse af MySQL.

Som nævnt ovenfor skal du fra MySQL 8.0 og fremefter bruge den rekursive with syntaks.

Effektivitet

For meget store datasæt kan denne løsning blive langsom, da find_in_set operation er ikke den mest ideelle måde at finde et tal på en liste på, bestemt ikke på en liste, der når en størrelse i samme størrelsesorden som antallet af returnerede poster.

Alternativ 1:med rekursiv , tilslut med

Flere og flere databaser implementerer SQL:1999 ISO-standarden MED [RECURSIVE] syntaks til rekursive forespørgsler (f.eks. 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+ ). Og fra version 8.0 understøtter MySQL det . Se toppen af ​​dette svar for den syntaks, der skal bruges.

Nogle databaser har en alternativ, ikke-standard syntaks til hierarkiske opslag, såsom CONNECT BY klausul tilgængelig på Oracle , DB2 , Informix , CUBRID og andre databaser.

MySQL version 5.7 tilbyder ikke en sådan funktion. Når din databasemotor leverer denne syntaks, eller du kan migrere til en, der gør det, så er det bestemt den bedste mulighed at gå efter. Hvis ikke, så overvej også følgende alternativer.

Alternativ 2:Stilignende identifikatorer

Tingene bliver meget nemmere, hvis du tildeler id værdier, der indeholder den hierarkiske information:en sti. For eksempel, i dit tilfælde kunne dette se sådan ud:

ID NAVN
19 kategori1
19/1 kategori2
19/1/1 kategori3
19/1/1/1 kategori4

Derefter vælg ville se sådan ud:

select  id,
        name 
from    products
where   id like '19/%'

Alternativ 3:Gentagne selvforbindelser

Hvis du kender en øvre grænse for, hvor dybt dit hierarkitræ kan blive, kan du bruge en standard sql forespørgsel som denne:

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;

Se denne violin

hvor betingelse angiver, hvilken forælder du ønsker at hente efterkommere af. Du kan udvide denne forespørgsel med flere niveauer efter behov.



  1. Fjern alle mellemrum fra en streng i SQL Server

  2. Konverter varchar til datetime i SQL Server

  3. Oracle DateTime i Where-klausulen?

  4. SQL-punktnotation