2010-02-23 9 views
6

Ich arbeite mit einer Baumstruktur in MySQL, die mit dem geschachtelten Sets-Modell dargestellt wird.So wählen Sie sofortige Kinder und Vorfahren alle in derselben Abfrage

Ich hoffe, einige von Ihnen sql Experten können mir beim Erstellen einer SELECT-Abfrage helfen.

Ich möchte in der Lage sein, eine Reihe von Knoten mit LIKE übereinstimmen. Für jeden übereinstimmenden Knoten benötige ich auch eine durch Komma aufgelöste Liste der Vorfahren dieses Knotens und eine Komma-aufgelöste Liste der unmittelbaren Nachfolger dieses Knotens.

Ich bin nicht wirklich sicher, wo ich damit anfangen soll - wenn so etwas in einer einzigen Abfrage überhaupt möglich ist. (Derzeit beende ich dies mit einer Abfrage innerhalb einer Schleife.) Was ich hoffe, ist eine Ergebnismenge, die ungefähr so ​​aussehen könnte ....

Beginnend mit der Zeichenkette "qu" und die Tabelle abfragend " Körper "Ich bekomme ...

Alle Vorschläge, wie Sie dies ohne Schleifen Abfragen erreichen würde sehr geschätzt werden.

+0

Ich habe derzeit keine Zeit, um eine vollständige Antwort zu schreiben, aber wenn Sie Zugriff auf das Buch SQL Cookbook haben, deckt das Kapitel über hierarchische Abfragen die Verwendung von MySQL, um dieses Problem zu lösen. Für MySQL müssen Sie die Verzweigungstiefe kennen, um die Abfragen zu verwenden, aber das ist kein Problem - Sie wissen, wie tief Sie gehen möchten. Die Lösung ist nicht extrem trivial, aber es ist möglich. –

+0

@Sheepsimulator - Danke, ich werde sehen, ob ich das Buch nachverfolgen kann. – Travis

+0

Sheepsimulator, ich stimme nicht zu. es ist trivial zu lösen, wenn du weißt, welche Tiefe du brauchst - der Heilige Gral macht es für eine beliebige Tiefe. Oder na ja, nicht heiliger Gral, aber weißt du, willkürliche Tiefe macht das Problem interessant. IMO –

Antwort

0

Während ich mit Nickf einig, dass dies schlecht und schmutzig, es ist immer noch Spaß, so hier geht:

SELECT  base.left_id, base.ancestors, 
      GROUP_CONCAT(children.left_id) children 
FROM  (
      SELECT  base.left_id 
      ,   GROUP_CONCAT(ancestors.left_id) ancestors 
      FROM  nested_set base 
      LEFT JOIN nested_set ancestors 
      ON   base.left_id  BETWEEN ancestors.left_id 
              AND ancestors.right_id 
      WHERE  base.name LIKE '%criteria%' 
      GROUP BY base.left_id 
      ) base          
LEFT JOIN nested_set children 
ON   children.left_id BETWEEN base.left_id 
           AND base.right_id 
LEFT JOIN nested_set inbetween 
ON   inbetween.left_id BETWEEN base.left_id 
           AND base.right_id 
AND  children.left_id BETWEEN inbetween.left_id 
           AND inbetween.right_id  
WHERE  inbetween.left_id IS NULL 
GROUP BY base.left_id 

Grundsätzlich ist der Trick ist es in zwei Schritten zu lösen: Erstens, die Vorfahren Problem lösen, und zerquetsche die Vorfahren zu einer Liste mit, dann benutze dieses Ergebnis, um es für die Kinder zu lösen.

Der Teil der Vorfahren ist relativ einfach, es ist die Unterabfrage in der from-Klausel in meiner Lösung. Die Kinder sind ein bisschen schwieriger. Es funktioniert, indem alle Abkömmlinge genommen werden und dann gefordert wird, dass keine Knoten zwischen dem Basisknoten und den Abkömmlingen existieren, was im Grunde die Abkömmlinge auf nur die Kinder beschränkt.

Es gibt andere Variationen dieser Strategie, um das Problem zu lösen - zum Beispiel können Sie die Kinder zuerst tun und die Vorfahren mit einer Unterabfrage in der SELECT Liste lösen.

+0

verwenden Danke für Ihre Antwort. Ich werde einige Zeit damit verbringen, mit Ihrem Code-Snippet oben zu arbeiten, um zu sehen, ob ich es für mich arbeiten lassen kann. Nebenbei, Sie schlagen auch vor, dass dies eine schlechte Sache wäre, um tatsächlich zu implementieren. Ist das wirklich eine schlechte Idee? Ich muss davon ausgehen, dass eine solche Lösung * wesentlich * schneller ist als das Schleifen kleinerer Anfragen. Danke noch einmal. – Travis

+0

Nun, die Sache ist, das verschachtelte Set ist eine echte Hektik, um richtig zu machen. Zuerst die Daten in dieser Struktur pflegen und dann abfragen. Was ich besonders nicht mag, ist, dass eine kleine Veränderung, die ganz links im Baum ist, im Wesentlichen den gesamten Baum oben und rechts von dieser lokalen Veränderung neu schreibt: ein Albtraum der Skalierbarkeit. Dann schaue ich auf typische Bäume, mit denen ich mich beschäftige: Sie sind typischerweise ziemlich klein, meist gelesen und relativ statisch. Normalerweise habe ich eine Adjazenzliste + einen App-Code oder einen materialisierten Pfad oder eine Verschlusstabelle, abhängig davon, was ich gerade brauche. –

+0

Ich musste ein paar kleine Dinge ändern, um es mit meinem Setup zu arbeiten, aber es funktioniert wie ein Zauber. Da Geschwindigkeit eine große Sorge für mich ist, werde ich das verwenden. Nochmals vielen Dank. – Travis

0

In einer Abfrage? Ich würde mich nicht kümmern. Das SQL wäre schrecklich und wahrscheinlich nicht einmal so effizient. Teilen Sie jedes Bit in logisch kleinere Abfragen auf: Suchen Sie zuerst alle übereinstimmenden Knoten und dann für jedes einzelne die zusätzlichen Informationen, die Sie benötigen.

+0

Das mache ich gerade. Ich beginne mit der Auswahl der Knoten, die mit LIKE übereinstimmen. Dann durchlaufe ich sie und suche bei jeder Iteration getrennt nach Vorfahren und Kindern. Abhängig von der Größe der anfänglichen Ergebnismenge kann das eine Menge Fragen sein ... – Travis

+2

okay, ich verstehe das, aber na und? Ich würde es trotzdem bevorzugen, mehrere kleinere Abfragen zu lesen/zu führen, die eine einzelne, wohlverstandene Funktion ausführen, und nicht einen einzelnen Behemoth, der für alle außer den Super-Gurus unleserlich ist. – nickf

+0

Nun, ich bin kein Super-Guru. :) Ich versuche nur mein SQL zu optimieren. Ich habe immer verstanden, dass, sobald Sie beginnen, Ihre SQL-Abfragen zu loopen, Katastrophe ist gleich um die Ecke. – Travis

0

Ich bin nicht 100% sicher, was Sie wollen, aber wenn ich richtig verstehe, können Sie dies mit einem normalisierten Datenbankschema und Unterabfragen erreichen.

Zum Beispiel:

Tabelle "Knoten" Table "node_parents"

Die "Knoten" Tabelle aller Knoten speichern würde, und "node_parents" würden Karte Beziehungen zwischen verschiedenen Knoten.

Wenn Sie also LIKE für einen bestimmten Knoten auswählen, können Sie alle Eltern und Kinder von node_parents übernehmen.

Sie könnten die zusätzlichen Informationen mit Joins oder Unterabfragen greifen.

+0

Das Problem ist, dass Sie nur eine Ebene mit diesem Ansatz behandeln können. –

+0

an zweiter Stelle, dachte, yeah das ist chaotisch, Sie wären nicht in der Lage, es mit nur 1 Abfrage afaik zu tun. Sie müssten mindestens eine gespeicherte Prozedur zu Schleife –

0

Diese Frage ist viel schwieriger als ich in Ihrem anderen Beitrag erwartet habe, aber ich stimme nicht mit den ersten Postern zu antworten.

Ich bin ziemlich zuversichtlich, dass es mit einer einzigen Abfrage möglich ist.

Sie müssen SUBQUERIES verwenden und wählt. Haben Sie sich jemals die wirklich gute Demo auf der mySQL-Website zum Adjacency List Model angeschaut?

Da Sie für den "Knoten" LIKE können Sie dann Sub-Abfragen für Ihre SQL-Abfrage verwenden, um alle Eltern und Eltern zu erhalten. Eine meiner Fragen, die ich so gemacht habe, war absolut massiv! Aber es hat funktioniert.

Werfen Sie einen Blick: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

Dies ist nur ein kleiner Code-Schnipsel, die zeigt, wie die unmittelbaren Kinder gefunden wird.

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth 
FROM nested_category AS node, 
    nested_category AS parent, 
    nested_category AS sub_parent, 
    (
     SELECT node.name, (COUNT(parent.name) - 1) AS depth 
     FROM nested_category AS node, 
     nested_category AS parent 
     WHERE node.lft BETWEEN parent.lft AND parent.rgt 
     AND node.name = 'PORTABLE ELECTRONICS' 
     GROUP BY node.name 
     ORDER BY node.lft 
    )AS sub_tree 
WHERE node.lft BETWEEN parent.lft AND parent.rgt 
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt 
    AND sub_parent.name = sub_tree.name 
GROUP BY node.name 
HAVING depth <= 1 
ORDER BY node.lft 
Verwandte Themen