jo: Nested Sets - Ebene auslesen (SQL)

Hi.

Ich würde gerne aus einer Kategorieverwaltung in Form eines Nested Sets nur eine Ebene herauslesen.
Wie müsste der Query aussehen dafüür? Ich bekomms nicht hin.

Das ist die Struktur: (LFT und RGT werde sind angegeben)

  • Oberkategorien (1,18)
      - Kommunikation (2,11)
        - Kommunikationstheorien (3,8)
          - Paul W. (4,5)
          - Schulz von Thun (6,7)
        - Kommunikationswissenschaften (9,10)
      - Sport (12,13)
      - Esoterik (14,15)
      - Lifestyle (16,17)

Wie kann ich z.B. nur Kommunikation, Sport, Esoterik und Lifestyle auslesen?
Also immer nur eine Ebene. Z.B. auch einfach nur PaulW. und Schulz von Thun?

Grüße,

jo

  1. Hi!

    Ich würde gerne aus einer Kategorieverwaltung in Form eines Nested Sets nur eine Ebene herauslesen.
    Wie müsste der Query aussehen dafüür? Ich bekomms nicht hin.

    Ich wüsste keinen anderen Weg als mit einem Subquery jeweils die Anzahl der übergeordneten Knoten zu zählen und diese Anzahl dann zu vergleichen. Oder du pflegst das Level als weiteres Feld, dann ist es einfacher abzufagen.

    Lo!

    1. Hi Dedlfix.

      Ich wüsste keinen anderen Weg als mit einem Subquery jeweils die Anzahl der übergeordneten Knoten zu zählen und diese Anzahl dann zu vergleichen. Oder du pflegst das Level als weiteres Feld, dann ist es einfacher abzufagen.

      Hi Bahnhof.

      Das hilft mir leider garnicht weiter, vllt aber ich da nen Schaden am Kopf, aber das System ist mir so schon Komplex genug.
      Warum denn die Übergeordneten Knoten zählen? Und wie?
      Welches Level? Wo einpflegen und wie?

      Grüße, jo

      1. Hi jo,

        Warum denn die Übergeordneten Knoten zählen?

        Was ist des Level eines Knotens? Genau - es ist im wesentlichen die Anzahl der uebergerodneten Knoten (ggf. +1 oder so, je nach Geschmack). Also ist genau diese Anzahl das, was hier von Interesse ist.

        Und wie?

        Auf deutsch: "Finde die Anzahl aller Knoten, die ueber dem gefragten Knoten liegen."
        bzw.
        "Finde die Anzahl aller Knoten, deren "Lft"-Wert kleiner und deren "Rgt"-Wert groesser als der des gefragten Knotens sind.

        Und auf SQL? (Hinweis: entweder ein Subselect, oder ein Self Join).

        Viele Gruesse,
        der Bademeister

        1. Hi.

          Aber ich verstehe überhaupt nicht warum die Anzahl der Oberhalb liegenden?

          Datenbank sieht so aus:
          id name lft rgt

          • Oberkategorien (1,18)
              - Kommunikation (2,11)
                - Kommunikationstheorien (3,8)
                  - Paul W. (4,5)
                  - Schulz von Thun (6,7)
                - Kommunikationswissenschaften (9,10)
              - Sport (12,13)
              - Esoterik (14,15)
              - Lifestyle (16,17)

          So. Ich möchte folgendes auslesen: Kommunikation, Sport, Esoterik, Lifestyle.
          Es sind die Unterkategorien erster Ebene unter "Oberkategorien".
          Ich möchte _nicht_ auslesen was _in_ diesen Kategorien ist (z.B. Kommunikationstheorien.

          Zuerst lese ich mit einem Select die LFT und RGT Werte von "Oberkategorien" aus. Also $lft=1 und $rgt=18.

          Mein erster Versuch war:

          SELECT categoryname,categoryid  
          FROM board_category  
          WHERE lft > $LFT AND rgt = lft + 1 AND rgt < $RGT
          

          Da kommt aber auch der Rest mit.

          Vielleicht haben wir ja ein Verständnis-Problem meiner seits was Ober/Unterhalb bedeutet.

          Oberkategorien ist für mich oberhalb von Kommunikation. P. Watzlawik ist für mich unterhalb von Kommunikation.

          Wenn ich also jetzt Kommunikation, Sport etc. auslesen möchte - da sagt irh mir ich soll die Knoten oberhalb zählen? Da ist aber doch nur noch "Oberkategorien".

          "Finde die Anzahl aller Knoten, die ueber dem gefragten Knoten liegen."

          0

          Unterhalb: 8
          4 davon in erster Ebene - und nur diese will ich auslesen.

          Ich bin einfach schwer verwirrt. Ich bekomms nicht hin.

          Gruß, jo

          1. Aber ich verstehe überhaupt nicht warum die Anzahl der Oberhalb liegenden?

            Datenbank sieht so aus:
            id name lft rgt

            • Oberkategorien (1,18)
                - Kommunikation (2,11)
                  - Kommunikationstheorien (3,8)
                    - Paul W. (4,5)
                    - Schulz von Thun (6,7)
                  - Kommunikationswissenschaften (9,10)
                - Sport (12,13)
                - Esoterik (14,15)
                - Lifestyle (16,17)

            So. Ich möchte folgendes auslesen: Kommunikation, Sport, Esoterik, Lifestyle.

            Und der Grund, warum Du diese auslesen moechtest, ist die eine Eigenschaft, die sie verbindet: die haben dasselbe Level, sprich, dieselbe Anzahl von daruebliegenden Knoten - naemlich je einen (den Knoten "Oberkategorien"). Oder?
            Dies ist also die Eigenschaft, die Du beim Auslesen als Bedingung (in der WHERE-Klausel) testen willst.

            Ich möchte _nicht_ auslesen was _in_ diesen Kategorien ist (z.B. Kommunikationstheorien.

            Deswegen hat ja auch keiner gesagt, dass Du die unterhalb davon liegenden Knoten angucken sollst :-)

            Zuerst lese ich mit einem Select die LFT und RGT Werte von "Oberkategorien" aus. Also $lft=1 und $rgt=18.

            Ich empfehle kein "zuerst", das kann man in einer Abfrage machen (und sollte dies auch, denn "in echt" muss man ansonsten gut aufpassen, dass die Werte aus der ersten Abfrage nicht schon wieder falsch sind, wenn man die zweite Abfrage ausfuehrt).

            Vielleicht haben wir ja ein Verständnis-Problem meiner seits was Ober/Unterhalb bedeutet.

            Oberkategorien ist für mich oberhalb von Kommunikation. P. Watzlawik ist für mich unterhalb von Kommunikation.

            Genau.

            Wenn ich also jetzt Kommunikation, Sport etc. auslesen möchte - da sagt irh mir ich soll die Knoten oberhalb zählen?

            Genau.

            Da ist aber doch nur noch "Oberkategorien".

            Genau.

            "Finde die Anzahl aller Knoten, die ueber dem gefragten Knoten liegen."
            0

            Nein. Das war so gemeint: Fuer jeden Knoten, ueberlege erstmal, wie Du sein Level bestimmst. Dies tust Du, in dem Du die darueberliegenden knoten zaehlst. Und wenn Du weisst, wie Du das Level bekommst, kannst Du eine SQL-Abfrage der Form

            "Gib mir alle Knoten, deren Level gleich 1 ist."

            ganz leicht schreiben. Der erste Schritt zum Verstaendnis waere also eine Abfrage, die auf deutsch heisst:

            "Gib mir Namen und Level aller Knoten". Level gibt es als Feld nicht in der Datanbank, muss also per Subselect oder Join ermittelt werden.

            Ich bin einfach schwer verwirrt. Ich bekomms nicht hin.

            Das sind wohl die meisten, wenn sie mit "Nested Sets" Bekanntschaft machen, keine Sorge. Aber das Essentielle fast jeder Abfrage von Nested Sets ist, dass man die Verschachtelung dabei mitgeliefert bekommt, d.h. genau das Level bestimmt. Das ist genau das, worum es geht, daher lohnt es sich, dass Du da ein richtiges Verstaendnis fuer entwickelst. Wo hast Du denn das mit den Nested Sets her?

            Viele Gruesse,
            der Bademeister

            1. Das sind wohl die meisten, wenn sie mit "Nested Sets" Bekanntschaft machen, keine Sorge. Aber das Essentielle fast jeder Abfrage von Nested Sets ist, dass man die Verschachtelung dabei mitgeliefert bekommt, d.h. genau das Level bestimmt. Das ist genau das, worum es geht, daher lohnt es sich, dass Du da ein richtiges Verstaendnis fuer entwickelst. Wo hast Du denn das mit den Nested Sets her?

              hier

              Aber ich bekomme es immernoch nicht hin.
              Wiel ich finde überall nur COUNT(*)-1 as level

              Aber nirgendwo als Sub-Select und nirgendwo in der Where Klausel.

                
              SELECT node1.categoryname,  
              					node2.categoryid,  
              					COUNT(*)-1 AS level  
              					  
              				FROM board_category AS node1,  board_category AS node2  
              				WHERE  
              				node1.categoryid=1 AND node2.categoryid=1  
              				AND node1.rgt BETWEEN node2.lft AND node2.rgt  
              				AND level = 1
              ~~~ ????  
                
              Mir brennt der Kopf und ich würde es gerne heute noch fertig kriegen. Könntest du mir die Abfrage schreiben? Ich werde sie nicht einfach nur übernehmen aber ich würde es dann einfach verstehen.  
                
              Beste Grüße, jo
              
              1. Hi!

                Mir brennt der Kopf und ich würde es gerne heute noch fertig kriegen. Könntest du mir die Abfrage schreiben? Ich werde sie nicht einfach nur übernehmen aber ich würde es dann einfach verstehen.

                Mach es schrittweise. Eine Beispiel für eine Abfrage, die ein Level ermittelt, gibt es beispielsweise im MySQL-Artikel zu Nested Sets (Zwischenüberschrift: Finding the Depth of the Nodes). node.name brauchst du nicht in der Abfrage, Sortieren ist auch egal. Führ die Abfrage erst einmal (mit node.name) aus, um die Richtigkeit zu bestätigen. Sie bildet die Grundlage für das Subselect, das als korrelierte Unterabfrage eingebunden werden wird. Als nächsten Schritt baust du eine Einschränkung auf einen einzigen Knoten ein (WHERE um node.catagory_id=? erweitern). Beim Einsetzen der Subquery in die eigentliche Abfrage tauschst du das ? gegen eine Referenz auf die in der Haupt-Query aktuelle ID aus.

                Die passende Stelle wäre anstelle des Wortes level in deiner WHERE-Klausel. In der SELECT-Klausel brauchst du sie nicht, denn das Level weißt du ja bereits vor der Abfrage. Es ist ja in deinem Fall auch immer der gleiche Wert. Wenn es doch in der SELECT-Klausel auftauchen soll, dann müsstest du die Subquery dort noch einmal hinschreiben. Sie nur im SELECT anzuführen geht nicht, weil beim Ausführen des WHERE das SELECT noch nicht ausgewertet wurde, und dort berechnete Werte nicht berücksichtigt werden können. Das ginge erst im HAVING.

                Lo!

              2. Aber ich bekomme es immernoch nicht hin.
                Wiel ich finde überall nur COUNT(*)-1 as level

                Aber nirgendwo als Sub-Select

                Nein, als Join. Ist doch nicht so schlimm, oder?

                und nirgendwo in der Where Klausel.

                Vielleicht, weil diese Beispiele nicht diejenigen Sets mit einem bestimmten Level abfragen wollen, sondern alle (und nur deren Level dabei bestimmen).

                Könntest du mir die Abfrage schreiben? Ich werde sie nicht einfach nur übernehmen aber ich würde es dann einfach verstehen.

                Im von Dir verlinkten Artikel steht eine Beispielabfrage:

                  
                SELECT n.name,  
                         COUNT(*)-1 AS level  
                    FROM tree AS n,  
                         tree AS p  
                   WHERE n.lft BETWEEN p.lft AND p.rgt  
                GROUP BY n.lft  
                ORDER BY n.lft;  
                
                

                Alles, was Du an einem Beispiel von mir verstehen wuerdest, kannst Du auch da verstehen. Als Hinweis: n liefert jeweils den Knoten, der abgefragt wird, p einen uebergeordneten - die muessen gezaehlt werden.

                Mir brennt der Kopf und ich würde es gerne heute noch fertig kriegen.

                Das ist jetzt nicht boes gemeint, aber gib Dir lieber etwas mehr zeit, um Joins, GROUP BY-Klauseln und das Konzept von Nested Sets *vernuenftig* zu verstehen. "Heute noch" ist da ziemlich knapp bemessen, das macht sich nicht im Vorbeigehen - jedenfalls nicht mit brennendem Kopf.

                Viele Gruesse,
                der Bademeister

          2. Hi!

            Aber ich verstehe überhaupt nicht warum die Anzahl der Oberhalb liegenden?

            • Oberkategorien (1,18)
                - Kommunikation (2,11)
                  - Kommunikationstheorien (3,8)
                    - Paul W. (4,5)
                    - Schulz von Thun (6,7)
                  - Kommunikationswissenschaften (9,10)
                - Sport (12,13)
                - Esoterik (14,15)
                - Lifestyle (16,17)

            Oberkategorien ist in Level/Ebene 1, Kommunikation, Sport, Esoterik und Lifestyle sind Level 2. Sie haben alle genau eine übergeordnete Kategorie, also ist deren Level 1 + 1. Kommunikationstheorien und Kommunikationswissenschaften sind in Level 3 (= zwei übergeordnete + 1)

            So. Ich möchte folgendes auslesen: Kommunikation, Sport, Esoterik, Lifestyle.

            Dann fragst du alle ab, deren Level gleich 2 ist (wenn es als extra Feld gepflegt ist) oder beim Berechnen 2 ergibt.

            Ich möchte _nicht_ auslesen was _in_ diesen Kategorien ist (z.B. Kommunikationstheorien.

            Dazu musst du die mit Level 3 (oder allgemein gesagt: ungleich 2) ignorieren. Das passiert ja automatisch, wenn du auf 2 vergleichst.

            Zuerst lese ich mit einem Select die LFT und RGT Werte von "Oberkategorien" aus. Also $lft=1 und $rgt=18.

            Das brauchst du nur, wenn du nur einen Teilbaum betrachten willst.

            Vielleicht haben wir ja ein Verständnis-Problem meiner seits was Ober/Unterhalb bedeutet.
            Oberkategorien ist für mich oberhalb von Kommunikation. P. Watzlawik ist für mich unterhalb von Kommunikation.

            Das passt.

            Wenn ich also jetzt Kommunikation, Sport etc. auslesen möchte - da sagt irh mir ich soll die Knoten oberhalb zählen? Da ist aber doch nur noch "Oberkategorien".

            Das musst du machen, um die Ebene zu ermitteln (wenn sie nicht als Extrafeld gepflegt ist). Andererseits musst du dich "seitwärts durchhangeln", was aber nicht sehr effektiv ist. Dazu müsstest du den ersten Knoten der ebenen ermitteln, was noch einfach ist, dann von dessen RGT+1 auf das LFT des Geschwisters schließen, und so weiter und so fort. Das RGT lässt sich nicht wirklich errechnen, der Abstand zwischen LFT und RGT hängt ja von der Anzahl der Kinder ab und ist deshalb pro Knoten individuell.

            Ich bin einfach schwer verwirrt. Ich bekomms nicht hin.

            Irgendeins der Tutorials hatte doch auch Beispiele mit Extra-Feld für das Level ... find ich grad nicht. Das Extrafeld ist jedenfalls beim Abfragen von Vorteil, weil dadurch die ständige Zählung der übergeordneten Knoten wegfällt. Das erkauft man sich aber durch einen erhöhten Aufwand bei Änderungen im Baum.

            Lo!

  2. Also ich habe jetzt einfach per Hand in die DB eine Spalte "level" eingepflegt und die bisherigen EInträge angepasst.

    Nun sieht meine Abfrage so aus:

    SELECT node1.categoryname,node1.categoryid	  
    FROM board_category AS node1  
    WHERE  
    node1.rgt BETWEEN node1.lft AND node1.rgt  
    AND level = 2
    
    1. Hi!

      Also ich habe jetzt einfach per Hand in die DB eine Spalte "level" eingepflegt und die bisherigen EInträge angepasst.

      Ich sagte ja, dass das das Einfachste ist (zumindest für Abfragen) :-)

      Lo!

    2. So ich hab sie etwas "verbessert":

      SELECT node1.categoryname,node1.categoryid  
      FROM board_category AS node1,  
      (SELECT level,lft,rgt FROM board_category WHERE categoryid=2) AS node2  
      WHERE  
      node1.lft BETWEEN node2.lft AND node2.rgt  
      AND node1.level = node2.level+1
      

      Das funktioniert auch.
      Ich muss nur noch schauen beim Löschen / Einfplegen von weitern Datensätzen die Ebenen zu bestimmen und mit einzutragen.

      Ich danke euch.
      Fällt euch nochwas ein - ein Fall o.ä. worauf ich achten sollte?
      Oder eine Verbesserung des Querys?

      Gruß, jo