<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Develnet.org &#187; MySql Tutorials</title>
	<atom:link href="http://develnet.org/online/mysql-tutorial/feed" rel="self" type="application/rss+xml" />
	<link>http://develnet.org</link>
	<description>Code News Tricks und ein wenig Netzkultur</description>
	<lastBuildDate>Sat, 21 Aug 2010 18:41:33 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Manipulation der Baumstruktur</title>
		<link>http://develnet.org/manipulation-der-baumstruktur.html</link>
		<comments>http://develnet.org/manipulation-der-baumstruktur.html#comments</comments>
		<pubDate>Thu, 18 Jun 2009 21:00:31 +0000</pubDate>
		<dc:creator>Horno</dc:creator>
				<category><![CDATA[MySql Tutorials]]></category>

		<guid isPermaLink="false">http://develnet.org/?p=55</guid>
		<description><![CDATA[Im Abschnitt Der Aufbau eines Nested Sets-Baumes wurde eine Einfügeoperation vorgestellt, die einem gegebenen Knoten immer einen neuen Kindknoten hinzufügt und zwar &#8220;ganz rechts&#8221; im Baum, also in Bezug auf einen preorder-walk &#8220;nach&#8221; seinen Geschwistern. Für das Beispiel dieses Tutorials ist diese Methode ausreichend, da das in Bezug auf ein einfaches Forum genau die gewünschte [...]]]></description>
			<content:encoded><![CDATA[<p>Im Abschnitt <em>Der Aufbau eines Nested Sets-Baumes</em> wurde eine Einfügeoperation vorgestellt, die einem gegebenen Knoten immer einen neuen Kindknoten hinzufügt und zwar &#8220;ganz rechts&#8221; im Baum, also in Bezug auf einen preorder-walk &#8220;nach&#8221; seinen Geschwistern.<span id="more-55"></span> Für das Beispiel dieses Tutorials ist diese Methode ausreichend, da das in Bezug auf ein einfaches Forum genau die gewünschte Funktionalität darstellt.</p>
<p>Für andere Anwendungen wie zum Beispiel dem interaktiven Aufbau von Menüsystemen werden aber in der Regel weitere Operationen zur Manipulation der Baumstruktur benötigt. In diesem Anhang werden einige der wichtigsten davon vorgestellt. Dabei müssen alle Funktionen auf der Datenbank als &#8220;atomare Operationen&#8221; ausgeführt werden. Das kann entweder durch ein sperren der gesamten Tabelle oder viel effizienter durch das Einbetten der Methoden in eine Transaktion geschehen, sofern ein Transaktionsmanagemt zur Verfügung steht (z.B. ab MySQL 4+ mit InnoDB).</p>
<h2>Bezugspunkte</h2>
<p>Alle Funktionen zur weiteren Manipulation des Nested-Set-Baumes benötigen als Eingangsparameter die <tt>node_ID</tt> des Knotens, auf den sie sich beziehen. Aus dieser können dann in einem einfachen <tt>SELECT</tt>-Statement die zugehörige <tt>root_id</tt> sowie die <tt>LFT</tt> und <tt>RGT</tt> Werte ermittelt werden.</p>
<pre>LOCK TABLES node WRITE;
...
SELECT root_id, lft, rgt FROM node WHERE node_id = V_NODE_ID
...
UNLOCK TABLES;</pre>
<p>Im Folgenden werden diese Werte einfach als virtuelle Variablen mit dem Prefix <tt>V_</tt> dargestellt. In der Realität können sie einfach innerhalb der jeweils verwendeten, übergeordneten Programmiersprache bereitgestellt werden.</p>
<h2>Ein Bruder rechts:</h2>
<p>Mit der folgenden Methode kann ein neues Blatt auf dem gleichen Level, rechts vom einem vorhandenen Knoten eingefügt werden. Als Eingangsparameter wird dazu die ID des Bezugsknotens, <tt>V_BROTHER_ID</tt>, benötigt. Anhand dieser werden zunächst die zugehörigen Werte <tt>V_ROOT_ID</tt> und <tt>V_BROTHER_RGT</tt> ermittelt.</p>
<pre>LOCK TABLES node WRITE;

# make room for the new node
UPDATE node
   SET lft = lft + 2
 WHERE root_id = V_ROOT_ID AND lft &gt; V_BROTHER_RGT;

UPDATE node
   SET rgt = rgt + 2
 WHERE root_id = V_ROOT_ID AND rgt &gt; V_BROTHER_RGT;

# insert the sibling
INSERT INTO node ( root_id, payload, lft, rgt )
          VALUES ( V_ROOT_ID, 'a right brother', V_BROTHER_RGT + 1,
                   V_BROTHER_RGT + 2 );
UNLOCK TABLES;</pre>
<hr /><a name="A4"></a></p>
<h2>Ein Bruder links:</h2>
<p>Analog zur eben beschriebenen Methode fügt diese ein neues Blatt auf dem gleichen Level, links vom einem vorhandenen Knoten ein. Als Eingangsparameter wird dazu wieder die ID des Bezugsknotens, <tt>V_BROTHER_ID</tt>, benötigt. Daraus werden dann die zugehörigen Werte <tt>V_ROOT_ID</tt> und <tt>V_BROTHER_LFT</tt> selektiert.</p>
<pre>LOCK TABLES node WRITE;

# make room for the new node
UPDATE node
   SET lft = lft + 2
 WHERE root_id = V_ROOT_ID AND lft &gt;= V_BROTHER_LFT;

UPDATE node
   SET rgt = rgt + 2
 WHERE root_id = V_ROOT_ID AND rgt &gt; V_BROTHER_LFT;

# insert the sibling
INSERT INTO node ( root_id, payload, lft, rgt )
          VALUES ( V_ROOT_ID, 'a brother to the left', V_BROTHER_LFT,
                   V_BROTHER_LFT + 1 );
UNLOCK TABLES;</pre>
<p>Mit Hilfe dieser beiden Methoden, sowie der Einfügeoperation aus dem Abschnitt <em>Der Aufbau eines Nested Sets-Baume</em>s ist es möglich, beliebige Nested-Set-Bäume in beliebiger Reihenfolge aufzubauen bzw. zu erweitern.</p>
<h2>Ein Blatt löschen</h2>
<p>Zum Löschen eines einzelnen Blattes (das ist ein Knoten ohne Kinder <img src='http://develnet.org/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  werden dessen ID, <tt>V_NODE_ID</tt>, sowie die zugehörigen <tt>LFT</tt>, <tt>RGT</tt> und <tt>ROOT_ID</tt> Werte, <tt>V_LFT</tt>, <tt>V_RGT</tt> und <tt>V_ROOT_ID</tt>, benötigt. Im Vorfeld muss natürlich geprüft werden, ob es sich wirklich nur um ein Blatt handelt (Bedingung: <tt>V_RGT = V_LFT + 1</tt>), was in der umgebenden Programmiersprache leicht möglich ist.</p>
<pre>LOCK TABLES node WRITE;

# delete the node
DELETE FROM node WHERE node_id = V_NODE_ID;                

# move the rest of the nodes ...
UPDATE node
   SET lft = lft - 2
 WHERE root_id = V_ROOT_ID AND lft &gt; V_RGT;

UPDATE node
   SET rgt = rgt - 2
 WHERE root_id = V_ROOT_ID AND rgt &gt; V_RGT;

UNLOCK TABLES;</pre>
<hr /><a name="A6"></a></p>
<h2>Einen Teilbaum löschen</h2>
<p>Das Löschen eines Teilbaumes verläuft prinzipiell gleich wie das Löschen eines Blattes. Allerdings muss zusätzlich ein Wert <tt>V_MOVE</tt> berechnet werden, der angibt, um welche Werte die <tt>LFT</tt> / <tt>RGT</tt> Attribute der Folgeknoten vermindert werden müssen.</p>
<p><tt>V_MOVE</tt> kann in der umgebenden Programmiersprache aus den üblichen Variablen wie folgt berechnet werden:</p>
<pre>Pseudocode...

V_MOVE = floor((V_RGT-V_LFT)/2);
V_MOVE = 2 * (1+V_MOVE);</pre>
<pre>LOCK TABLES node WRITE;

# delete the node including its children
DELETE FROM node
      WHERE root_id = V_ROOT_ID
        AND lft between V_LFT AND V_RGT;                

# move the rest of the nodes ...
UPDATE node
   SET lft = lft - V_MOVE
 WHERE root_id = V_ROOT_ID AND lft &gt; V_RGT;

UPDATE node
   SET rgt = rgt - V_MOVE
 WHERE root_id = V_ROOT_ID AND rgt &gt; V_RGT;

UNLOCK TABLES;</pre>
<p>Mit dieser Funktion kann natürlich auch ein einzelnes Blatt gelöscht werden. Das wäre dann der Spezialfall <tt>V_RGT = V_LFT + 1</tt>.</p>
<p>Für <tt>V_MOVE</tt> würde sich dann <tt>V_MOVE = 2 * (1 + floor ( V_LFT + 1 - V_LFT )/2)) = 2</tt> ergeben.</p>
<h3>Und der Rest?</h3>
<p>Was noch übrig bleibt sind diverse Verschiebeoperationen. Diese können am besten durch Kombinationen der vorgestellten Grundoperationen realisiert werden. Das Schema ist dabei immer:</p>
<ul>
<li>Daten des Blattes / Teilbaumes exportieren</li>
<li>Blatt / Teilbaum löschen</li>
<li>Exportierte Daten wieder in der richtigen Reihenfolge einfügen</li>
</ul>
<p>Eine ausführliche Darstellung würde den Rahmen dieses Tutorials aber sicher sprengen, sie können aber gerne beim Autor <a href="http://web.archive.org/web/20070105135146/http://www.develnet.org/245.html">Holger Morgenstern</a> nachfragen.</p>
<p>Die Darstellung der Implementation einer Nested-Set-Modellierung mittels JAVA und MySQL finden Sie unter <a href="http://web.archive.org/web/20070105135146/http://www.morgenstern.net/NestedSet.html" target="_blank">http://www.morgenstern.net/NestedSet.html</a> .</p>
]]></content:encoded>
			<wfw:commentRss>http://develnet.org/manipulation-der-baumstruktur.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Selektieren der Daten</title>
		<link>http://develnet.org/selektieren-der-daten.html</link>
		<comments>http://develnet.org/selektieren-der-daten.html#comments</comments>
		<pubDate>Thu, 18 Jun 2009 20:57:26 +0000</pubDate>
		<dc:creator>Horno</dc:creator>
				<category><![CDATA[MySql Tutorials]]></category>

		<guid isPermaLink="false">http://develnet.org/?p=50</guid>
		<description><![CDATA[&#8220;Wozu der ganze Aufwand eigentlich?&#8221; mag man sich fragen &#8230; Nun, die Früchte unserer Arbeit können wir mit dem folgenden Query ernten:
  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;
Dieses Konstrukt gibt den kompletten Thread-Baum mit den Elementen aus, die zu root_id [...]]]></description>
			<content:encoded><![CDATA[<p>&#8220;Wozu der ganze Aufwand eigentlich?&#8221; mag man sich fragen &#8230; Nun, die Früchte unserer Arbeit können wir mit dem folgenden Query ernten:<span id="more-50"></span></p>
<pre>  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;</pre>
<p>Dieses Konstrukt gibt den kompletten Thread-Baum mit den Elementen aus, die zu <tt>root_id</tt> = 1 gehören. Zusätzlich enthalten ist der Grad (<tt>level</tt>) &#8211; so ist es ein Leichtes, korrekte Einrückungen der Threads darzustellen. Das Ergebnis:</p>
<pre>+-----------------------+-------+
| payload               | level |
+-----------------------+-------+
| A - Das Wurzelposting |     1 |
| B - Reply auf "A"     |     2 |
| C - Reply auf "B"     |     3 |
| D - 2. Reply auf "B"  |     3 |
+-----------------------+-------+</pre>
<p>Als Teil eines Threaded Forums könnte die Ausgabe so aussehen:</p>
<ul>
<li>A &#8211; Das Wurzelposting
<ul>
<li>B &#8211; Reply auf &#8220;A&#8221;
<ul>
<li>C &#8211; Reply auf &#8220;B&#8221;</li>
<li>D &#8211; 2. Reply auf &#8220;B&#8221;</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Ähnlich wie beim Erweitern des Baumes benötigen wir für die Ausgabe keinen direkten &#8220;Anfangspunkt&#8221; (<tt>node_id</tt>) &#8211; es reicht die eindeutige Zuordung (<tt>root_id</tt>) eines Postings zum Thread, den Rest ergibt die Logik der Nested Sets-Zahlenpaare.</p>
<p>Im normalen Einsatz haben wir trotzdem einen Ausgangspunkt: Üblicherweise wird in einer Übersicht mehr als ein Thread dargestellt. Nach dem Beschaffen der Daten der Wurzelpostings (aller Posting in den <tt>root_id = node_id</tt> gilt) für eine Übersichtsdarstellung verfügen wir über die benötigten Parameter. Für jedes dieser Wurzelpostings muss zusätzlich die oben beschreibene Operation angewendet werden.</p>
<p><strong>Merke:</strong></p>
<ul>
<li>Man könnte auf die Idee kommen, ein komplettes Forum in nur einem Baum mit einer &#8220;unsichtbaren&#8221; Wurzel zu realisieren, um dann das ganze Forum und dessen Teilbäume (Threads) mit einer einzigen Query herauszulesen. Der gesunde Menschenverstand sollte an dieser Stelle aktiviert werden: Je größer das Nested Set wird, umso teurer ist es neue Einträge einzufügen und umso länger wird das Herauszulesen mittels dem vorgestellten Join dauern.</li>
<li>Hier gilt es einen Kompromiss zwischen Bequemlichkeit und Geschwindigkeit zu finden, und das Forum in kleinere autarke Teilbäume zu unterteilen.</li>
</ul>
<h2>Mehr Informationen aus dem Baum:</h2>
<p>Mit einer leicht veränderten Query können wir für jeden Eintrag die Anzahl der Kind-Knoten ermitteln, und somit bei der Ausgabe die Anzahl der Antworten auf ein Posting angeben:</p>
<pre>  SELECT node1.payload,
         floor(( node1.rgt - node1.lft) / 2) AS children,
         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;</pre>
<p>Das Ergebnis:</p>
<pre>+-----------------------+----------+-------+
| payload               | children | level |
+-----------------------+----------+-------+
| A - Das Wurzelposting |        3 |     1 |
| B - Reply auf "A"     |        2 |     2 |
| C - Reply auf "B"     |        0 |     3 |
| D - 2. Reply auf "B"  |        0 |     3 |
+-----------------------+----------+-------+</pre>
<p>Selbstverständlich könnte man die <tt>LFT-RGT</tt>-Zahlenpaare auch direkt aus den Datenbankfeldern rausziehen und die kleine Berechungsoperation in einer übergeordneten Steuerungssprache durchführen.</p>
<hr />Betrachten wir abschließend das Ergebnis der letzten Query auf einen größeren Baum mit dem Augenmerk auf die jeweilige Einrückungstiefe und die Anzahl der Kinder. Der Baum dürfte bereits aus dem visuellen Modell bekannt sein:</p>
<pre>+---------+----------+-------+
| payload | children | level |
+---------+----------+-------+
| A       |       12 |     1 |
| B       |        3 |     2 |
| C       |        0 |     3 |
| D       |        1 |     3 |
| E       |        0 |     4 |
| F       |        7 |     2 |
| G       |        0 |     3 |
| H       |        5 |     3 |
| I       |        3 |     4 |
| J       |        1 |     5 |
| K       |        0 |     6 |
| L       |        0 |     5 |
| M       |        0 |     4 |
+---------+----------+-------+</pre>
<p><img style="float: left; margin-right: 50px;" src="http://web.archive.org/web/20070105134915/http://www.develnet.org/embed/nestedsets7.gif" border="0" alt="Baumdiagramm" width="231" height="316" /></p>
]]></content:encoded>
			<wfw:commentRss>http://develnet.org/selektieren-der-daten.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Der Aufbau eines Nested Sets-Baumes</title>
		<link>http://develnet.org/der-aufbau-eines-nested-sets-baumes.html</link>
		<comments>http://develnet.org/der-aufbau-eines-nested-sets-baumes.html#comments</comments>
		<pubDate>Thu, 18 Jun 2009 20:55:49 +0000</pubDate>
		<dc:creator>Horno</dc:creator>
				<category><![CDATA[MySql Tutorials]]></category>

		<guid isPermaLink="false">http://develnet.org/?p=48</guid>
		<description><![CDATA[Als Implementation der Nested Sets mit SQL realisieren wir im Folgenden die Grundstruktur eines &#8220;Threaded Forum&#8221; also eines Forums in dem Antworten auf ein Posting baumartig dargestellt werden können.
Als Datenquelle für unsere Implementation eignet sich eine beliebige SQL-Datenbank &#8211; bei der Überprüfung der Beispiele wurde die schnelle und kostenlose MySQL-Datenbank verwendet. Bei anderen Datenbanken sind [...]]]></description>
			<content:encoded><![CDATA[<p>Als Implementation der Nested Sets mit SQL realisieren wir im Folgenden die Grundstruktur eines &#8220;Threaded Forum&#8221; also eines Forums in dem Antworten auf ein Posting baumartig dargestellt werden können.<span id="more-48"></span></p>
<p>Als Datenquelle für unsere Implementation eignet sich eine beliebige SQL-Datenbank &#8211; bei der Überprüfung der Beispiele wurde die schnelle und kostenlose MySQL-Datenbank verwendet. Bei anderen Datenbanken sind die SQL-Statements bzw. -Funktionen (z.B. <tt>LAST_INSERT_ID()</tt>, ggf. das Locking oder Sequenzen und Trigger anstatt <tt>AUTO_INCREMENT</tt>) auf die Gegebenheiten der jeweiligen Datenbank anzupassen. Wenn das RDBMS Transaktionen beherrscht (<tt>COMMIT</tt>, <tt>ROLLBACK</tt>) sollte man selbstverständlich nicht vergessen diese ergänzend zu den Beispielen zu implementieren. Auf das <tt>LOCK TABLES</tt> kann dann natürlich verzichtet werden, was die Gesamtperformance des Systems i.d.R. deutlich erhöht.</p>
<p><strong>Unsere simple Thread-Tabelle:</strong></p>
<pre>CREATE TABLE node (
    node_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    root_id INT(10) UNSIGNED NOT NULL,
    payload VARCHAR(64)      NULL,
    lft     INT(10) UNSIGNED NOT NULL,
    rgt     INT(10) UNSIGNED NOT NULL,

    PRIMARY KEY (node_id),
    KEY root_id (root_id)
);</pre>
<p>Um verschiedene Threads (Bäume) voneinander unterscheiden zu können, führen wir in jedem Datensatz die <tt>root_id</tt> mit. Die <tt>root_id</tt> ist bei jedem Datensatz, der zu einem Thread gehört, gleich und mit der <tt>node_id</tt> (dem Primärschlüssel) des allerersten Postings eines Threads identisch.</p>
<hr /><a name="A2"></a></p>
<h2>Die Baumwurzel &#8211; das Rootposting:</h2>
<p>Die einfachste Operation in unserem Baum ist das Einfügen eines Root- resp. Wurzelpostings. Diese setzt sich aus einem <tt>INSERT</tt> und einem <tt>UPDATE</tt>-Statement zusammen. Das <tt>UPDATE</tt>-Statement wird benötigt, um die bereits erwähnte <tt>root_id</tt> dem Wert der <tt>node_id</tt> anzupassen, da der Wert der <tt>node_id</tt> automatisch vergeben wird (<tt>AUTO_INCREMENT</tt> bei MySQL) und wir diesen vor der <tt>INSERT</tt>-Operation nicht kennen können:</p>
<pre>LOCK TABLES node WRITE;

INSERT INTO node ( payload, lft, rgt )
          VALUES ( 'A - Das Wurzelposting', 1, 2 );

UPDATE node
   SET root_id = LAST_INSERT_ID()
 WHERE node_id = LAST_INSERT_ID();

UNLOCK TABLES;</pre>
<p><strong>Das wenig spektakuläre Ergebnis sieht zunächst folgendermaßen aus:</strong></p>
<pre>+---------+---------+-----------------------+-----+-----+
| node_id | root_id | payload               | lft | rgt |
+---------+---------+-----------------------+-----+-----+
|       1 |       1 | A - Das Wurzelposting |   1 |   2 |
+---------+---------+-----------------------+-----+-----+</pre>
<p>Ein Wurzelposting ohne Kinder wird immer durch die Zahlen <tt>LFT</tt> = 1 und <tt>RGT</tt> = 2 repräsentiert.</p>
<hr /><a name="A3"></a></p>
<h2>Reply &#8211; der Baum wird erweitert:</h2>
<p>Die Erweiterung des Baumes gestaltet sich ein wenig trickreicher, wenn auch nicht undurchschaubar, wenn man das visuelle Modell der Nested Sets verstanden hat.</p>
<p>Um die korrekte Erweiterung des Baumes zu gewährleisten, kommen ebenfalls mehrere SQL-Statements zum Einsatz. Zwei davon stellen die Integrität der <tt>LFT-RGT</tt>-Zahlenpaare sicher, das dritte speichert den neuen Datensatz (das Reply) in der Datenbanktabelle ab. Als Eingangsparameter benötigen wir die <tt>root_id</tt> (nicht die <tt>node_id</tt>!) &#8211; d.h. die Thread-Zugehörigkeit &#8211; und die <tt>LFT</tt>- und <tt>RGT</tt>-Zahlen des Datensatzes an den der neue Knoten &#8220;angehängt&#8221; werden soll. Im folgendem Beispiel sind es die Werte:</p>
<pre>root_id = 1   (Variable: V_ROOT_ID)
    lft = 1   (Variable: V_LFT)
    rgt = 2   (Variable: V_RGT)</pre>
<p>&#8230; also die Daten des Wurzelpostings A. Um den Bezug zum nächsten Beispiel herzustellen wurden diese Werte als imaginäre Variablen (<tt>V_ROOT_ID</tt>, <tt>V_LFT</tt> und <tt>V_RGT</tt>) benannt. Diese Daten müssen vor der Einfügeoperation (z.B. durch ein entsprechendes <tt>SELECT</tt>-Statement für den zuerweiternden Knoten) ermittelt werden. Da üblicherweise eine übergeordnete Programmiersprache die Steuerung der SQL-Statements übernimmt, dürfte das Beschaffen dieser Werte kein Problem darstellen.</p>
<p><strong>Zusätzlich gewährleistet hier das Locking der Tabellen, dass es zu keiner Zeit zu Kollisionen mir einem anderen Prozeß kommt, der eine ähnliche Einfügeoperation beabsichtigt:</strong></p>
<pre>LOCK TABLES node WRITE;

UPDATE node
   SET lft      =  lft + 2
 WHERE root_id  =  V_ROOT_ID
   AND lft      &gt;  V_RGT;

UPDATE node
   SET rgt      =  rgt + 2
 WHERE root_id  =  V_ROOT_ID
   AND rgt      &gt;= V_RGT;

INSERT INTO node ( root_id, payload, lft, rgt )
          VALUES ( V_ROOT_ID, 'B - Reply auf "A"', V_RGT, V_RGT + 1 );

UNLOCK TABLES;</pre>
<p>Spätestens an dieser Stelle wird es deutlich warum sich Nested Sets im Allgemeinen nur für Strukturen eignen, auf die zum größten Teil lesend zugegriffen wird: Die Erweiterung der Baustruktur ist relativ &#8220;teuer&#8221; &#8211; sie kostet viel Zeit, da u.U. sehr viele Knoten eines Baumes verändert werden müssen. Wie teuer so eine Erweiterung des Baumes ist, hängt natürlich von der Komplexität der Struktur ab.</p>
<p>Leider muß in MySQL die komplette Tabelle temporär für den Schreibzugriff gesperrt werden, da uns im Normallfall hier keine Transaktionen zur Verfügung stehen. (Ab version 4.x unterstützt auch MySQL mit dem jetzt zur Standard Installation gehörenden &#8220;InnoDB&#8221; Tabellentyp Transaktionen. Falls diese Version eingesetzt wird, kann man auf das Locking verzichten und stattdessen die drei Operationen in eine Transaktion kapseln.)</p>
<p>Das Ergebnis in der Datenbank:</p>
<pre>+---------+---------+-----------------------+-----+-----+
| node_id | root_id | payload               | lft | rgt |
+---------+---------+-----------------------+-----+-----+
|       1 |       1 | A - Das Wurzelposting |   1 |   4 |
|       2 |       1 | B - Reply auf "A"     |   2 |   3 |
+---------+---------+-----------------------+-----+-----+</pre>
<p>Wiederholt man diese &#8220;Ergänzungs-Query&#8221; ein weiteres Mal mit den Werten <tt>LFT</tt>- und <tt>RGT</tt>-Werten des neuen Knotens (B)</p>
<pre>root_id = 1   (Variable: V_ROOT_ID)
    lft = 2   (Variable: V_LFT)
    rgt = 3   (Variable: V_RGT)</pre>
<p>mit der Absicht dem Knoten B seinerseits einen Kind-Knoten hinzuzufügen, so erhält man:</p>
<pre>+---------+---------+-----------------------+-----+-----+
| node_id | root_id | payload               | lft | rgt |
+---------+---------+-----------------------+-----+-----+
|       1 |       1 | A - Das Wurzelposting |   1 |   6 |
|       2 |       1 | B - Reply auf "A"     |   2 |   5 |
|       3 |       1 | C - Reply auf "B"     |   3 |   4 |
+---------+---------+-----------------------+-----+-----+</pre>
<p>Als letztes Beispiel fügen wir dem Knoten B noch ein zusätzliches &#8220;Reply&#8221; hinzu, und beobachten die Änderung in der Datenbank. Das <tt>LFT-RGT</tt>-Zahlenpaar für den Knoten B hat sich mittlerweile geändert, so dass unsere Ausgangswerte für die Einfügeoperation wie folgt aussehen:</p>
<pre>root_id = 1   (Variable: V_ROOT_ID)
    lft = 2   (Variable: V_LFT)
    rgt = 5   (Variable: V_RGT)</pre>
<p>Nachdem die Query ausgeführt wurde finden wir folgendes zufriedenstellendes Ergebnis vor:</p>
<pre>+---------+---------+-----------------------+-----+-----+
| node_id | root_id | payload               | lft | rgt |
+---------+---------+-----------------------+-----+-----+
|       1 |       1 | A - Das Wurzelposting |   1 |   8 |
|       2 |       1 | B - Reply auf "A"     |   2 |   7 |
|       3 |       1 | C - Reply auf "B"     |   3 |   4 |
|       4 |       1 | D - 2. Reply auf "B"  |   5 |   6 |
+---------+---------+-----------------------+-----+-----+</pre>
<p>(Anmerkung: Die hier vorgestellte Einfügeoperation, fügt einem gegebenen Knoten immer einen neuen Kindknoten hinzu und zwar &#8220;ganz rechts&#8221; im Baum, also in Bezug auf einen preorder-walk &#8220;nach&#8221; seinen Geschwistern. Allgemeinere Operationen zum Einfügen von Knoten rechts und links von einem gegebenen Knoten werden im Manipulation der Baumstruktur dargestellt.)</p>
]]></content:encoded>
			<wfw:commentRss>http://develnet.org/der-aufbau-eines-nested-sets-baumes.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Das &#8216;Nested Sets&#8217; Modell &#8211; Bäume mit SQL</title>
		<link>http://develnet.org/das-nested-sets-modell-baume-mit-sql.html</link>
		<comments>http://develnet.org/das-nested-sets-modell-baume-mit-sql.html#comments</comments>
		<pubDate>Thu, 18 Jun 2009 20:52:31 +0000</pubDate>
		<dc:creator>Horno</dc:creator>
				<category><![CDATA[MySql Tutorials]]></category>

		<guid isPermaLink="false">http://develnet.org/?p=44</guid>
		<description><![CDATA[
Der Aufbau eines Nested Sets-Baumes
Selektieren der Daten
Manipulation der Baumstruktur


Status Quo
Ein häufige Programmieraufgabe ist es baumartige Strukturen abzubilden, also Strukturen in denen jeder Knoten einen Baumes beliebig viele Unterknoten besitzen kann, die jeweils wieder beliebig viele Unterknoten besitzen können etc.
Diese Strukturen kennt jeder, wenn er sich mal genauer Filesystem-Browser (z.B. den Windows-Explorer o.ä), strukturierte Webkataloge oder [...]]]></description>
			<content:encoded><![CDATA[<ul>
<li><a href="http://develnet.org/der-aufbau-eines-nested-sets-baumes.html">Der Aufbau eines Nested Sets-Baumes</a></li>
<li><a href="http://develnet.org/selektieren-der-daten.html">Selektieren der Daten</a></li>
<li><a href="http://develnet.org/manipulation-der-baumstruktur.html">Manipulation der Baumstruktur</a></li>
</ul>
<p><span id="more-44"></span></p>
<h1>Status Quo</h1>
<p>Ein häufige Programmieraufgabe ist es baumartige Strukturen abzubilden, also Strukturen in denen jeder Knoten einen Baumes beliebig viele Unterknoten besitzen kann, die jeweils wieder beliebig viele Unterknoten besitzen können etc.</p>
<p>Diese Strukturen kennt jeder, wenn er sich mal genauer Filesystem-Browser (z.B. den Windows-Explorer o.ä), strukturierte Webkataloge oder ähnliche Tools angeschaut hat: Verzeichnisse (Knoten) enthalten Dateien und Verzeichnisse, die ihrerseits wieder Dateien und Verzeichnisse enthalten. Diese Schachtelung erfolgt dabei beliebig tief.</p>
<p>Nun, in Filesystem-Browsern erfolgt die Darstellung rekursiv, da der ganze darzustellende Baum mittels Pointern im RAM offensichtlich schnell genug durchlaufen werden kann. Was ist aber, wenn man aus einer relationalen SQL-Datenbank Daten performant holen möchte, um diese als einen Baum mit z.B. HTML zu visualisieren?</p>
<p>Die mit Abstand schlechteste Idee ist es, verkettete Listen mit Rückwärts- bzw. Vorwärtsreferenzen in der Datenbank zu erzeugen und ebenfalls rekursiv für jeden Knoten eines vermeintlichen SQL-Baumes eine separate Query an die Datenbank abzusetzen.</p>
<p>Solange der darzustellende Baum klein bleibt, werden Performanceverluste nicht augenscheinlich und fallen nicht ins Gewicht, aber bei größerer Datenmengen wird die Datenbank bei diesem Verfahren zwangsläufig in die Knie gehen müssen, da hunderte von Queries den Server verstopfen können.</p>
<p><strong>Merke:</strong></p>
<ul>
<li>Nie rekursive Queries an die Datenbank absetzen, <em>wenn es sich vermeiden lässt</em>.</li>
</ul>
<p>Mit &#8220;Nested Sets&#8221; (verschachtelten Mengen), einfachen Kenntnissen der Relationalen Algebra und wenig SQL-Verständnis lassen sich binäre und allgemeine Bäume abbilden, ohne dass es im Datenbanklayer zu Rekursion kommen muss. Relationale Algebra ist auch das interne Arbeitsprinzip der meisten SQL-Datenbanken.</p>
<p>Dieses Tutorial beschreibt einen Ansatz für ein Threaded Forum mit Nested Sets in einer abstrakten Form. Den Anfang macht ein visuelles Modell.</p>
<h1>Das visuelle Modell</h1>
<p>Um einen Baum in einer SQL-Datenbank darstellen zu können, bieten sich sog. &#8220;Nested Sets&#8221; &#8211; verschachtelte Mengen &#8211; an. Für das Auslesen einer beliebig tiefen Baumstruktur benötigt man dann nur einen einzigen Datenbank-Query.</p>
<p>Hat man eine Oracle-Datenbank zur Hand, so ist es unnötig auf die hier beschriebene Methode auszuweichen, da Oracle den SQL-Befehl <tt>CONNECT BY (PRIOR)</tt> beherrscht, und sich somit leicht verkettete Datensätze samt ihrem Grad (Einrückungstiefe, <tt>LEVEL</tt>) herauslesen lassen. Mehr zu <tt>CONNECT BY</tt> findet sich in der freundlichen Oracle-Dokumentation.</p>
<p>Das gleiche gilt für IBM&#8217;s Datenbank DB2. Dort läßt sich eine &#8220;konventionelle&#8221; Baumdarstellung mittels verketteter Listen dadurch effizient gestalten, dass rekursive Queryies direkt von der Datenbank unterstützt und optimiert werden. Für nähere Informationen dazu siehe <tt>RECURSIVE QUERY</tt> der &#8220;SQL Reference&#8221; von DB2.</p>
<p>Beides sind jedoch proprietäre Erweiterungen von Oracle / IBM und kein Standard-SQL, also in der Regel bei anderen DBMS nicht verfügbar. Die &#8220;Nested Set Modellierung&#8221; arbeitet dagegen auch auf Systemen effizient, die nur einen minimale SQL Standard unterstützen.</p>
<p><strong>Ein Baumast mit zwei Blättern läßt sich als Menge folgendermaßen abbilden:</strong></p>
<p><img src="http://develnet.org/wp-content/plugins/hot-linked-image-cacher/upload/web.archive.org/web/20070105135012/www.develnet.org/embed/nestedsets1.gif" border="0" alt="Baumast und Mengendiagramm" width="276" height="107" /></p>
<p>Mengen (Blätter) B und C sind Untermengen der Menge A, also Kinder der Wurzel A. In unseren Beispielen verwenden wir einfachheitshalber nur binäre Bäume &#8211; also Bäume mit nur jeweils höchstens zwei Kindern. Selbstverständlich ist die Technik auch auf allgemeine Bäume, deren Knoten wiederum beliebig viele Kind-Knoten enthalten können, anwendbar.</p>
<p>Was hier im Modell offensichtlich ist, muß in einer Datenbanktabelle nicht nur als solches gekennzeichnet werden, es muß auch die Reihenfolge (B vor C) gespeichert werden.</p>
<p>Für diese Speicherung der Abhängigkeit innerhalb einer solchen Struktur benötigen wir zwei Tabellenfelder (im Folgenden LFT und RGT gennant). Die Payload, also die Nutzlast des Knotens beschränkt sich in unseren Beispielen zunächst nur auf einen Buchstaben (A .. n), um die verschiedenen Knoten des Baumes benennen zu können.</p>
<p><strong>Dies sei ein Beispielknoten mit der Nutzlast und dem Zahlenpaar, das die Abhängigkeiten repräsentiert:</strong></p>
<p><img src="http://develnet.org/wp-content/plugins/hot-linked-image-cacher/upload/web.archive.org/web/20070105135012/www.develnet.org/embed/nestedsets2.gif" border="0" alt="Beispielknoten" width="66" height="66" /></p>
<p>Um die Reihenfolge der Knoten zu sichern, speichern wir in den LFT- und RGT-Feldern Zahlen, die uns beim Herauslesen der Daten ermöglichen, mit dem den sog. Preorder-Walk den Baum zu traversieren (zu durchlaufen) &#8211; beim Preorder-Walk bedeutet das, dass zuerst der Wurzel-Knoten, dann alle Knoten, die &#8220;links&#8221; vom jeweiligen Vaterknoten, danach alle Knoten die &#8220;rechts&#8221; davon liegen, gelesen werden.</p>
<p>Die LFT- und RGT-Zahlenpaare setzen sich für unseren kleinen Beispielbaum folgendermaßen zusammen:</p>
<p><img src="http://develnet.org/wp-content/plugins/hot-linked-image-cacher/upload/web.archive.org/web/20070105135012/www.develnet.org/embed/nestedsets3.gif" border="0" alt="Einfache Nested-Sets-Abhängigkeit" width="256" height="101" /></p>
<p>Das Verständnis für die LFT- und RGT-Zahlen ist essentiell, um das Nested-Sets-Prinzip zu erfassen: von der ersten LFT-Zahl (die &#8220;1&#8243; im Wurzel-Knoten A) ausgehend, wird zuerst das linke Blatt (B) durchlaufen, danach das rechte (C). Bei dieser Traverse &#8211; einfach den Pfeilen folgen &#8211; wird jeweils eine Zählervariable um den Wert 1 inkrementiert, und zuerst im LFT- und dann nochmals inkremetiert im RGT-Feld gespeichert.</p>
<p><strong>Das zweite Beispiel sieht damit nicht mehr so kompliziert aus:</strong></p>
<p><img src="http://develnet.org/wp-content/plugins/hot-linked-image-cacher/upload/web.archive.org/web/20070105135012/www.develnet.org/embed/nestedsets4.gif" border="0" alt="Ein Nested-Sets-Baum" width="491" height="178" /></p>
<p>Ausgehend von der Wurzel A wird wiederum jeweils der linke Teilbaum durchlaufen, dann jeweils der rechte. Die LFT- und RGT-Zahlen werden in der vorgebenen Reihenfolge inkrementiert. Besitzt ein Kind-Knoten seinerseits Kinder, so wiederholt sich die Prozedur ebenfalls für diesen Kind-Knoten etc.</p>
<p>Die Reihenfolge des Preorder-Walks ist den Pfeilen oder einfach nur dem Gesetz des Alphabetes zu entnehmen. Dieser Nested Sets-Baum würde in einer seiner Nutzformen den Baum darstellen, der im rechten Teil der Grafik abgebildet ist.</p>
<p><strong>Die Mengendarstellung dieses Baumes könnte folgendermaßen aussehen:</strong></p>
<p><img src="http://develnet.org/wp-content/plugins/hot-linked-image-cacher/upload/web.archive.org/web/20070105135012/www.develnet.org/embed/nestedsets5.gif" border="0" alt="Mengendiagramm" width="211" height="153" /></p>
<p>Aufgrund der verschiedenen Zahlenabhängigkeiten kann man sich leicht vorstellen, dass die Nested Sets-Technik sich primär für Strukturen eignet, die nur gelesen werden müssen, z.B. die Darstellung von Forumsbeiträgen oder Katalog- und Menübäumen, da bei jeder Änderung des Baumes alle Abhängigkeiten geändert werden müssen. Bei Forumsbeiträgen oder ähnlichen Bäumen kann man davon ausgehen, das in 95% der Fälle der Zugriff lesend erfolgt.</p>
<p>Bäume die sich öfters dynamisch verändern (müssen) &#8211; z.B. Sortierung &#8211; werden mit dieser Technik ausgebremst.</p>
]]></content:encoded>
			<wfw:commentRss>http://develnet.org/das-nested-sets-modell-baume-mit-sql.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
