Dla MySQL 8+: użyj rekurencyjnego with
składnia.
Dla MySQL 5.x: używaj zmiennych wbudowanych, identyfikatorów ścieżek lub połączeń automatycznych.
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;
Wartość określona w parent_id =19
powinien być ustawiony na id
rodzica, którego chcesz wybrać wszystkich potomków.
MySQL 5.x
W przypadku wersji MySQL, które nie obsługują wspólnych wyrażeń tabel (do wersji 5.7), można to osiągnąć za pomocą następującego zapytania:
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))
Oto skrzypce .
Tutaj wartość określona w @pv :='19'
powinien być ustawiony na id
rodzica, którego chcesz wybrać wszystkich potomków.
To zadziała również, jeśli rodzic ma wiele dzieci. Wymagane jest jednak, aby każdy rekord spełniał warunek parent_id
Przypisania zmiennych w zapytaniu
To zapytanie używa specyficznej składni MySQL:zmienne są przypisywane i modyfikowane podczas jego wykonywania. Przyjmuje się pewne założenia dotyczące kolejności wykonywania:
z
klauzula jest oceniana jako pierwsza. Więc to tutaj@pv
zostanie zainicjowany.gdzie
klauzula jest oceniana dla każdego rekordu w kolejności pobierania zfrom
skróty. Tak więc jest to sytuacja, w której warunek jest uwzględniony tylko w rekordach, dla których rodzic został już zidentyfikowany jako znajdujący się w drzewie potomków (wszyscy potomkowie rodzica głównego są stopniowo dodawani do@pv
).- Warunki w tym
gdzie
klauzule są oceniane w kolejności, a ocena jest przerywana, gdy całkowity wynik jest pewny. Dlatego drugi warunek musi być na drugim miejscu, ponieważ dodajeid
do listy nadrzędnej i powinno się to zdarzyć tylko wtedy, gdyid
przechodzi pierwszy warunek.długość
funkcja jest wywoływana tylko po to, aby upewnić się, że ten warunek jest zawsze spełniony, nawet jeślipv
string z jakiegoś powodu da fałszywą wartość.
W sumie można uznać te założenia za zbyt ryzykowne, aby na nich polegać. dokumentacja ostrzega:
możesz uzyskać oczekiwane wyniki, ale nie jest to gwarantowane [...] kolejność oceny dla wyrażeń zawierających zmienne użytkownika jest niezdefiniowana.
Tak więc, mimo że działa ono konsekwentnie z powyższym zapytaniem, kolejność oceny może nadal się zmieniać, na przykład po dodaniu warunków lub użyciu tego zapytania jako widoku lub podzapytania w większym zapytaniu. Jest to „funkcja”, która zostanie usunięta w przyszłości Wersja MySQL :
Poprzednie wersje MySQL umożliwiały przypisanie wartości do zmiennej użytkownika w instrukcjach innych niż SET
. Ta funkcja jest obsługiwana w MySQL 8.0 w celu zapewnienia kompatybilności wstecznej, ale może zostać usunięta w przyszłej wersji MySQL.
Jak wspomniano powyżej, począwszy od MySQL 8.0 powinieneś używać rekurencyjnego with
składnia.
Wydajność
W przypadku bardzo dużych zestawów danych to rozwiązanie może działać wolniej, ponieważ find_in_set
operacja nie jest najlepszym sposobem na znalezienie liczby na liście, a już na pewno nie na liście, która osiąga rozmiar w tym samym rzędzie wielkości, co liczba zwróconych rekordów.
Alternatywna 1:z rekurencyjną
, połącz przez
Coraz więcej baz danych wdraża Standard ISO SQL:1999 Z [RECURSIVE] składnia
dla zapytań rekurencyjnych (np. 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 wersji 8.0, obsługuje ją również MySQL
. Zobacz górną część tej odpowiedzi, aby poznać składnię, której należy użyć.
Niektóre bazy danych mają alternatywną, niestandardową składnię do wyszukiwania hierarchicznego, na przykład CONNECT BY
klauzula dostępna na Oracle
, DB2
, Informix , CUBRID
i inne bazy danych.
MySQL w wersji 5.7 nie oferuje takiej funkcji. Jeśli twój silnik bazy danych zapewnia taką składnię lub możesz przeprowadzić migrację do takiej, która ją obsługuje, to z pewnością jest to najlepsza opcja. Jeśli nie, rozważ również następujące alternatywy.
Alternatywna 2:Identyfikatory w stylu ścieżki
Sprawy stają się o wiele łatwiejsze, jeśli przypiszesz id
wartości, które zawierają informacje hierarchiczne:ścieżka. Na przykład w twoim przypadku może to wyglądać tak:
ID | NAZWA |
---|---|
19 | kategoria1 |
19/1 | kategoria2 |
19/1/1 | kategoria3 |
19/1/1/1 | kategoria4 |
Następnie Twój wybierz
wyglądałby tak:
select id,
name
from products
where id like '19/%'
Alternatywna 3:powtarzające się samopołączenia
Jeśli znasz górną granicę głębokości, w jakiej może osiągnąć twoje drzewo hierarchii, możesz użyć standardowego sql
zapytanie w ten sposób:
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;
Zobacz to skrzypce
gdzie
warunek określa, którego rodzica chcesz pobrać potomków. W razie potrzeby możesz rozszerzyć to zapytanie o więcej poziomów.