Tabellenverknüpfungen :/



  • 2 und 19 sind die rgt- und lft-Werte +1!



  • Was mir jetzt auch noch Kopfzerbrechen bereitet, ist, wie man alle Einträge eines "Levels (egal ob Ast oder Blatt) ausgeben lassen kann.

    Also an deinem Beispiel:

    A
    |- B
    |  |- C
    |  |  `- D
    |  `- E
    `- F
       |- G
       `- H
    

    Wie kann ich nur die Kinder von A, also B und F, ausgeben lassen?
    Ich weiß, dass zwischen B und F folgender Zusammenhang besteht:
    B.rgt = F.lft +1
    Und so lässt sich das für alle weiteren Kinder von A beliebig fortsetzen...
    vorgänger.rgt = nachfolger.lft +1
    Aber ich kann mir kein Query basteln, das entsprechend Arbeitet...
    Rekursiv würde ich es wohl schaffen, aber es geht ja genau darum, das zu verhindern... 😞



  • hm, gute Frage. Hab' gerade mal ein wenig gegrübelt, aber bin nicht wirklich dahintergestiegen.

    Evtl. fällt mir morgen etwas ein.

    Bzgl. der Teilbäume: Ich will ja nicht bezielt einen Baum ausblenden, also erst den gesamten Baum holen und dann einen Ast ausblenden, sondern eher erst alles (bis auf Wurzel) ausblenden und dann gezielt einblenden. Evtl. kommt's einfach nur auf den Blickwinkel drauf an, mal sehen.

    Falls mir noch was einfällt, dann geb' ich auf jeden Fall Bescheid. Ich würd' mir aber nicht zuviel Hoffnungen machen, so wirklich scheine ich es noch nicht geblickt zu haben. 😞



  • hmpf, also eigentlich müsste Dein Problem so ungefähr zu lösen sein:

    SELECT 
    	n.*,
    	round((n.tr_rgt-n.tr_lft-1)/2,0) AS childs,
    	count(*)+(n.tr_lft>1) AS level,
    	((min(p.tr_rgt)-n.tr_rgt-(n.tr_lft>1))/2) > 0 AS lower,
    	(( (n.tr_lft-max(p.tr_lft)>1) )) AS upper
    FROM 
    	testbaum n, 
    	testbaum p 
    WHERE 
    	n.tr_lft BETWEEN p.tr_lft AND p.tr_rgt
    	AND (p.tr_root = n.tr_root)
    	AND (p.tr_id != n.tr_id OR n.tr_lft = 1)
    	AND n.tr_lft BETWEEN 1 AND 14
    	AND (level = 2)
    GROUP BY n.tr_root, n.tr_id
    ORDER BY n.tr_root, n.tr_lft
    

    allerdings bekomme ich immer die Fehlermeldung:

    #1054 - Unknown column 'level' in 'where clause'
    

    Aber irgendwie muss ich doch auf 'level' filtern können ... *grübel

    PS: Bei mir sind 1 und 14 die lft- bzw. rgt-Werte von der Wurzel, level = 2 wären also dann die direkten Kinder.

    // EDIT:
    Argh, manchmal kann es so einfach sein ... *schäm* 😃

    SELECT 
    	n.*,
    	round((n.tr_rgt-n.tr_lft-1)/2,0) AS childs,
    	count(*)+(n.tr_lft>1) AS level,
    	((min(p.tr_rgt)-n.tr_rgt-(n.tr_lft>1))/2) > 0 AS lower,
    	(( (n.tr_lft-max(p.tr_lft)>1) )) AS upper
    FROM 
    	testbaum n, 
    	testbaum p 
    WHERE 
    	n.tr_lft BETWEEN p.tr_lft AND p.tr_rgt
    	AND (p.tr_root = n.tr_root)
    	AND (p.tr_id != n.tr_id OR n.tr_lft = 1)
    	AND n.tr_lft BETWEEN 1 AND 14
    GROUP BY n.tr_root, n.tr_id HAVING level = 2
    ORDER BY n.tr_root, n.tr_lft
    

    PS: Having bei Group by ist Dein Freund. 🙂



  • Hey, ich denke ich hab' mein Problem auch gelöst. 🙂

    SELECT
    	n.*, COUNT(*) - 1 AS level
    FROM
    	testbaum AS n,
    	testbaum AS p
    WHERE 
    	n.tr_lft BETWEEN p.tr_lft AND p.tr_rgt
    GROUP BY n.tr_root, n.tr_lft
    HAVING
    	(n.tr_lft BETWEEN 1 AND 14 AND level = 0)
    	OR
    	(n.tr_lft BETWEEN 2 AND 13 AND level = 1)
    	OR
    	(n.tr_lft BETWEEN 3 AND 6 AND level = 2)
    /*	OR
    	(n.tr_lft BETWEEN 9 AND 12 AND level = 2) */
    ORDER BY n.tr_root, n.tr_lft
    

    Wenn das letzte OR in der HAVING-Klausel auskommentiert bleibt, dann ist das zweite Kind (lft: 8 und rgt: 13) auf Ebene 1 geschlossen, wenn ich den Kommentar mit verwende, dann ist der Baum komplett geöffnet. Für die Wurzel benutzt man die imaginären Werte lft: 0 und rgt: 15, wodurch dann 1 und 14 entstehen, da lft immer lft+1 und rgt = rgt-1 zum filtern ist. 🙂

    Jetzt muss ich mir nur noch überlegen, wie ich erkenne, wenn (wie in meinem Beispiel) level 1 nicht geöffnet ist, für level 2 aber etwas geöffnet ist, was ja dann nicht als geöffnet dargestellt werden darf. 🙂



  • 🙂 👍

    Die HAVING-Klausel sieht wirklich sehr vielversprechend aus!
    Was für Werte stehen bei dir in tr_root, ich habe diese Spalte nicht.

    Das Query von der Tutorial-Seite lies sich mit der HAVING-Klausel auch leicht erweitern:

    SELECT 
            o.*,
           COUNT(p.id)-1 AS level
        FROM test AS n,
             test AS p,
             test AS o
       WHERE o.lft BETWEEN p.lft AND p.rgt
         AND o.lft BETWEEN n.lft AND n.rgt
         AND n.id = 1
    GROUP BY o.lft HAVING level = 1
    ORDER BY o.lft;
    

    Ergebnis sind auch wieder alle Kinder der Wurzel.
    Allerdings braucht man jetzt hier, und so weit ich das erkennen kann, bei deiner Variante auch, immer zwei Parameter, um die Ergebnisse darstellen zu können. Das Level und die ID des entsprechenden Astes bei dieser Variante, das Level und die lft/rgt-Werte des entsprechenden Astes bei deiner Variante.



  • MySql-Doofi schrieb:

    Ergebnis sind auch wieder alle Kinder der Wurzel.
    Allerdings braucht man jetzt hier, und so weit ich das erkennen kann, bei deiner Variante auch, immer zwei Parameter, um die Ergebnisse darstellen zu können. Das Level und die ID des entsprechenden Astes bei dieser Variante, das Level und die lft/rgt-Werte des entsprechenden Astes bei deiner Variante.

    Ja, das sehe ich auch so, ich denke da kommt man auch nicht drumherum. 😞

    MySql-Doofi schrieb:

    Die HAVING-Klausel sieht wirklich sehr vielversprechend aus!
    Was für Werte stehen bei dir in tr_root, ich habe diese Spalte nicht.

    Ich hab' bei meiner Beispiel-Tabelle die Möglichkeit mehrere Bäume in dieser Tabelle zu speichern, also mehrere Wurzeln. In tr_root steht also immer die tr_id der Wurzel. Wenn es sich um die Wurzel selbst handelt, dann ist tr_id = tr_root. Habe ich bei irgendeiner Seite so gesehen und gefiel mir. 🙂



  • oO

    Du machst mir das zu kompilziert. 😉
    Ich glaube ein mühevolles anpassen deines Query kann ihc mir sparen, läuft ja eh auf das selbe raus.

    Nochmal zu den Leveln, das Level des Kindes eines Astes ist um 1 größer als das Level des Elternastes.

    Wenn man jetzt die ID des Elternastes hat und somit das Level ermitteln kann, möchte man doch meinen, dass das ausreicht, um alle Kinder darzustellen. Aber ich krieg es auch nicht hin... 😞



  • Jetzt bin ich verwirrt. 🙂

    Also, meiner Meinung nach brauchst Du die lft-/rgt-Werte und das level, um die Kinder darzustellen, was imho auch logisch ist. Nehmen wir mal folgenden Baum:

    A
    |- B
    |  |- C
    |  `- D
    `- E
       |- F
       `- G
    

    Dazu gehört folgende Tabelle:

    +-------+---------+---------+--------+--------+-------+
    | tr_id | tr_root | tr_name | tr_lft | tr_rgt | level |
    +-------+---------+---------+--------+--------+-------+
    |     1 |       1 | A       |      1 |     14 |     0 |
    |     2 |       1 | B       |      2 |      7 |     1 |
    |     3 |       1 | C       |      3 |      4 |     2 |
    |     4 |       1 | D       |      5 |      6 |     2 |
    |     5 |       1 | E       |      8 |     13 |     1 |
    |     6 |       1 | F       |      9 |     10 |     2 |
    |     7 |       1 | G       |     11 |     12 |     2 |
    +-------+---------+---------+--------+--------+-------+
    

    Wenn ich jetzt alle Kinder von 'B' haben möchte, dann muss ich doch alle Datensätze holen, die level 2 = getLevel('B')+1 und tr_lft > 2 und tr_rgt < 7 haben. Wenn ich die Bedingung der lft-/rgt-Werte weglasse, dann bekomme ich ja zusätzlich 'F' und 'G' als Kinder ausgegeben, was ja falsch wäre.

    Hab' Dein Statement mit der ID (ohne lft/rgt) nicht überprüft, daher weiß ich nicht genau, ob das so läuft, daher hier nur die Erklärung, warum das mit den lft/rgt-Werte laufen sollte.

    Die lft/rgt-Werte in einem Teilbaum TB eines Knotens K liegen immer zwischen den lft/rgt-Werten von K, deswegen ist dies als Filter-Bedingung imho unerlässlich.



  • Nochmal am Beispiel, alle Kinder von A anzeigen zu lassen:

    +-------+---------+---------+--------+--------+-------+
    | tr_id | tr_root | tr_name | tr_lft | tr_rgt | level |
    +-------+---------+---------+--------+--------+-------+
    |     1 |       1 | A       |      [b]1[/b] |     [b]14[/b] |     0 |
    |     2 |       1 | B       |      [b]2[/b] |      [b]7[/b] |     1 |
    |     3 |       1 | C       |      3 |      4 |     2 |
    |     4 |       1 | D       |      5 |      6 |     2 |
    |     5 |       1 | E       |      [b]8[/b] |     [b]13[/b] |     1 |
    |     6 |       1 | F       |      9 |     10 |     2 |
    |     7 |       1 | G       |     11 |     12 |     2 |
    +-------+---------+---------+--------+--------+-------+
    

    Gegeben ist nur der lft-Wert von A!
    1 -> 2 -> 7 -> 8 -> 13 -> 14
    Alle Äste verhalten sich da ähnlich wie in einem Parent-Baum.
    Für alle Äste, außer der Wurzel gilt immer:
    row1.tr_rgt = row2.tr_lft+1

    Warum kann man das nicht nutzen? Ich probiere immer noch damit rum...



  • Um das zu machen müsstest Du ein Statement bauen, welches so ähnlich aussieht:

    select * from `tree` 
    where `tr_lft` = 2 or (`tr_lft` > 2 and `tr_lft` = `prev_row`.`tr_rgt` + 1)
    order by `tr_lft` asc
    

    wobei Du hier natürlich einen Ausdruck für `prev_row` bräuchtest, den es afaik nicht gibt.

    Mag sein, dass ich mich irre, aber ich glaube, dass man auf Felder des vorherigen Datensatzes in einer Liste nicht zugreifen kann.

    Evtl. würde eine Möglichkeit bestehen, wenn Du die gleiche Liste, also in diesem Beispiel B und C versetzt aneinander joinen würdest, Du also eine Liste der Form

    +---------+--------+--------+----------+---------+---------+
    | tr_name | tr_lft | tr_rgt | tr_name2 | tr_lft2 | tr_rgt2 |
    +---------+--------+--------+----------+---------+---------+
    | B       |      2 |      7 | A        |       1 |      14 |
    | E       |      8 |     13 | B        |       2 |       7 |
    +---------+--------+--------+----------+---------+---------+
    

    Wenn Du einen Join in der Form hinbekommst, dann kannst Du ja einfach ein

    where tr_lft = tr_lft2 + 1 or tr_lft = tr_rgt2 + 1
    

    dranhängen, dann müsste das laufen.

    Nur, wenn Du soweit bist, dass Du genau diese Liste dranjoinen kannst, dann brauchst Du eigentlich nicht mehr weitermachen, weil Du ja dann schon die Liste gefunden hast, die Dich interessiert.

    Also, ich denke, das klappt so nicht, wie Du Dir das vorstellst. 😞

    Oder hast Du evtl. in der Zwischenzeit eine Lösung gefunden? 😃



  • Ne, hab die Idee jetzt verworfen, und wie du weiter oben schon vorgeschlagen hast, eine zusätzliche Spalte level eingefügt. Funktioniert prima und ich bin soweit auch zufrieden. 🙂

    mantiz, vielen vielen Dank für deine Hilfe, hast mir wirklich sehr gut geholfen! 👍


Anmelden zum Antworten