Mam tabelę MySQL, która jest następująca:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
Teraz chcę mieć jedno zapytanie MySQL, do którego po prostu podaję identyfikator [na przykład powiedz „id = 19”], a następnie powinienem uzyskać wszystkie jego identyfikatory potomne [tzn. Wynik powinien mieć identyfikatory „20, 21,22”]. ... Hierarchia dzieci nie jest znana, może się różnić ...
Mam też rozwiązanie korzystające z pętli for ... Daj mi znać, jak to osiągnąć, używając pojedynczego zapytania MySQL, jeśli to możliwe.
mysql
sql
hierarchical-data
recursive-query
Tarun Parswani
źródło
źródło
Odpowiedzi:
W przypadku MySQL 8+: użyj
with
składni rekurencyjnej .W przypadku MySQL 5.x: użyj zmiennych wbudowanych, identyfikatorów ścieżek lub połączeń wewnętrznych.
MySQL 8+
Podana wartość
parent_id = 19
powinna być ustawiona naid
wartość rodzica, dla którego chcesz wybrać wszystkich potomków.MySQL 5.x
W przypadku wersji MySQL, które nie obsługują typowych wyrażeń tabelowych (do wersji 5.7), można to osiągnąć za pomocą następującego zapytania:
Oto skrzypce .
Tutaj wartość podana w
@pv := '19'
powinna być ustawiona naid
wartość rodzica, dla którego chcesz wybrać wszystkich potomków.Działa to również wtedy, gdy rodzic ma wiele dzieci. Wymagane jest jednak, aby każdy rekord spełniał warunek
parent_id < id
, w przeciwnym razie wyniki nie będą kompletne.Zmienne przypisania w zapytaniu
To zapytanie używa określonej składni MySQL: zmienne są przypisywane i modyfikowane podczas jego wykonywania. Poczyniono pewne założenia dotyczące kolejności wykonania:
from
Klauzula jest oceniana jako pierwszy. To właśnie tam@pv
zostaje zainicjowany.where
Klauzula jest obliczane dla każdego rekordu w celu wydobycia odfrom
aliasów. Zatem w tym przypadku warunek obejmuje tylko rekordy, dla których rodzic został już zidentyfikowany jako znajdujący się w drzewie potomków (do potomków głównego rodzica dodawane są stopniowo@pv
).where
punkcie są oceniane w kolejności, a ocena jest przerywana, gdy całkowity wynik jest pewny. Dlatego drugi warunek musi znajdować się na drugim miejscu, ponieważ dodaje onid
do listy nadrzędnej, i powinno to się zdarzyć tylko wtedy, gdyid
spełni pierwszy warunek.length
Funkcja nazywa się tylko upewnić, warunek ten jest zawsze prawdziwe, nawet jeślipv
ciąg będzie z jakiegoś powodu, uzyskując wartość falsy.Podsumowując, można uznać, że założenia te są zbyt ryzykowne, aby na nich polegać. Dokumentacja ostrzega:
Nawet jeśli działa to spójnie z powyższym zapytaniem, kolejność oceny może się jeszcze zmienić, na przykład po dodaniu warunków lub użyciu tego zapytania jako widoku lub zapytania podrzędnego w większym zapytaniu. Jest to „funkcja”, która zostanie usunięta w przyszłej wersji MySQL :
Jak wspomniano powyżej, od MySQL 8.0 należy używać
with
składni rekurencyjnej .Wydajność
W przypadku bardzo dużych zestawów danych to rozwiązanie może
find_in_set
działać wolniej, ponieważ operacja nie jest najbardziej idealnym sposobem znalezienia liczby na liście, a na pewno nie na liście, która osiąga rozmiar w tym samym rzędzie wielkości, co liczba zwracanych rekordów.Alternatywa 1:
with recursive
,connect by
Coraz więcej baz danych implementuje standardową
WITH [RECURSIVE]
składnię SQL: 1999 ISO 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+ ). Od wersji 8.0 MySQL obsługuje to . Zobacz na górę tej odpowiedzi, aby użyć składni.Niektóre bazy danych mają alternatywną, niestandardową składnię do wyszukiwania hierarchicznego, na przykład
CONNECT BY
klauzula dostępna w Oracle , DB2 , Informix , CUBRID i innych bazach danych.MySQL w wersji 5.7 nie oferuje takiej funkcji. Gdy aparat bazy danych udostępnia tę składnię lub możesz przeprowadzić migrację do takiej, która to robi, jest to z pewnością najlepsza opcja. Jeśli nie, rozważ także następujące alternatywy.
Alternatywa 2: Identyfikatory w stylu ścieżki
Sprawy stają się o wiele łatwiejsze, jeśli przypisujesz
id
wartości zawierające informacje hierarchiczne: ścieżkę. Na przykład w twoim przypadku może to wyglądać tak:Wtedy twój
select
wyglądałby tak:Alternatywa 3: Powtarzane samodzielne dołączanie
Jeśli znasz górną granicę głębokości, na jaką może stać się Twoje drzewo hierarchii, możesz użyć standardowego
sql
zapytania takiego:Zobacz to skrzypce
Te
where
określa, które rodzic stan chcesz pobrać potomków. W razie potrzeby możesz rozszerzyć to zapytanie o więcej poziomów.źródło
parent_id > id
, nie możesz użyć tego rozwiązania.WITH RECURSIVE
metody, uważam, że następujący artykuł jest bardzo pomocny w przypadku różnych scenariuszy, takich jak głębokość rekurencji, rozróżnienia oraz cykle wykrywania i zamykaniaZ bloga Zarządzanie danymi hierarchicznymi w MySQL
Struktura tabeli
Pytanie:
Wynik
Większość użytkowników w danym momencie miała do czynienia z danymi hierarchicznymi w bazie danych SQL i bez wątpienia dowiedziała się, że zarządzanie danymi hierarchicznymi nie jest tym, do czego przeznaczona jest relacyjna baza danych. Tabele relacyjnej bazy danych nie są hierarchiczne (jak XML), ale są po prostu płaską listą. Dane hierarchiczne mają relację rodzic-dziecko, która nie jest naturalnie reprezentowana w tabeli relacyjnej bazy danych. Czytaj więcej
Więcej szczegółów można znaleźć na blogu.
EDYTOWAĆ:
Wynik:
Odwołanie: Jak wykonać zapytanie rekurencyjne SELECT w Mysql?
źródło
Spróbuj tych:
Definicja tabeli:
Wiersze eksperymentalne:
Procedura przechowywana rekurencyjna:
Funkcja opakowania dla procedury składowanej:
Wybierz przykład:
Wynik:
Filtrowanie wierszy z określoną ścieżką:
Wynik:
źródło
(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
Najlepsze podejście, jakie wymyśliłem, to
Opis podejścia liniowego można znaleźć wszędzie, na przykład tutaj lub tutaj . Jako funkcji - to jest to, co mnie enspired.
W końcu - mniej lub bardziej proste, stosunkowo szybkie i PROSTE rozwiązanie.
Ciało funkcji
A potem ty tylko
Mam nadzieję, że to komuś pomoże :)
źródło
Zrobił to samo w przypadku kolejnego zapytania
Mysql select rekurencyjny dostać wszystkie dziecko z wieloma poziomami
Zapytanie będzie:
źródło
SELECT idFolder, (SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM ( SELECT @pv:=(SELECT GROUP_CONCAT(idFolder SEPARATOR ',') FROM Folder WHERE idFolderParent IN (@pv)) AS lv FROM Folder JOIN (SELECT @pv:= F1.idFolder )tmp WHERE idFolderParent IN (@pv)) a) from folder F1 where id > 10
; Nie mogę polecić F1.idFolder dla @pvNULL
. Czy wiesz, dlaczego tak może być? Czy istnieją wymagania wstępne dotyczące silnika bazy danych, czy zmieniło się coś od czasu udzielenia odpowiedzi, która powoduje, że to zapytanie jest nieaktualne?Jeśli potrzebujesz dużej prędkości odczytu, najlepszą opcją jest użycie tabeli zamknięcia. Tabela zamknięcia zawiera wiersz dla każdej pary przodków / potomków. Tak więc w twoim przykładzie wyglądałaby tabela zamknięcia
Po utworzeniu tej tabeli zapytania hierarchiczne stają się bardzo łatwe i szybkie. Aby uzyskać wszystkich potomków kategorii 20:
Oczywiście, gdy używasz takich zdenormalizowanych danych, ma to duży minus. Musisz utrzymywać tabelę zamknięcia obok tabeli kategorii. Najlepszym sposobem jest prawdopodobnie użycie wyzwalaczy, ale nieco skomplikowane jest prawidłowe śledzenie wstawek / aktualizacji / usuwania tabel zamknięcia. Podobnie jak w przypadku innych elementów, musisz spojrzeć na swoje wymagania i zdecydować, jakie podejście jest dla Ciebie najlepsze.
Edycja : zobacz pytanie Jakie są opcje przechowywania danych hierarchicznych w relacyjnej bazie danych? po więcej opcji. Istnieją różne optymalne rozwiązania dla różnych sytuacji.
źródło
Proste zapytanie do listy dzieci pierwszego rekurencji:
Wynik:
... z lewym złączeniem:
Rozwiązanie @tincot, aby wyświetlić listę wszystkich dzieci:
Przetestuj online za pomocą Sql Fiddle i zobacz wszystkie wyniki.
http://sqlfiddle.com/#!9/a318e3/4/0
źródło
Możesz to zrobić w innych bazach danych dość łatwo za pomocą zapytania rekurencyjnego (YMMV na wydajności).
Innym sposobem na to jest przechowywanie dwóch dodatkowych bitów danych, wartości lewej i prawej. Lewa i prawa wartość są uzyskiwane z przejścia przez strukturę drzewa, którą reprezentujesz w przedsprzedaży.
Nazywa się to Zmodyfikowanym przejściem do drzewa zamówień przedpremierowych i pozwala uruchomić proste zapytanie, aby uzyskać wszystkie wartości nadrzędne naraz. Ma również nazwę „zestaw zagnieżdżony”.
źródło
Wystarczy użyć klasy BlueM / tree php, aby utworzyć drzewo tabeli relacji własnych w mysql.
Oto przykład użycia BlueM / tree:
źródło
To tabela kategorii .
Wynik::
źródło
To trochę trudne, sprawdź, czy to działa dla Ciebie
Link do skrzynki SQL http://www.sqlfiddle.com/#!2/e3cdf/2
Zastąp odpowiednio nazwą pola i tabelą.
źródło
Coś tu nie wymienionego, choć nieco podobne do drugiej alternatywy zaakceptowanej odpowiedzi, ale inne i niskie koszty dla zapytania o dużej hierarchii i łatwych (wstawianie aktualizacji usuń) elementów, dodałoby stałą kolumnę ścieżki dla każdego elementu.
niektóre jak:
Przykład:
Zoptymalizuj długość ścieżki i
ORDER BY path
używając kodowania base36 zamiast prawdziwego identyfikatora ścieżki numerycznejhttps://en.wikipedia.org/wiki/Base36
Pomijanie również separatora ukośnika „/” za pomocą stałej długości i dopełniania do zakodowanego identyfikatora
Szczegółowe wyjaśnienie dotyczące optymalizacji tutaj: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
DO ZROBIENIA
budowanie funkcji lub procedury podziału ścieżki dla wycofujących się przodków jednego elementu
źródło
base36
To działa dla mnie, mam nadzieję, że to również zadziała dla ciebie. Daje ci zestaw rekordów Root to Child dla dowolnego konkretnego menu. Zmień nazwę pola zgodnie ze swoimi wymaganiami.
źródło
Łatwiej mi było:
1) utwórz funkcję, która sprawdzi, czy element znajduje się w hierarchii nadrzędnej innego elementu. Coś w tym stylu (nie napiszę funkcji, zrób to przy WHILE DO):
w twoim przykładzie
2) użyj podselekcji, coś takiego:
źródło
Zrobiłem dla ciebie zapytanie. To da Ci kategorię rekurencyjną z jednym zapytaniem:
Oto skrzypce .
źródło