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+
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 angegebene Wert parent_id = 19
sollte auf den Wert id
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:
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 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:
- Die
from
Klausel wird zuerst ausgewertet. Hier @pv
wird also initialisiert.
- Die
where
Klausel wird für jeden Datensatz in der Reihenfolge des Abrufs aus den from
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
).
- Die Bedingungen in dieser
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 die id
zur übergeordneten Liste hinzufügt , und dies sollte nur geschehen, wenn id
die erste Bedingung erfüllt ist. Die length
Funktion wird nur aufgerufen, um sicherzustellen, dass diese Bedingung immer erfüllt ist, auch wenn die pv
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:
Möglicherweise erhalten Sie die erwarteten Ergebnisse, dies ist jedoch nicht garantiert. [...] Die Reihenfolge der Auswertung für Ausdrücke mit Benutzervariablen ist nicht definiert.
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 :
In früheren Versionen von MySQL war es möglich, einer Benutzervariablen in anderen Anweisungen als einen Wert zuzuweisen SET
. Diese Funktionalität wird aus Gründen der Abwärtskompatibilität in MySQL 8.0 unterstützt, muss jedoch in einer zukünftigen Version von MySQL entfernt werden.
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:
ID | NAME
19 | category1
19/1 | category2
19/1/1 | category3
19/1/1/1 | category4
Dann select
würden Sie so aussehen:
select id,
name
from products
where id like '19/%'
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:
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;
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.