För MySQL 8+: använd den rekursiva med
syntax.
För MySQL 5.x: använd inline-variabler, sökvägs-ID:n eller självanslutningar.
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ärdet som anges i parent_id =19
bör ställas in på id
för den förälder du vill välja alla ättlingar till.
MySQL 5.x
För MySQL-versioner som inte stöder Common Table Expressions (upp till version 5.7), skulle du uppnå detta med följande fråga:
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))
Här är en fiol .
Här, värdet som anges i @pv :='19'
bör ställas in på id
för den förälder du vill välja alla ättlingar till.
Detta fungerar även om en förälder har flera barn. Det krävs dock att varje post uppfyller villkoret parent_id
Variabeltilldelningar i en fråga
Den här frågan använder specifik MySQL-syntax:variabler tilldelas och modifieras under dess körning. Vissa antaganden görs om exekveringsordningen:
från klausulen utvärderas först. Så det är där @pv
initieras.-
var satsen utvärderas för varje post i den ordning de hämtas från från
alias. Så det är här ett villkor ställs för att endast inkludera poster för vilka föräldern redan identifierats som i det efterkommande trädet (alla avkomlingar till den primära föräldern läggs successivt till i@pv
). - Villkoren i denna
var
klausulen utvärderas i ordning, och utvärderingen avbryts när det totala resultatet är säkert. Därför måste det andra villkoret vara på andra plats, eftersom det lägger tillid
till föräldralistan, och detta bör bara hända omid
klarar det första villkoret.längden
funktionen anropas bara för att säkerställa att detta villkor alltid är sant, även ompv
sträng skulle av någon anledning ge ett falskt värde.
Sammantaget kan man tycka att dessa antaganden är för riskabla att förlita sig på. dokumentationen varnar:
du kanske får de resultat du förväntar dig, men detta är inte garanterat [...] utvärderingsordningen för uttryck som involverar användarvariabler är odefinierad.
Så även om det fungerar konsekvent med ovanstående fråga, kan utvärderingsordningen fortfarande ändras, till exempel när du lägger till villkor eller använder den här frågan som en vy eller underfråga i en större fråga. Det är en "funktion" som kommer att tas bort i framtiden MySQL-version :
Tidigare versioner av MySQL gjorde det möjligt att tilldela ett värde till en användarvariabel i andra satser än
SET
. Den här funktionen stöds i MySQL 8.0 för bakåtkompatibilitet men kan tas bort i en framtida version av MySQL.
Som nämnts ovan, från MySQL 8.0 och framåt bör du använda den rekursiva with
syntax.
Effektivitet
För mycket stora datamängder kan denna lösning bli långsam, eftersom finn_i_uppsättning
operation är inte det mest idealiska sättet att hitta ett nummer i en lista, absolut inte i en lista som når en storlek i samma storleksordning som antalet poster som returneras.
Alternativ 1:med rekursiv
, anslut med
Fler och fler databaser implementerar SQL:1999 ISO-standarden Med [RECURSIVE] syntax
för rekursiva frågor (t.ex. 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+
). Och från och med version 8.0, stöder MySQL det
. Se överst i det här svaret för syntaxen som ska användas.
Vissa databaser har en alternativ, icke-standard syntax för hierarkiska uppslagningar, såsom CONNECT BY
klausul tillgänglig på Oracle
, DB2
, Informix , CUBRID
och andra databaser.
MySQL version 5.7 erbjuder inte en sådan funktion. När din databasmotor tillhandahåller denna syntax eller om du kan migrera till en som gör det, så är det definitivt det bästa alternativet att välja. Om inte, överväg också följande alternativ.
Alternativ 2:Sökvägsliknande identifierare
Saker och ting blir mycket enklare om du tilldelar id
värden som innehåller den hierarkiska informationen:en sökväg. Till exempel, i ditt fall kan detta se ut så här:
ID | NAMN |
---|---|
19 | kategori1 |
19/1 | kategori2 |
19/1/1 | kategori3 |
19/1/1/1 | kategori4 |
Sedan välj
skulle se ut så här:
select id,
name
from products
where id like '19/%'
Alternativ 3:Upprepade självanslutningar
Om du vet en övre gräns för hur djupt ditt hierarkiträd kan bli kan du använda en standard sql
fråga så här:
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 denna fiol