Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak utworzyć hierarchiczne rekurencyjne zapytanie MySQL?

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 , w przeciwnym razie wyniki nie będą kompletne.

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 z from 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ż dodaje id do listy nadrzędnej i powinno się to zdarzyć tylko wtedy, gdy id przechodzi pierwszy warunek. długość funkcja jest wywoływana tylko po to, aby upewnić się, że ten warunek jest zawsze spełniony, nawet jeśli pv 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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySQL:Szybki podział typów złączeń

  2. Jak wygenerować unikalny identyfikator w MySQL?

  3. MySQL - Jak wyszukać dokładne dopasowanie słów za pomocą LIKE?

  4. Zapytania hierarchiczne w MySQL

  5. Zapytanie sprzężenia MySQL za pomocą podobnego?