Für MySQL 8+: Verwenden Sie den rekursiven with
Syntax.
Für MySQL 5.x: Verwenden Sie Inline-Variablen, Pfad-IDs oder Selbstverknüpfungen.
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;
Der in parent_id = 19
angegebene Wert sollte auf id
gesetzt werden des Elternteils, von dem Sie alle Nachkommen auswählen möchten.
MySQL 5.x
Für MySQL-Versionen, die Common Table Expressions nicht unterstützen (bis Version 5.7), würden Sie dies mit der folgenden Abfrage erreichen:
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))
Hier ist eine Geige .
Hier der in @pv := '19'
angegebene Wert sollte auf id
gesetzt werden des Elternteils, von dem Sie alle Nachkommen auswählen möchten.
Dies funktioniert auch, wenn ein Elternteil mehrere hat Kinder. Voraussetzung ist jedoch, dass jeder Datensatz die Bedingung parent_id < id
erfüllt , andernfalls sind die Ergebnisse nicht vollständig.
Variablenzuweisungen innerhalb einer Abfrage
Diese Abfrage verwendet eine spezifische MySQL-Syntax:Variablen werden während ihrer Ausführung zugewiesen und geändert. Über die Ausführungsreihenfolge werden einige Annahmen getroffen:
- Der
from
Klausel wird zuerst ausgewertet. Hier also@pv
wird initialisiert. - Das
where
-Klausel wird für jeden Datensatz in der Reihenfolge des Abrufs ausfrom
ausgewertet Pseudonyme. Hier wird also eine Bedingung gestellt, um nur Datensätze einzubeziehen, für die der Elternteil bereits als im Nachkommenbaum vorhanden identifiziert wurde (alle Nachkommen des primären Elternteils werden nach und nach zu@pv
hinzugefügt ). - Die Bedingungen in diesem
where
Satz werden der Reihe nach ausgewertet, und die Auswertung wird unterbrochen, sobald das Gesamtergebnis feststeht. Daher muss die zweite Bedingung an zweiter Stelle stehen, da sie dieid
hinzufügt in die übergeordnete Liste, und dies sollte nur geschehen, wenn dieid
besteht die erste Bedingung. Dielength
Die Funktion wird nur aufgerufen, um sicherzustellen, dass diese Bedingung immer wahr ist, selbst wenn diepv
string würde aus irgendeinem Grund einen falschen Wert ergeben.
Alles in allem mag man diese Annahmen zu riskant finden, um sich darauf zu verlassen. Die Dokumentation warnt:
Sie erhalten möglicherweise die erwarteten Ergebnisse, dies ist jedoch nicht garantiert. [...] Die Reihenfolge der Auswertung für Ausdrücke mit Benutzervariablen ist nicht definiert.
Obwohl es 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 ein "Feature", das in Zukunft entfernt wird MySQL-Version :
Frühere Versionen von MySQL ermöglichten es, einer Benutzervariablen in anderen Anweisungen als SET
einen Wert zuzuweisen . Diese Funktionalität wird in MySQL 8.0 aus Gründen der Abwärtskompatibilität unterstützt, kann aber in einer zukünftigen Version von MySQL entfernt werden.
Wie oben erwähnt, sollten Sie ab MySQL 8.0 das rekursive with
verwenden Syntax.
Effizienz
Bei sehr großen Datenmengen kann diese Lösung langsam werden, da die find_in_set
Die Operation ist nicht der ideale Weg, um eine Zahl in einer Liste zu finden, schon gar nicht in einer Liste, die eine Größe in der gleichen Größenordnung wie die Anzahl der zurückgegebenen Datensätze erreicht.
Alternative 1:with recursive
, connect by
Immer mehr Datenbanken implementieren den SQL:1999 ISO-Standard WITH [RECURSIVE]
Syntax
für rekursive Abfragen (z. B. 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
. Siehe oben in dieser Antwort für die zu verwendende Syntax.
Einige Datenbanken haben eine alternative, nicht standardmäßige Syntax für hierarchische Suchen, wie z. B. CONNECT BY
Klausel verfügbar auf Oracle
, DB2
, Informix , CUBRID
und andere Datenbanken.
MySQL-Version 5.7 bietet eine solche Funktion nicht. Wenn Ihre Datenbank-Engine diese Syntax bereitstellt oder Sie zu einer migrieren können, die dies tut, dann ist dies sicherlich die beste Option. Wenn nicht, dann ziehen Sie auch die folgenden Alternativen in Betracht.
Alternative 2:Bezeichner im Pfadstil
Die Dinge werden viel einfacher, wenn Sie id
zuweisen würden Werte, die die hierarchischen Informationen enthalten:ein Pfad. In Ihrem Fall könnte dies beispielsweise so aussehen:
ID | NAME |
---|---|
19 | Kategorie1 |
19/1 | Kategorie2 |
19/1/1 | Kategorie3 |
19/1/1/1 | Kategorie4 |
Dann select
würde so aussehen:
select id,
name
from products
where id like '19/%'
Alternative 3:Wiederholte Selbstverknüpfungen
Wenn Sie eine Obergrenze dafür kennen, wie tief Ihr Hierarchiebaum werden darf, können Sie ein Standard-sql
verwenden Abfrage wie folgt:
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;
Siehe diese Geige
Das where
Bedingung gibt an, von welchem Elternteil Sie die Nachkommen abrufen möchten. Sie können diese Abfrage bei Bedarf um weitere Ebenen erweitern.