Probleme mit SQL (Nested Sets)
Ole
- datenbank
Hi
wie die ÜBerschrift schon vermuten läßt, habe ich ein paar Probleme mit Nestet Sets.
Ich versuche grade das oben verlinkte Tutorial an meine Bedürfnisse anzupassen und merke dabei, daß ich außer SELECT, INSERT, UPDATE und DELETE noch nicht allzuviel mit SQL gemacht habe unter ich nicht alles verstehe was hier passiert.
Das erstellen der Tabelle war kein Problem und mit diesem Code habe ich mein Rootposting erzeugt:
LOCK TABLES kategorien WRITE;
INSERT INTO kategorien ( kat_name,kat_titel,kat_link,kat_position,kat_not_kat,kat_frei, lft, rgt )
VALUES ( 'Start','Start','index.cfm','','0','1',1,2 );
UPDATE kategorien
SET root_id = LAST_INSERT_ID()
WHERE node_id = LAST_INSERT_ID();
UNLOCK TABLES;
Soweit, sogut. Was ich allerdings nicht verstehe, ist die Syntax der Query zum Abrufen der Tabelle:
SELECT node1.payload,
COUNT(*) AS level
FROM node AS node1,
node AS node2
WHERE node1.root_id = 1
AND node2.root_id = 1
AND node1.lft BETWEEN node2.lft AND node2.rgt
GROUP BY node1.LFT;
Meine Tabelle heißt, anders als im Tutorial, nicht "node" sondern "kategorien". Also muß ich hier eben "node" durch "kategorien" ersetzen.
Aber: Wie kann "node1.payload" selektiert werden, wenn node1 garnicht definiert ist? Oder wird erst "node AS node1" abgearbeitet, so daß node1 an dieser Stelle doch schon definiert ist?
Meine SQL-Bücher haben mich leider nicht weiter gebracht. Vieleicht kann mir jemand die Query ja so erklären, daß ich sie auch verstehe :)
danke
Ole
(8-)>
echo $begrüßung;
SELECT node1.payload,
COUNT(*) AS level
FROM node AS node1,
node AS node2
Aber: Wie kann "node1.payload" selektiert werden, wenn node1 garnicht definiert ist? Oder wird erst "node AS node1" abgearbeitet, so daß node1 an dieser Stelle doch schon definiert ist?
Wenn du mal ein ganz einfaches SELECT-Statement nimmst
SELECT feld FROM tabelle
dann stünde der SQL-Parser vor dem gleichen Problem. Er liest SELECT feld und frag sich, woher "feld" nehmen, wenn nicht stehlen? Die Antwort findet er erst in der FROM-Klausel.
echo "$verabschiedung $name";
Hi
guter Einwand :)...habs verstanden :)
Danke
Ole
(8-)>
Hallo Ole,
Das erstellen der Tabelle war kein Problem und mit diesem Code habe ich mein Rootposting erzeugt:
LOCK TABLES kategorien WRITE;
INSERT INTO kategorien ( kat_name,kat_titel,kat_link,kat_position,kat_not_kat,kat_frei, lft, rgt )
VALUES ( 'Start','Start','index.cfm','','0','1',1,2 );
d.h. statt payload hast Du die Spalten
kat\_name,
kat\_titel,
kat\_link,
kat\_position,
kat\_not\_kat,
kat\_frei
~~~sql
>
> UPDATE kategorien
> SET root_id = LAST_INSERT_ID()
> WHERE node_id = LAST_INSERT_ID();
>
> UNLOCK TABLES;
>
Soweit, sogut. Was ich allerdings nicht verstehe, ist die Syntax der Query zum Abrufen der Tabelle:
hier entsprechend einsetzen. Bitte beachte, dass im Tutorial bereits Anworten drin sind, d.h. bei Dir Unterkategorien.
SELECT node1.payload,
COUNT(*) AS level
FROM node AS node1,
node AS node2
WHERE node1.root_id = 1
AND node2.root_id = 1
AND node1.lft BETWEEN node2.lft AND node2.rgt
GROUP BY node1.LFT;
MySQL ist schlampig im Umgang mit GROUP BY. Alle anderen DBMS verlangen, dass nach node1.payload gruppiert wird, auch wenn es hier an der Ausgabe nichts ändert. (Für mich ein Bug, kein Feature)
> Meine Tabelle heißt, anders als im Tutorial, nicht "node" sondern "kategorien". Also muß ich hier eben "node" durch "kategorien" ersetzen.
> Aber: Wie kann "node1.payload" selektiert werden, wenn node1 garnicht definiert ist? Oder wird erst "node AS node1" abgearbeitet, so daß node1 an dieser Stelle doch schon definiert ist?
node1 ist ein Aliasname für die Tabelle node. node2 ebenfalls. Du greifst zweimal auf die gleiche Tabelle zurück, siehe auch meine Erklärung [ beim Selfjoin](http://aktuell.de.selfhtml.org/artikel/review/datenbanken/fortgeschrittene-joins/selfjoin.htm).
Freundliche Grüße
Vinzenz
Hi
d.h. statt payload hast Du die Spalten
genau
hier entsprechend einsetzen. Bitte beachte, dass im Tutorial bereits Anworten drin sind, d.h. bei Dir Unterkategorien.
Ich habe zur Zeit nur das Rootposting drin. Ändert das irgendetwas an der Syntax? Wenn ich das ganze richtig verstanden habe, müßt mir kat_name (statt payload) und 1 für level ausgegeben werden
MySQL ist schlampig im Umgang mit GROUP BY. Alle anderen DBMS verlangen, dass nach node1.payload gruppiert wird, auch wenn es hier an der Ausgabe nichts ändert. (Für mich ein Bug, kein Feature)
Das kann ich leider nicht beurteilen. Habe wie gesagt, die Funktionen diverser DBMS (mssql, mysql, postgre) nur an der Oberfläche angekratzt :).
node1 ist ein Aliasname für die Tabelle node. node2 ebenfalls. Du greifst zweimal auf die gleiche Tabelle zurück, siehe auch meine Erklärung beim Selfjoin.
da ich mit joins auch noch auf kriegsfuß stehe, ist die seite zur weiteren betrachtung in die bookmarks gewandert :)
danke
Ole
(8-)>
Ich nochmal :)
bin schon ein gutes Stück weitergekommen, hänge jetzt allerdings an einer neuen Stelle.
Ich habe jetzt ein Showcase geschrieben, welcher mir alle Kategorien aus der DB ausließt und als verschachtelte Liste darstellt.
Diese Liste ist in ein Formular einbezogen und jeder Listenpunkt hat einen Radiobutton, welches als Wert die node_id mitbekommt.
Soll nun eine neue Kategorie erstellt werden, muß dazu ein Radiobutton ausgewählt werden um die ID der Kategorie zu bekommen in welcher die Unterkategorie angelegt wird.
Wenn ich alles richtig verstanden habe, wird diese Unterkategorie rechts von der übergeordneten Kategorie angelegt.
Nun gibt es jedoch in dem Tutorial sowohl die Möglichkeit eine Kategorie rechts einzufügen, als auch links.
Reicht es wenn ich sage, daß die neue Kategorie rechts der alten angelegt wird, oder muß ich obendrein noch sagen, daß es links einer anderen angelegt wird?
Außerdem irritiert mich folgende Formulierung:
"Als Eingangsparameter wird dazu die ID des Bezugsknotens, V_BROTHER_ID, benötigt. Anhand dieser werden zunächst die zugehörigen Werte V_ROOT_ID und V_BROTHER_RGT ermittelt."
V_BROTHER_ID ist doch die node_ID die ich mit dem Formular übergebe, aber woher leite ich die anderen beiden ab?
Ich bin heute irgendwie mental nicht ganz auf der Höhe. :(
Danke
Ole
(8-)>
echo $begrüßung;
Soll nun eine neue Kategorie erstellt werden, muß dazu ein Radiobutton ausgewählt werden um die ID der Kategorie zu bekommen in welcher die Unterkategorie angelegt wird.
Wenn ich alles richtig verstanden habe, wird diese Unterkategorie rechts von der übergeordneten Kategorie angelegt.
Nun gibt es jedoch in dem Tutorial sowohl die Möglichkeit eine Kategorie rechts einzufügen, als auch links.
Das wäre dann keine Unterkategorie sondern eine gleichwertige. Eine Unterkategorie muss quasi innendrin abzweigen, nicht außerhalb. Also dein Knoten (inklusive rechtem Nachbar) wäre:
+-------------+ +-------------+
| Knoten | | r. Nachbar |
+-------------+ +-------------+
| L:14 | R:15 |--| L:16 | R:17 |
+-------------+ +-------------+
Der muss nun gesplittet werden, so dass R um 2 größer wird, damit der neue Unterknoten 15 und 16 für L und R bekommen kann. (Die L- und R-Werte der Knoten mit R und L > 15 müssen auch um 2 erhöht werden.)
+-------------+ +-------------+
| Knoten | | r. Nachbar |
+-------------+ +-------------+
| L:14 | R:17 |--| L:18 | R:19 |
+-------------+ +-------------+
| |
+----+ +-----+
| +-------------+ |
| | Unterknoten | |
| +-------------+ |
+-| L:15 | R:16 |-+
+-------------+
echo "$verabschiedung $name";
Moin,
nachdem ich viel rumprobiert habe bin ich bisger zu folgender Lösung gelangt, welche in meinen letzten versuchen zumindest immer das richtige in die tabelle eingetragen hat
LOCK TABLES kategorien WRITE;
UPDATE kategorien SET lft = lft + 2 WHERE lft > #V_rgt#;
UPDATE kategorien SET rgt = rgt + 2 WHERE rgt > #V_RGT#;
UPDATE kategorien SET rgt = rgt + 2 WHERE node_id = #v_node_id#
INSERT INTO kategorien ( root_id,kat_name,kat_titel,kat_link,kat_position,kat_not_kat,kat_frei,lft,rgt)
VALUES ( #v_rrot_id#, '#form.kat_name#','#form.kat_titel#','#form.kat_link#','#form.kat_position#','#form.kat_not_kat#','#form.kat_frei#', #V_LFT# + 1, #V_LFT# + 2 );
UNLOCK TABLES;
v_root_id, v_node_id, v_lft und v_rgt übergeben ich.
Mal sehen wie weit ich bis zum nächsten Problem komme.
Über Tipps und Verbesserungsvorschläge freu ich mich natürlich immer :)
danke
Ole
(8-)>
Hi
kaum geschrieben schon wiederlegt :(
Funktiniert doch nicht *sniff*
Ich werds weiter probieren.
Danke
Ole
(8-)>
Ich werds weiter probieren.
Neuer Ansatz, funktuioniert bisher:
LOCK TABLES kategorien WRITE;
UPDATE kategorien SET lft = lft + 2 WHERE lft > #pi_rgt#;
UPDATE kategorien SET rgt = rgt + 2 WHERE rgt >= #pi_rgt#;
INSERT INTO kategorien ( root_id,kat_name,kat_titel,kat_link,kat_position,kat_not_kat,kat_frei,lft,rgt) VALUES ( #c_root_id#, '#form.kat_name#','#form.kat_titel#','#form.kat_link#','#form.kat_position#','#form.kat_not_kat#','#form.kat_frei#', #c_lft#, #c_rgt#);
UNLOCK TABLES;
Die Berechnung der Variablen nehme ich mittels ColdFusion vor:
<!--- Daten ermitteln --->
<cfquery datasource="#dbs#" name="getData">
SELECT * FROM kategorien WHERE node_id = #form.node_id#
</cfquery>
<!--- Parent ist-Variablen --->
<cfset pi_node_id = getData.node_id>
<cfset pi_root_id = getData.root_id>
<cfset pi_lft = getData.lft>
<cfset pi_rgt = getData.rgt>
<!--- Child Variablen --->
<cfset c_root_id = pi_root_id>
<cfset c_lft = pi_rgt>
<cfset c_rgt = pi_rgt + 1>
(denke daß kann man auch lesen wenn man noch nie zuvor mit ColdFusion zu tun hatte :))
so long
Ole
(8-)>
Hi
Auf Nested Sets wurde ich aufmerksam durch diesen Thread. Hier hatte ich ein Problem geschildert bzgl. der Auswahl von Artikeln bezogen auf eine Kategorie.
Nun habe ich die kategorien-Tabelle auf Nested Sets umgebaut und stehe wieder vor dem Problem, wie ich nun, unter zur Hilfenahme meiner Relationstabelle, alle Artikel zusammenbekomme, die aus einer Kategorie und ihren Unterkategorien stammen.
Wie bekomme ich das hin?
Ich hoffe mir kann jemand nen Schubs in die richtige Richtung geben :)
Danke
Ole
(8-)>
echo $begrüßung;
Nun habe ich die kategorien-Tabelle auf Nested Sets umgebaut und stehe wieder vor dem Problem, wie ich nun, unter zur Hilfenahme meiner Relationstabelle, alle Artikel zusammenbekomme, die aus einer Kategorie und ihren Unterkategorien stammen.
Zuerst brauchst du aus der kategorien-Tabelle die IDs der betroffenen Kategorien (inklusive Unterkategorien). Wenn diese Abfrage steht, nimmst du diese in ein Subselect (du hast ja MySQL 5, da geht das) und schränkst deine Relationen gemäß diesem Subselect ein.
SELECT ... FROM relationen WHERE kat_id IN (SELECT id FROM kategorien WHERE ...)
Mit Joins statt Subselect lässt sich sicher auch was machen ...
echo "$verabschiedung $name";
Hi
Mit Joins statt Subselect lässt sich sicher auch was machen ...
Danke, manchmal kommt man vor Betriebsblindheit nicht mal mehr auf die "einfachsten" Lösungen.
Ich hab es jetzt so gelösst:
SELECT * FROM rel_kategorie_artikel
INNER JOIN artikel ON rel_kategorie_artikel.art_id = artikel.id
WHERE (
artikel.freigegeben = "1"
AND artikel.archiviert = "0"
AND rel_kategorie_artikel.kat_id IN (SELECT node_id FROM kategorien WHERE lft >= #lft# AND rgt <= #rgt#)
)
ORDER BY datum DESC
Dem Anschein nach, funktioniert es genau so wie ich es gerne hätte :).
Danke
Ole
(8-)>