Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wie erstelle ich eine hierarchische rekursive MySQL-Abfrage?

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 aus from 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 die id hinzufügt in die übergeordnete Liste, und dies sollte nur geschehen, wenn die id besteht die erste Bedingung. Die length Die Funktion wird nur aufgerufen, um sicherzustellen, dass diese Bedingung immer wahr ist, selbst wenn die pv 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.