Pro MySQL 8+: použijte rekurzivní with
syntaxe.
Pro MySQL 5.x: použijte vložené proměnné, ID cesty nebo vlastní spojení.
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;
Hodnota uvedená v parent_id = 19
by měl být nastaven na id
rodiče, jehož chcete vybrat všechny potomky.
MySQL 5.x
U verzí MySQL, které nepodporují Common Table Expressions (až do verze 5.7), byste toho dosáhli pomocí následujícího dotazu:
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))
Zde je housle .
Zde je hodnota zadaná v @pv := '19'
by měl být nastaven na id
rodiče, jehož chcete vybrat všechny potomky.
To bude fungovat i v případě, že rodič má více děti. Je však nutné, aby každý záznam splňoval podmínku parent_id < id
, jinak nebudou výsledky úplné.
Přiřazení proměnných uvnitř dotazu
Tento dotaz používá specifickou syntaxi MySQL:proměnné jsou přiřazovány a upravovány během jeho provádění. Existují určité předpoklady ohledně pořadí provádění:
from
klauzule se hodnotí jako první. Takže tam je@pv
se inicializuje.where
klauzule je vyhodnocena pro každý záznam v pořadí načtení zfrom
aliasy. Zde je tedy nastavena podmínka, aby byly zahrnuty pouze záznamy, u kterých byl rodič již identifikován jako ve stromu potomků (všichni potomci primárního rodiče jsou postupně přidáváni do@pv
).- Podmínky v tomto
where
klauzule jsou hodnoceny v pořadí a hodnocení je přerušeno, jakmile je celkový výsledek jistý. Druhá podmínka proto musí být na druhém místě, protože přidáváid
do nadřazeného seznamu a mělo by k tomu dojít pouze v případěid
splňuje první podmínku.length
funkce je volána pouze proto, aby se ujistil, že tato podmínka je vždy pravdivá, i kdyžpv
řetězec by z nějakého důvodu poskytl falešnou hodnotu.
Celkově vzato mohou být tyto předpoklady příliš riskantní, než aby se na ně spoléhalo. dokumentace varuje:
můžete získat očekávané výsledky, ale to není zaručeno [...] pořadí vyhodnocení výrazů zahrnujících uživatelské proměnné není definováno.
Takže i když to funguje konzistentně s výše uvedeným dotazem, pořadí vyhodnocení se může stále změnit, například když přidáte podmínky nebo použijete tento dotaz jako pohled nebo dílčí dotaz ve větším dotazu. Jedná se o „funkci“, která bude v budoucnu odstraněna Vydání MySQL :
Předchozí verze MySQL umožňovala přiřadit hodnotu uživatelské proměnné v jiných příkazech než
SET
. Tato funkce je podporována v MySQL 8.0 kvůli zpětné kompatibilitě, ale v budoucí verzi MySQL bude odstraněna.
Jak je uvedeno výše, od MySQL 8.0 byste měli používat rekurzivní with
syntaxe.
Účinnost
U velmi velkých souborů dat může být toto řešení pomalé, protože find_in_set
operace není nejideálnějším způsobem, jak najít číslo v seznamu, rozhodně ne v seznamu, který dosahuje velikosti řádově stejné jako počet vrácených záznamů.
Alternativa 1:with recursive
, connect by
Stále více databází implementuje SQL:1999 ISO standard WITH [RECURSIVE]
syntaxe
pro rekurzivní dotazy (např. 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+
). A od verze 8.0 to podporuje i MySQL
. Použitou syntaxi naleznete v horní části této odpovědi.
Některé databáze mají alternativní, nestandardní syntaxi pro hierarchické vyhledávání, jako je CONNECT BY
klauzule dostupná na Oracle
, DB2
, Informix , CUBRID
a další databáze.
MySQL verze 5.7 takovou funkci nenabízí. Když váš databázový stroj poskytuje tuto syntaxi nebo můžete migrovat na takový, který ano, pak je to určitě nejlepší volba. Pokud ne, zvažte také následující alternativy.
Alternativa 2:Identifikátory stylu cesty
Věci budou mnohem jednodušší, pokud přiřadíte id
hodnoty, které obsahují hierarchické informace:cesta. Ve vašem případě by to mohlo vypadat například takto:
ID | NAME |
---|---|
19 | kategorie1 |
19/1 | kategorie2 |
19/1/1 | kategorie3 |
19/1/1/1 | kategorie4 |
Poté select
bude vypadat takto:
select id,
name
from products
where id like '19/%'
Alternativa 3:Opakovaná vlastní připojení
Pokud znáte horní hranici hloubky vašeho hierarchického stromu, můžete použít standardní sql
dotaz takto:
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;
Podívejte se na tyto hudle
where
podmínka určuje, kterého rodiče chcete získat potomky. Tento dotaz můžete podle potřeby rozšířit o další úrovně.