dedlfix: NestedSets

Beitrag lesen

echo $begrüßung;

FROM
     admin_v3 AS t1,
     admin_v3 AS t2

t1 und t2 sind recht aussagelose Bezeichner. Sie sagten mehr über ihre Bedeutung aus, gäbest du ihnen wie im Wikipedia-Beispiel Tiefensuche die Bezeichnungen node und parent. t1 ist also dein Knoten, den du haben möchtest, der Join mit t2 dient nur zum Ermitteln der Elternknoten, die zum Pfad bis zum aktuellen t1-Knoten gehören.

Ich möchte nun nur die zwei tasks Unterpunkte ausgeben, wenn der User sich auf der übergeordneten Seite befindet.

WHERE
     t1.task != ''

Diese WHERE-Bedingung beschränkt die Ergebnismenge schon mal auf die Knoten, denen ein Task zugeordnet ist.

t1.rgt BETWEEN t1.lft AND t2.rgt

Unter anderem hier scheint mir der Fehler zu liegen. Erwähntes Wikipedia-Beispiel eignet sich ja schon mal zur Suche nach Knoten inklusive der Ermittlung ihrer Ebene. Gesucht werden alle Elternknoten zum aktuellen Knoten. Gemäß der Betrachtung als verschachtelte Mengen sind das diejenigen, die den aktuellen Knoten umschließen. Also sollte das t1.lft ein t2.lft sein. Da ein BETWEEN die angegebenen Grenzwerte mit einschließt, kommt der Knoten also ebenfalls - und zwar mit sich selbst verknüpft - in der Ergebnismenge vor.

Wenn ein Knoten n Eltern hat, hast du nun (n+1)-mal den Knoten in der Ergebnismenge stehen. Weil wir auch noch zwecks Ermittlung dieses Wertes n+1, der ja die Ebene des Knotens darstellt, eine Gruppierung brauchen, muss über ein eindeutiges Merkmal des Knotens gruppiert werden.

GROUP BY
     t2.lft

t2 ist der Elternknoten. Davon hat ein Knoten mehrere unterschiedliche. Das ist also kein passendes Gruppierungsmerkmal. lft, rgt oder id eines Knotens (= t1) sind eindeutige Merkmale[1].

GROUP BY t1.id

wäre die richtige Gruppierung.

SELECT
     t1.*,
     COUNT(*) AS ebene

"ebene" entspricht dem Wert n+1 meiner obigen Erläuterung.

t1.* geht nur unter MySQL. Mit anderen DBMS kann bei einer Gruppierung nur die gruppierenden Felder und Aggregatfunktionen verwenden, da das Ergebnis der anderen Felder im Allgemeinen nicht eindeutig ist. In unserem Fall ist es das jedoch, und hier hat MySQLs Eigenart einen Vorteil, weil wir ansonsten nur die id des Knotens als Ergebnis hätten und zum Ermitteln der restlichen Knotendaten ein weiteres Select benötigten, z.B. in Form einer Supquery[2].

HAVING
     t1.lft>" .$path[$ebene]['lft'] ." AND
     t1.rgt<" .$path[$ebene]['rgt'] ." AND
     ebene = $ebene+1

Die ersten beiden Bedingungen täten ohne MySQLs Eigenart auch nicht funktionieren, da ein HAVING nur Werte aus der SELECT-Klausel betrachten kann. Es ist aber nicht notwendig, sie in der HAVING-Klausel zu notieren. Diese Einschränkung kann gleich bei der ersten Einschränkung der Ergebnismenge notiert werden, also in der WHERE-Klausel. Einzig die Ebeneneinschränkung muss im HAVING verbleiben, denn dieser Wert steht ja erst nach der Gruppierung zur Verfügung, und zu dem Zeitpunkt ist WHERE schon Geschichte.

[1] Solange man nur einen Baum in der Tabelle verwaltet. Hat man mehrere Bäume quasi parallel in einer Tabelle, muss man zu lft oder rgt noch den Baum-Identifizierer hinzunehmen, oder aber id, das über alle Datensätze eindeutig sein muss.

[2] Nein, Supquery ist kein Schreibfehler, aber wohl auch kein allgemeingültiger Begriff. Gemeint ist das Gegenteil eines Subquerys, also ein Query außen drumrum. Oder andersrum betrachtet: Die eben besprochene Query, die nur eine ID liefert, muss als Subquery einer Abfrage notiert werden, die alle (gewünschten) Knotendaten zu einer/der gegebenen ID ermittelt.

echo "$verabschiedung $name";