Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   [PHP] Datensätze als Baum darstellen (https://www.delphipraxis.net/108794-%5Bphp%5D-datensaetze-als-baum-darstellen.html)

Matze 19. Feb 2008 18:48


[PHP] Datensätze als Baum darstellen
 
Hallo,

ich versuche gerade, Kategorie-Namen als Baum anzeigen zu lassen.

Meine Datenbanktabelle hat grob diesen Aufbau:

Code:
id | parent_id | name
----------------------------
0  |    -1     | Pflanzen
1  |    -1     | Tiere
2  |     6     | Giraffe
3  |     6     | Maus
4  |     0     | Löwenzahn
5  |     6     | Katze
6  |     1     | Säugetiere
7  |     1     | Vögel
8  |     7     | Adler
Der Baum sollte also am Ende so darstellbar sein:

Code:
Pflanzen
    Löwenzahn
Tiere
    Säugetiere
        Giraffe
        Maus
        Katze
    Vögel
        Adler
Natürlich sollten die Ebenen beliebig tief verschachtelt sein können.

Ich habe es versucht, wie hier beschrieben, doch diese Funktion gibt den Baum Stück-für-Stück aus und das möchte ich nicht. Ich hätte das gerne in einem Array zurückgegeben (Aufbau wie der Baum oben), damit ich mit diesem weiterarbeiten kann.

Wie muss das genau aussehen?

Grüße

cruiser 19. Feb 2008 18:57

Re: [PHP] Datensätze als Baum darstellen
 
Hallo...

ich nehm Wordpress für eine Website und da hab ichs fürs Menü auch so eine Struktur mit einer eignen Funktion geparsed... evtl. hilft dir das ja...

Code:
// special styled menu
function SahaPrintMenu($parent="0", $path=null, $level=0) {
global $wpdb;

if (! isset($path) ) {
$path = explode("/", $_SERVER["REQUEST_URI"]);
array_shift($path);
}

$actpath= "/";
if ($level > 0) {
$before = "<h3 class=\"level".$level."\">";
$after = "</h3>";
for ($i = 0; $i < $level; $i++) {
$actpath .= "$path[$i]/";
}
} else {
$before = "<h2>";
$after = "</h2>";
}

$nosubs = array(
0 => array("mitglieder", "galerien"),
1 => array("galerie-archiv")
); // end $nosubs

$query = "
SELECT post_title,post_name,id
FROM $wpdb->posts
WHERE post_type=\"page\"
AND post_parent=\"$parent\"
AND post_status=\"publish\"
ORDER BY menu_order
";

$result = $wpdb->get_results($query, ARRAY_A);

foreach ($result as $row) {
if (count($wpdb->get_results("SELECT id FROM $wpdb->posts WHERE post_parent=\"$row[id]\"", ARRAY_A))) {
// hat unterpunkte
if (isset($nosubs[$level])) {
// irgendwas soll keine unterpunkte haben
if (in_array($row[post_name], $nosubs[$level])) {
// dieses soll keine unterpunkte haben
if ($row[post_name] == $path[$level]) {
// aktuell
echo $before."<a class=\"currlink\" href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
} else {
// nicht aktuell
echo $before."<a href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
}
} else {
// dieses darf unterpunkte haben
if ($row[post_name] == $path[$level]) {
//aktuell
if (isset($path[$level + 1])) {
// pfad geht tiefer
echo $before."<a href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
} else {
// pfad geht nicht tiefer
echo $before."<a class=\"currlink\" href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
}
SahaPrintMenu($row[id], $path, $level + 1);
} else {
// nicht aktuell
echo $before."<a href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
}
}
} else {
// alles soll Unterpunkte haben
if ($row[post_name] == $path[$level]) {
// aktuell
if (isset($path[$level + 1])) {
// pfad geht tiefer
echo $before."<a href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
} else {
// pfad geht nicht tiefer
echo $before."<a class=\"currlink\" href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
}
SahaPrintMenu($row[id], $path, $level + 1);
} else {
// nicht aktuell
echo $before."<a href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
}
}
} else {
// hat keine unterpunkte
if ($row[post_name] == $path[$level]) {
// aktuell
echo $before."<a class=\"currlink\" href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
} else {
// nicht aktuell
echo $before."<a href=\"$actpath".$row[post_name]."\">".$row[post_title]."</a>".$after;
}
}
}
}
zugegeben... ist jetzt seeeeehr WordPress-Spezifisch ;)

Matze 19. Feb 2008 19:04

Re: [PHP] Datensätze als Baum darstellen
 
Hallo Ronny,

danke fürs Heraussuchen. Wie ich das beim groben Überfliegen feststellen kann, macht dein Code nichts anderes als der auf der verlinken Website, nämlich in der Schleife nacheinander die Menüeinträge auszugeben. Und genau das möchte ich ja nicht. ;)

Grüße

omata 19. Feb 2008 19:09

Re: [PHP] Datensätze als Baum darstellen
 
Wenn das in einem Rutsch erzeugt werden soll, solltest du uns nochmal erzählen welche Datenbank du einsetzt.

Gruss
Thorsten

Matze 19. Feb 2008 19:11

Re: [PHP] Datensätze als Baum darstellen
 
Hallo Thorsten,

das hatte ich vergessen zu schreiben: MySQL 5.0.26

Grüße

Edit: Es muss auch nicht super performant sein. Nur die Funktion zum Abarbeiten des Arrays sollte einigermaßen flott arbeiten, doch über die mach ich mir noch keine Gedanken. Erst einmal brauche ich das Array, dann schaue ich mal, ob ich das rekursiv durchgehen kann. Wenn nicht, dann frage ich hier weiter. ;)

bluesbear 19. Feb 2008 19:35

Re: [PHP] Datensätze als Baum darstellen
 
Zitat:

Zitat von Matze
(...) Ich hätte das gerne in einem Array zurückgegeben (Aufbau wie der Baum oben), damit ich mit diesem weiterarbeiten kann. (...)

Ich verstehe die Aufgabenstellung nicht so ganz - Einen Baum in einem Array darstellen? :gruebel: Wie soll das Array denn aussehen?

Matze 19. Feb 2008 19:49

Re: [PHP] Datensätze als Baum darstellen
 
Hallo Klaus

Zitat:

Zitat von bluesbear
Ich verstehe die Aufgabenstellung nicht so ganz - Einen Baum in einem Array darstellen? :gruebel:

Nein, Ich möchte Daten so in ein Array packen, dass man daraus leicht einen Baum beim Durchlaufen des Arrays darstellen kann. Vielleicht habe ich mich in dieser Hinsicht schlecht ausgedrückt.

Zitat:

Zitat von bluesbear
Wie soll das Array denn aussehen?

Was da am Geeignetsten ist, weiß ich nicht, ich denke aber etwas in der Art:

Code:
array (
    [0] => Pflanzen = array (
                [0] => Löwenzahn
            )
    [1] => Tiere = array (
            [0] => Säugetiere = array (
                    [0] => Giraffe
                    [1] => Maus
                    [2] => Katze
                )
            [1] => Vögel = array (
                    [0] => Adler
                )
            )
)
Grüße

mkinzler 19. Feb 2008 20:21

Re: [PHP] Datensätze als Baum darstellen
 
Die Darstellung des Arrays könnte man mit Hilfe eines Templates erleichtern

Matze 19. Feb 2008 20:22

Re: [PHP] Datensätze als Baum darstellen
 
Zitat:

Zitat von mkinzler
Die Darstellung des Arrays könnte man mit Hilfe eines Templates erleichtern

Ein Template-System nutze ich und das ist mit ein Grund, wieso ist die Daten zuerst in einem Array haben möchte. :thumb:

cruiser 19. Feb 2008 20:28

Re: [PHP] Datensätze als Baum darstellen
 
Ich schau mal drüber... sollte nicht das Prob sein aus einem Datensatz einen Baum zu machen ;)

Edit (4:33): Okay... eigentlich wollt ich vor einer Stunde so langsam pennen, da kam mir ein Geistesblitz.
Wenn du einen Datensatz hast ist es allerdings wichtig, dass die ID immer in der ersten Spalte steht, oder du passt die Vorverarbeitung des Datensatzes an.

Code:
<html>
  <head>
    <title>DataSet 2 Tree - Test</title>
    <style>
      .code {
        margin-bottom: 10px;
        margin-top: 0;
        border: 1px solid black;
        }
    </style>
  </head>
  <body><?php

// Test-Datenmenge
$dataset = array(
  array("id"=>1, "parent"=>0, "text"=>"Test1", "data"=>"Data1"),
  array("id"=>2, "parent"=>0, "text"=>"Test2", "data"=>"Data2"),
  array("id"=>3, "parent"=>1, "text"=>"Test3", "data"=>"Data3"),
  array("id"=>4, "parent"=>2, "text"=>"Test4", "data"=>"Data4"),
  array("id"=>5, "parent"=>1, "text"=>"Test5", "data"=>"Data5"),
  array("id"=>6, "parent"=>1, "text"=>"Test6", "data"=>"Data6"),
  array("id"=>7, "parent"=>4, "text"=>"Test7", "data"=>"Data7"),
  array("id"=>8, "parent"=>4, "text"=>"Test8", "data"=>"Data8"),
  array("id"=>9, "parent"=>3, "text"=>"Test9", "data"=>"Data9")
);


// rekursives füllen eines Trees aus einer Datenmenge
// die & vor den variablen sind Zeigerangaben, damit auch
// rauskommt was gewünscht wird
function filltree(&$node, &$ds, $id=0) {
  // Datenmenge durchgehen (Tree-Root ist 0)
  foreach ($ds as $key=>$data) {
    // Wenn der parent mit der ID der zu füllenden Node übereinstimmt
    // eine Subnode erstellen
    if ($data["parent"] == $id) {
      // Zuordnung von Nutzdaten zu ID
      $node[$data["id"]] = array("text" => $data["text"], "data" => $data["data"]);
      // rekursiv füllen
      filltree($node[$data["id"]]["childs"], $ds, $data["id"]);
      // wenn es doch keine Kinder gab, den "childs"-Unterpunkt
      // wieder entfernen
      if (count($node[$data["id"]]["childs"]) == 0) { 
        unset($node[$data["id"]]["childs"]);
      }
      // eingetragenes Node aus der Datenmenge entfernen
      unset($ds[$key]);
    }
  }
}

// das eigentliche befüllen $tree wird dabei gleich mit definiert
filltree($tree, $dataset);

// Debug Ausgabe
echo "[b]print_r(\$tree):[/b]<pre class='code'>".print_r($tree, true)."</pre>";

?></body>
</html>
Ausgabe:

Code:
Array
(
    [1] => Array
        (
            [text] => Test1
            [data] => Data1
            [childs] => Array
                (
                    [3] => Array
                        (
                            [text] => Test3
                            [data] => Data3
                            [childs] => Array
                                (
                                    [9] => Array
                                        (
                                            [text] => Test9
                                            [data] => Data9
                                        )

                                )

                        )

                    [5] => Array
                        (
                            [text] => Test5
                            [data] => Data5
                        )

                    [6] => Array
                        (
                            [text] => Test6
                            [data] => Data6
                        )

                )

        )

    [2] => Array
        (
            [text] => Test2
            [data] => Data2
            [childs] => Array
                (
                    [4] => Array
                        (
                            [text] => Test4
                            [data] => Data4
                            [childs] => Array
                                (
                                    [7] => Array
                                        (
                                            [text] => Test7
                                            [data] => Data7
                                        )

                                    [8] => Array
                                        (
                                            [text] => Test8
                                            [data] => Data8
                                        )

                                )

                        )

                )

        )

)
Das ganze hat aber vermutlich nicht die beste Laufzeit... durch eliminierung der bereits im Baum eingetragenen Nodes sollte es bei grossen Bäumen performanter werden. Aber ich bin doch nun etwas Müde ;)

Edit (13:40):
Hab alles noch ein wenig optimiert. Viel Spass mit.

Matze 20. Feb 2008 14:10

Re: [PHP] Datensätze als Baum darstellen
 
Hallo Ronny,

erst einmal vielen Dank, dass du dir diese Mühe gemacht hast und das auch noch so spät (bzw. so früh *g*). :thumb:

Ich erhalte als Array jedoch ein anderes, auch wenn ich deinen Code 1:1 in ein Testskript kopiere. Und zwar:

Code:
Array
(
    [1] => Array
        (
            [text] => Test1
            [data] => Data1
            [childs] => Array
                (
                    [3] => Array
                        (
                            [text] => Test3
                            [data] => Data3
                            [childs] => Array
                                (
                                    [9] => Array
                                        (
                                            [text] => Test9
                                            [data] => Data9
                                        )

                                )

                        )

                )

        )

)
Es fehlt also die Hälfte. :gruebel:
Die Rekursion arbeitet sich in die tiefste Ebene des ersten Parents vor und bricht dann ab, zumindest schaut's so aus. Das würde heißen, die foreach-Schleife läuft nie zu Ende, hm.

Grüße

cruiser 20. Feb 2008 14:33

Re: [PHP] Datensätze als Baum darstellen
 
hm.... keine Ahnung... bei mir gehts... evtl. ja ne Kleinigkeiut irgendwo... hier der aktuelle Code:

Code:
<html>
  <head>
    <title>DataSet 2 Tree - Test</title>
    <style>
      .code {
        margin-bottom: 10px;
        margin-top: 0;
        border: 1px solid black;
        }
    </style>
  </head>
  <body><?php

// Test-Datenmenge
$dataset = array(
  array("id"=>1, "parent"=>0, "text"=>"Test1", "data"=>"Data1"),
  array("id"=>2, "parent"=>0, "text"=>"Test2", "data"=>"Data2"),
  array("id"=>3, "parent"=>1, "text"=>"Test3", "data"=>"Data3"),
  array("id"=>4, "parent"=>2, "text"=>"Test4", "data"=>"Data4"),
  array("id"=>5, "parent"=>1, "text"=>"Test5", "data"=>"Data5"),
  array("id"=>6, "parent"=>1, "text"=>"Test6", "data"=>"Data6"),
  array("id"=>7, "parent"=>4, "text"=>"Test7", "data"=>"Data7"),
  array("id"=>8, "parent"=>4, "text"=>"Test8", "data"=>"Data8"),
  array("id"=>9, "parent"=>3, "text"=>"Test9", "data"=>"Data9")
);


// rekursives füllen eines Trees aus einer Datenmenge
// die & vor den variablen sind Zeigerangaben, damit auch
// rauskommt was gewünscht wird
function filltree(&$node, &$ds, $id=0) {
  // Datenmenge durchgehen (Tree-Root ist 0)
  foreach ($ds as $key=>$data) {
    // Wenn der parent mit der ID der zu füllenden Node übereinstimmt
    // eine Subnode erstellen
    if ($data["parent"] == $id) {
      // Zuordnung von Nutzdaten zu ID
      $node[$data["id"]] = array("text" => $data["text"], "data" => $data["data"]);
      // rekursiv füllen
      filltree($node[$data["id"]]["childs"], $ds, $data["id"]);
      // wenn es doch keine Kinder gab, den "childs"-Unterpunkt
      // wieder entfernen
      if (count($node[$data["id"]]["childs"]) == 0) { 
        unset($node[$data["id"]]["childs"]);
      }
      // eingetragenes Node aus der Datenmenge entfernen
      unset($ds[$key]);
    }
  }
}

// das eigentliche befüllen $tree wird dabei gleich mit definiert
filltree($tree, $dataset);

// Debug Ausgabe
echo "[b]print_r(\$tree):[/b]<pre class='code'>".print_r($tree, true)."</pre>";

?></body>
</html>

Matze 20. Feb 2008 14:34

Re: [PHP] Datensätze als Baum darstellen
 
Da ist es das gleiche. :?

Komisch, dass es bei dir geht und an der PHP-Version kann sowas eigentlich auch nicht liegen (PHP 5.x).

cruiser 20. Feb 2008 14:46

Re: [PHP] Datensätze als Baum darstellen
 
nur mal testweise:

kommentier mal Zeile 47 aus ( unset($ds[$key]); )

Matze 20. Feb 2008 14:47

Re: [PHP] Datensätze als Baum darstellen
 
Das habe ich bereits alles versucht, dennoch danke. Es sieht wirklich so aus, als würde die Schleife nicht zu Ende laufen.

cruiser 20. Feb 2008 14:48

Re: [PHP] Datensätze als Baum darstellen
 
hum... dann versteh ichs nich :drunken:

inherited 20. Feb 2008 14:55

Re: [PHP] Datensätze als Baum darstellen
 
Bei mir funktioniert es auch mit php4 nicht.

cruiser 20. Feb 2008 14:58

Re: [PHP] Datensätze als Baum darstellen
 
Nimm mal das Reference-Zeichen bei &$ds (Zeile 32) weg so dass da nur noch $ds steht... foreach scheint bei references seltsam zu funzen laut help... sorry

Matze 20. Feb 2008 15:01

Re: [PHP] Datensätze als Baum darstellen
 
Zitat:

Zitat von cruiser
Nimm mal das Reference-Zeichen bei &$ds (Zeile 32) weg so dass da nur noch $ds steht... foreach scheint bei references seltsam zu funzen laut help... sorry

Perfekt, vielen Dank! :firejump:

Da eine Referenz ist auch nicht nötig, richtig. Es geht nun. :thumb:

cruiser 20. Feb 2008 15:21

Re: [PHP] Datensätze als Baum darstellen
 
Dann aber zumindest die kopierte Datenmenge redzieren und damit den Schleifendurchlauf verkürzen ;)

Code:
<html>
  <head>
    <title>DataSet 2 Tree - Test</title>
    <style>
      .code {
        margin-bottom: 10px;
        margin-top: 0;
        border: 1px solid black;
        }
    </style>
  </head>
  <body><?php

// Test-Datenmenge
$dataset = array(
  array("id"=>1, "parent"=>0, "text"=>"Test1", "data"=>"Data1"),
  array("id"=>2, "parent"=>0, "text"=>"Test2", "data"=>"Data2"),
  array("id"=>3, "parent"=>1, "text"=>"Test3", "data"=>"Data3"),
  array("id"=>4, "parent"=>2, "text"=>"Test4", "data"=>"Data4"),
  array("id"=>5, "parent"=>1, "text"=>"Test5", "data"=>"Data5"),
  array("id"=>6, "parent"=>1, "text"=>"Test6", "data"=>"Data6"),
  array("id"=>7, "parent"=>4, "text"=>"Test7", "data"=>"Data7"),
  array("id"=>8, "parent"=>4, "text"=>"Test8", "data"=>"Data8"),
  array("id"=>9, "parent"=>3, "text"=>"Test9", "data"=>"Data9")
);


// rekursives füllen eines Trees aus einer Datenmenge
// die & vor den variablen sind Zeigerangaben, damit auch
// rauskommt was gewünscht wird
function filltree(&$node, $ds, $id=0) {
  // Datenmenge durchgehen (Tree-Root ist 0)
  foreach ($ds as $key=>$data) {
    // Wenn der parent mit der ID der zu füllenden Node übereinstimmt
    // eine Subnode erstellen
    if ($data["parent"] == $id) {
      // Zuordnung von Nutzdaten zu ID
      $node[$data["id"]] = array("text" => $data["text"], "data" => $data["data"]);
      // eingetragenes Node aus der Datenmenge entfernen
      unset($ds[$key]);
      // rekursiv füllen
      filltree($node[$data["id"]]["childs"], $ds, $data["id"]);
      // wenn es doch keine Kinder gab, den "childs"-Unterpunkt
      // wieder entfernen
      if (count($node[$data["id"]]["childs"]) == 0) { 
        unset($node[$data["id"]]["childs"]);
      }
    }
  }
}

// das eigentliche befüllen $tree wird dabei gleich mit definiert
filltree($tree, $dataset);

// Debug Ausgabe
echo "[b]print_r(\$tree):[/b]<pre class='code'>".print_r($tree, true)."</pre>";

?></body>
</html>

Matze 20. Feb 2008 15:23

Re: [PHP] Datensätze als Baum darstellen
 
Ah super, danke. :)

cruiser 20. Feb 2008 15:51

Re: [PHP] Datensätze als Baum darstellen
 
Noch ne kleine Änderung um die Durchlaufzeit zu optimieren ;)

Code:
// rekursives füllen eines Trees aus einer Datenmenge
// die & vor den variablen sind Zeigerangaben, damit auch
// rauskommt was gewünscht wird
function filltree(&$node, $ds, $id=0) {
  // Datenmenge durchgehen (Tree-Root ist 0)
  foreach ($ds as $key=>$data) {
    // Wenn der parent mit der ID der zu füllenden Node übereinstimmt
    // eine Subnode erstellen
    if ($data["parent"] == $id) {
      // Zuordnung von Nutzdaten zu ID
      $node[$data["id"]] = array("text" => $data["text"], "data" => $data["data"]);
      // eingetragenes Node aus der Datenmenge entfernen
      unset($ds[$key]);
      // rekursiv füllen
      $ds = filltree($node[$data["id"]]["childs"], $ds, $data["id"]);
      // wenn es doch keine Kinder gab, den "childs"-Unterpunkt
      // wieder entfernen
      if (count($node[$data["id"]]["childs"]) == 0) { 
        unset($node[$data["id"]]["childs"]);
      }
    }
  }

  return $ds;
}

Matze 20. Feb 2008 16:13

Re: [PHP] Datensätze als Baum darstellen
 
Öhm was habe ich davon, wenn ich $ds zurückgebe?
Das neue Array wird doch über &$node zurückgegeben, das andere interessiert mich eigentlich nicht, denn das habe ich davor ja auch schon, sonst könnte ich es nicht übergeben.

cruiser 20. Feb 2008 16:23

Re: [PHP] Datensätze als Baum darstellen
 
Aus $ds fliegen in der Rekursion weitere Datensätze, was dieses Tabellen-Array weiter kürzt und die Durchlaufzeit verringert. Die Rückgabe ist im Grunde nur für die Rekursion gut. Man könnt noch ne Prüfung auf $id != 0 vorsetzen ;)

Matze 20. Feb 2008 16:36

Re: [PHP] Datensätze als Baum darstellen
 
Achso stimmt!
Danke. irgendwann muss ich mir das mal ganz genau ansehen, denn die Rekursion macht mir doch jedesmal neu zu schaffen, auch wenn diese im Endeffekt immer ähnlich ist. :stupid:

omata 21. Feb 2008 00:49

Re: [PHP] Datensätze als Baum darstellen
 
Ich habe mich hier auch nochmal dran versucht.

Gruss
Thorsten

Matze 21. Feb 2008 07:43

Re: [PHP] Datensätze als Baum darstellen
 
Hallo Thorsten,

dankeschön. Dieses "CALL ..." habe ich noch nie verwendet, es klingt jedoch interessant, Abfragen so auslagern zu können. Die SQL-Abfrage scheint sehr komplex zu sein mit temporären Tabellen und ähnlichem. Mir persönlich kommt das ein wenig unsauber vor, doch da ich von dir bisher nur guten Source kenne, wird das schon sinnvoll sein, es so zu machen, auch wenn ich noch nicht alles nachvollziehen kann. :stupid:
Da das so viele Abfragen sind, würde ich sagen, ist das ganze recht langsam, doch das kann natürlich täuschen. Ich muss das bei Gelegenheit mal ausprobieren, falls ich das zum Laufen bekomme, auch wenn ich denke, dass ich bei der "einfacheren" Version bleibe, da ich deinen Code, selbst wenn ich ihn hundertprozentig verstehen würde, in ein paar Monaten nicht mehr nachvollzehen kann, denke ich.

Aktuell habe ich eine sehr simple "SELECT * FROM ..."-Abfrage, da ich wirklich alles aus der Tabelle benötige.

PS: Ich denke, dein verlinktes Thema wäre nicht schlecht für die Code-Library, zumindest der erste Beitrag. Den 2. könnte ich abtrennen und von der CL aus verlinken, da PHP in der CL weniger gut aufgehoben ist. :)

omata 21. Feb 2008 20:09

Re: [PHP] Datensätze als Baum darstellen
 
Über das CALL wird eine Stored Procedure auf dem Server ausgeführt. In der Prozedur wird die Baumstruktur der einen Tabelle ausgelesen und in der richtigen Reihenfolge ausgegeben.
Das interressante daran ist, das das verarbeiten der Baumstruktur sau schnell geht. Da hier bei jedem Schleifendurchlauf immer alle Elemente einer Baumebene angehängt werden. Bei einer Baumstiefe von 30 wird die Schleife auf dem Server also 30 mal ausgeführt, wieviele Baumelemente vorhanden sind ist dabei fast egal.
Du hast also nach außen nur eine SQL-Anfrage und bekommst einen, egal wie tief auch immer, verschachtelten Baum in einer super kurzen Zeit. Optimaler kannst du eine Baumstruktur nicht aus einer Datenbank ziehen.

Naja, du must das für dich entscheiden.

Gruss
Thorsten

Matze 21. Feb 2008 21:33

Re: [PHP] Datensätze als Baum darstellen
 
Hallo Thorsten,

das klingt natürlich sehr interessant. Ich werde auf jedenfall versuchen, den Code nachzuvollziehen, denn auf den ersten Blick sah dieser sehr verwirrend aus.
Ich werde das Array übrigens später cachen und nicht jedesmal aus der Datenbank auslesen und neu strukturieren. Folglich spielt die Zeit zum Auslesen keine allzu große Rolle.

Grüße

Matze 2. Mär 2008 21:05

Re: [PHP] Datensätze als Baum darstellen
 
Hallo,

mich treibt die Rekursion noch in den Wahnsinn. :wall:

Wie kann man denn anhand einer ID die übergeordneten Einträge bekommen?

Beispiel:

Code:
Pflanzen
    Löwenzahn
Tiere
    Säugetiere
        Giraffe
        Maus
        Katze
    Vögel
        Adler
Nun gebe ich die ID von "Adler" an eine rekusrive Funktion und zurückgegeben werden sollen dann die Daten von: Vögel, Tiere

Meine Theorie sieht so aus:
Ich gehe rekursiv so lange die Datensätze durch, bis die gegebene ID gefunden wird und dann wird nach dem rekursiven Aufruf (also beim "Zurücklaufen") das zu zurückgebene Array gefüllt.

Nur die Praxis endet entweder in einer Endlosrekursion oder gibt mir etwas komplett falsches aus. :?

Grüße

alcaeus 2. Mär 2008 21:28

Re: [PHP] Datensätze als Baum darstellen
 
Moin,

warum rekursiv wenn es auch iterativ geht? Die Loesung heisst Nested set. Ein paar Artikel dazu:
http://www.codeproject.com/KB/database/nestedsets.aspx (Die Queries sind zwar fuer MSSQL geschrieben, aber die Theorie sollte rueberkommen)
http://www.developersdex.com/gurus/articles/112.asp
http://www.developer.com/db/article.php/3517366

Der Vorteil der Sets ist, dass du die Elemente mit einem Query rausziehen kannst:
SQL-Code:
SELECT * FROM foo ORDER BY left_id ASC
Anschliessend musst du nur noch einmal ueber das Ergebnis drueberiterieren und entsprechend einruecken. So kriegst du auch bei riesigen (mein groesster Test war ~ 2 Mio. Blaetter) Baeumen kein Problem mitm Stack :)

Greetz
alcaeus

omata 2. Mär 2008 22:13

Re: [PHP] Datensätze als Baum darstellen
 
Hättest du dich mit meinem erwähnten Link beschäftigt, dann wäre das für dich kein Problem gewesen...

SQL-Code:
CREATE PROCEDURE `proc_GetParents`(NodeID INTEGER)
BEGIN
  DECLARE _Depth INT DEFAULT 1;
  DECLARE _Done INT DEFAULT 0;

  CREATE TEMPORARY TABLE tmpTable (
    id INT PRIMARY KEY, parentid INT, Depth INT, NodePath VARCHAR(1000)
  ) TYPE=HEAP;
  CREATE TEMPORARY TABLE tmpTable2 (
    id INT PRIMARY KEY, parentid INT, Depth INT, NodePath VARCHAR(1000)
  ) TYPE=HEAP;

  INSERT tmpTable (id, parentid, Depth, NodePath)
  SELECT id, parentid, _Depth, reihenfolge
  FROM nodes
  WHERE id = NodeID;
  IF ROW_COUNT() = 0 THEN
    SET _Done = 1;
  END IF;
  WHILE _Done = 0 DO
    SET _Depth = _Depth + 1;
    INSERT tmpTable2 (id, parentid, Depth, NodePath)
    SELECT t.id, t.parentid, _Depth, CONCAT(x.NodePath, '|', Reihenfolge)
    FROM nodes t
    INNER JOIN tmpTable x
      ON t.id = x.parentid
    WHERE x.Depth = _Depth-1;
    IF ROW_COUNT() = 0 THEN
      SET _Done = 1;
    END IF;
    INSERT tmpTable
    SELECT *
    FROM tmpTable2;
    DELETE FROM tmpTable2;
  END WHILE;

  SELECT t.id, t.parentid, t.bez, x.Depth, x.NodePath
  FROM nodes t
  INNER JOIN tmpTable x
    ON t.id = x.id
  ORDER BY x.NodePath DESC;

  DROP TEMPORARY TABLE tmpTable;
  DROP TEMPORARY TABLE tmpTable2;
END
Aber ich vermute, du möchtest das so nicht lösen. Schade, so geht das richtig leicht und schnell. Das ist das was alcaeus auch vorschlägt, ich habe es dir sogar schon für MySQL realisiert.
naja egal.

Gruss
Thorsten

Matze 2. Mär 2008 23:27

Re: [PHP] Datensätze als Baum darstellen
 
Hallo ihr beiden,

ich habe mich nun ein wenig mit den nested sets befasst und es ist schon erstaunlich, wie einfach das Auslesen funktioniert. Anhand dieses Artikels bastel ich mir erstmal eine ganz kleine Klasse dafür und versuche, ob ich damit klar komme.

Wie ich es dir, Thorsten, damals schon geschrieben habe, gefallen mir diese temporären Tabellen überhaupt nicht. Wenn ich Daten auslesen möchte, dann hätte ich gerne nur Lesezugriffe und will nicht zuerst mehrere temporäre Tabellen anlegen und nach dem Auslesen wieder löschen. Ich könnte mir vorstellen, dass das Risiko eines Datenverlustes auch ein wenig größer ist, wobei das vermutlich unwahrscheinlich ist.

Dennoch vielen Dank für deine Mühe und ich hoffe, du denkst nun nicht, dass ich undankbar sei. Mich freut es wirklich, dass du mir diese Möglichkeiten aufzeigst. Ich wusste vorher auch nicht, dass so etwas geht, dennoch gefällt mir diese Lösung, wie gesagt, nicht sonderlich.
Ich könnte mir aber gut vorstellen, dass andere über diesen SQL-Code dankbar sind, die eine andere Einstellung gegenüber dieser Vorgehensweise haben.

Einen guten Wochenanfang wünscht
Matze


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:57 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz