Ich habe eine MySQL-Tabelle, die wie folgt lautet:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
Jetzt möchte ich eine einzelne MySQL-Abfrage haben, zu der ich einfach die ID gebe [zum Beispiel 'id = 19'], dann sollte ich alle untergeordneten IDs erhalten [dh das Ergebnis sollte die IDs '20, 21,22 'haben]. ... Auch die Hierarchie der Kinder ist nicht bekannt, sie kann variieren ....
Außerdem habe ich bereits die Lösung mit der for-Schleife ..... Lassen Sie mich wissen, wie Sie dies mit einer einzigen MySQL-Abfrage erreichen können, wenn dies möglich ist.
mysql
sql
hierarchical-data
recursive-query
Tarun Parswani
quelle
quelle
Antworten:
Für MySQL 8+: Verwenden Sie die rekursive
with
Syntax.Für MySQL 5.x: Verwenden Sie Inline-Variablen, Pfad-IDs oder Self-Joins.
MySQL 8+
Der in angegebene Wert
parent_id = 19
sollte auf den Wertid
des übergeordneten Elements festgelegt werden, von dem Sie alle Nachkommen auswählen möchten.MySQL 5.x.
Für MySQL-Versionen, die Common Table Expressions (bis Version 5.7) nicht unterstützen, erreichen Sie dies mit der folgenden Abfrage:
Hier ist eine Geige .
Hier sollte der in angegebene Wert auf den Wert des übergeordneten Elements festgelegt
@pv := '19'
werden,id
von dem Sie alle Nachkommen auswählen möchten.Dies funktioniert auch, wenn ein Elternteil mehrere Kinder hat. Es ist jedoch erforderlich, dass jeder Datensatz die Bedingung erfüllt
parent_id < id
, da sonst die Ergebnisse nicht vollständig sind.Variablenzuweisungen innerhalb einer Abfrage
Diese Abfrage verwendet eine bestimmte MySQL-Syntax: Variablen werden während ihrer Ausführung zugewiesen und geändert. Es werden einige Annahmen über die Reihenfolge der Ausführung getroffen:
from
Klausel wird zuerst ausgewertet. Hier@pv
wird also initialisiert.where
Klausel wird für jeden Datensatz in der Reihenfolge des Abrufs aus denfrom
Aliasen ausgewertet . Hier wird also eine Bedingung gestellt, die nur Datensätze enthält, für die das übergeordnete Element bereits als im Nachkommenbaum identifiziert wurde (alle Nachkommen des primären übergeordneten Elements werden schrittweise hinzugefügt@pv
).where
Klausel werden der Reihe nach bewertet, und die Bewertung wird unterbrochen, sobald das Gesamtergebnis sicher ist. Daher muss die zweite Bedingung an zweiter Stelle stehen, da sie dieid
zur übergeordneten Liste hinzufügt , und dies sollte nur geschehen, wennid
die erste Bedingung erfüllt ist. Dielength
Funktion wird nur aufgerufen, um sicherzustellen, dass diese Bedingung immer erfüllt ist, auch wenn diepv
Zeichenfolge aus irgendeinem Grund einen falschen Wert ergeben würde.Alles in allem kann man diese Annahmen als zu riskant empfinden, um sich darauf zu verlassen. Die Dokumentation warnt:
Obwohl dies konsistent mit der obigen Abfrage funktioniert, kann sich die Auswertungsreihenfolge dennoch ändern, z. B. wenn Sie Bedingungen hinzufügen oder diese Abfrage als Ansicht oder Unterabfrage in einer größeren Abfrage verwenden. Es ist eine "Funktion", die in einer zukünftigen MySQL-Version entfernt wird :
Wie oben erwähnt, sollten Sie ab MySQL 8.0 die rekursive
with
Syntax verwenden.Effizienz
Bei sehr großen Datenmengen kann diese Lösung langsam werden, da der
find_in_set
Vorgang nicht der idealste Weg ist, um eine Zahl in einer Liste zu finden, schon gar nicht in einer Liste, deren Größe in der Größenordnung der Anzahl der zurückgegebenen Datensätze liegt.Alternative 1:
with recursive
,connect by
Immer mehr Datenbanken , um die Implementierung von SQL: 1999 ISO - Standard -
WITH [RECURSIVE]
Syntax für rekursive Abfragen (zB 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+ ). Und ab Version 8.0 unterstützt es auch MySQL . Die zu verwendende Syntax finden Sie oben in dieser Antwort.Einige Datenbanken verfügen über eine alternative, nicht standardmäßige Syntax für hierarchische Suchvorgänge, z. B. die
CONNECT BY
Klausel, die in Oracle , DB2 , Informix , CUBRID und anderen Datenbanken verfügbar ist .MySQL Version 5.7 bietet eine solche Funktion nicht. Wenn Ihr Datenbankmodul diese Syntax bereitstellt oder Sie zu einer solchen migrieren können, ist dies sicherlich die beste Option. Wenn nicht, ziehen Sie auch die folgenden Alternativen in Betracht.
Alternative 2: Pfadkennungen
Die Dinge werden viel einfacher, wenn Sie
id
Werte zuweisen , die die hierarchischen Informationen enthalten: einen Pfad. In Ihrem Fall könnte dies beispielsweise so aussehen:Dann
select
würden Sie so aussehen:Alternative 3: Wiederholte Selbstverbindungen
Wenn Sie eine Obergrenze für die Tiefe Ihres Hierarchiebaums kennen, können Sie eine Standardabfrage
sql
wie die folgende verwenden:Sehen Sie diese Geige
Die
where
Bedingung gibt an, von welchem Elternteil Sie die Nachkommen abrufen möchten. Sie können diese Abfrage nach Bedarf um weitere Ebenen erweitern.quelle
parent_id > id
, können Sie diese Lösung nicht verwenden.WITH RECURSIVE
MethodeAus dem Blog Verwalten hierarchischer Daten in MySQL
Tabellenstruktur
Abfrage:
Ausgabe
Die meisten Benutzer haben sich zu der einen oder anderen Zeit mit hierarchischen Daten in einer SQL-Datenbank befasst und zweifellos erfahren, dass die Verwaltung hierarchischer Daten nicht das ist, wofür eine relationale Datenbank gedacht ist. Die Tabellen einer relationalen Datenbank sind nicht hierarchisch (wie XML), sondern lediglich eine flache Liste. Hierarchische Daten haben eine Eltern-Kind-Beziehung, die in einer relationalen Datenbanktabelle natürlich nicht dargestellt wird. Weiterlesen
Weitere Informationen finden Sie im Blog.
BEARBEITEN:
Ausgabe:
Referenz: Wie führe ich die rekursive SELECT-Abfrage in MySQL durch?
quelle
Probiere diese:
Tabellendefinition:
Versuchsreihen:
Rekursiv gespeicherte Prozedur:
Wrapper-Funktion für die gespeicherte Prozedur:
Beispiel auswählen:
Ausgabe:
Filtern von Zeilen mit einem bestimmten Pfad:
Ausgabe:
quelle
(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
Der beste Ansatz, den ich mir ausgedacht habe, ist
Abstammungsansatz Beschreibung kann überall gefunden werden, zum Beispiel hier oder hier . Ab Funktion - das , was mich ist enspired.
Am Ende - mehr oder weniger einfache, relativ schnelle und EINFACHE Lösung.
Funktionskörper
Und dann bist du einfach
Hoffe es hilft jemandem :)
quelle
Habe das Gleiche für eine weitere Frage hier getan
MySQL auswählen rekursiv alle Kinder mit mehreren Ebenen erhalten
Die Abfrage lautet:
quelle
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
;; Ich kann F1.idFolder nicht für @pvNULL
als Ergebnis eine einzelne erhalten . Weißt du warum das sein könnte? Gibt es Voraussetzungen für das Datenbankmodul oder hat sich etwas geändert, seit Sie diese Antwort gegeben haben, die diese Abfrage veraltet macht?Wenn Sie eine schnelle Lesegeschwindigkeit benötigen, verwenden Sie am besten eine Verschlusstabelle. Eine Schließungstabelle enthält eine Zeile für jedes Ahnen / Nachkommen-Paar. In Ihrem Beispiel würde der Verschlusstisch also so aussehen
Sobald Sie diese Tabelle haben, werden hierarchische Abfragen sehr einfach und schnell. So erhalten Sie alle Nachkommen der Kategorie 20:
Natürlich gibt es einen großen Nachteil, wenn Sie denormalisierte Daten wie diese verwenden. Sie müssen die Schließungstabelle neben Ihrer Kategorietabelle pflegen. Der beste Weg ist wahrscheinlich die Verwendung von Triggern, aber es ist etwas komplex, Einfügungen / Aktualisierungen / Löschungen für Schließungstabellen korrekt zu verfolgen. Wie bei allem müssen Sie sich Ihre Anforderungen ansehen und entscheiden, welcher Ansatz für Sie am besten geeignet ist.
Bearbeiten : Siehe Frage Welche Optionen gibt es zum Speichern hierarchischer Daten in einer relationalen Datenbank? für mehr Optionen. Es gibt verschiedene optimale Lösungen für verschiedene Situationen.
quelle
Einfache Abfrage zum Auflisten von Kindern der ersten Rekursion:
Ergebnis:
... mit linkem Join:
Die Lösung von @tincot, um alle Kinder aufzulisten:
Testen Sie es online mit Sql Fiddle und sehen Sie alle Ergebnisse.
http://sqlfiddle.com/#!9/a318e3/4/0
quelle
Sie können dies in anderen Datenbanken ganz einfach mit einer rekursiven Abfrage (YMMV on Performance) tun.
Die andere Möglichkeit besteht darin, zwei zusätzliche Datenbits zu speichern, einen linken und einen rechten Wert. Der linke und der rechte Wert werden aus einer Vorbestellungsdurchquerung der Baumstruktur abgeleitet, die Sie darstellen.
Dies wird als Modified Preorder Tree Traversal bezeichnet und ermöglicht es Ihnen, eine einfache Abfrage auszuführen, um alle übergeordneten Werte gleichzeitig abzurufen. Es trägt auch den Namen "verschachtelte Menge".
quelle
Verwenden Sie einfach die BlueM / Tree- PHP-Klasse, um einen Baum aus einer Selbstbeziehungstabelle in MySQL zu erstellen .
Hier ist ein Beispiel für die Verwendung von BlueM / tree:
quelle
Es ist eine Kategorietabelle .
Ausgabe::
quelle
Es ist ein wenig knifflig, prüfen Sie, ob es für Sie funktioniert
SQL Fiddle Link http://www.sqlfiddle.com/#!2/e3cdf/2
Ersetzen Sie Ihren Feld- und Tabellennamen entsprechend.
quelle
Etwas, das hier nicht erwähnt wird, obwohl es der zweiten Alternative der akzeptierten Antwort etwas ähnlich ist, aber für große Hierarchie-Abfragen und einfache (Einfügen, Aktualisieren, Löschen) Elemente unterschiedlich und kostengünstig ist, würde für jedes Element eine dauerhafte Pfadspalte hinzufügen.
einige wie:
Beispiel:
Optimieren Sie die Pfadlänge und
ORDER BY path
verwenden Sie stattdessen die Base36-Codierung anstelle der reellen numerischen Pfad-IDhttps://en.wikipedia.org/wiki/Base36
Unterdrücken Sie auch das Schrägstrich-Trennzeichen '/', indem Sie eine feste Länge verwenden und die codierte ID auffüllen
Detaillierte Erläuterungen zur Optimierung finden Sie hier: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
MACHEN
Erstellen einer Funktion oder Prozedur zum Aufteilen des Pfads für zurückhaltende Vorfahren eines Elements
quelle
base36
Das funktioniert bei mir, hoffe, das funktioniert auch bei Ihnen. Sie erhalten einen Datensatzsatz Root to Child für ein bestimmtes Menü. Ändern Sie den Feldnamen gemäß Ihren Anforderungen.
quelle
Ich fand es leichter:
1) Erstellen Sie eine Funktion, die prüft, ob sich ein Element an einer beliebigen Stelle in der übergeordneten Hierarchie eines anderen Elements befindet. So etwas wie das (ich werde die Funktion nicht schreiben, mache es mit WHILE DO):
in deinem Beispiel
2) Verwenden Sie eine Unterauswahl, ungefähr so:
quelle
Ich habe eine Anfrage für Sie gestellt. Dadurch erhalten Sie eine rekursive Kategorie mit einer einzelnen Abfrage:
Hier ist eine Geige .
quelle