Maik W. aus E.: Rekursion, Zähler und mehrdimensionale Arrays

Tach zusammen,

ich habe folgendes:

In einer DB-Tabelle habe ich eine Menge von Behandlungen, die sind untereinander rekursiv über das Feld 'folgebehandlung_zu' verknüpft. Jetzt möchte ich etwas basteln, das mir am Ende ungefähr folgendes ausgibt:

Behandlungen:

mit 1 Folgebehandlung: 27
mit 2 Folgebehandlungen: 148
mit 3 Folgebehandlungen: 42
...

Ich habe das jetzt mal zerlegt und klein mit Folgendem angefangen:

  
  
 function Beh_Count($b_id){  
  
  static $counter=0;  
  $nachbehandlung = @mysql_result(mysql_query("SELECT behandlung_id FROM ueb_behandlungen  
                                             WHERE folgebehandlung_zu='$b_id'"),0,0);  
  
   if($nachbehandlung>0){  
    Beh_Count($nachbehandlung);  
    $counter = $counter+1;  
   }  
  
 echo "$counter"; //zeig mal  
 }  

Die Rekursion funktioniert, der Zähler geht entsprechend hoch, aber wie kann den Wert "aufbewahren", um die Durchgänge vernünftig zu zählen?

Dann muß ich ja "nur noch" was drumrum bauen, um die jeweiligen Elemente mit zwei, drei vier Durchläufen an sich zu zählen...

Kann man jedem Element der Behandlungen-Menge einen Index mitgeben oder einen äußeren Zähler einbauen?

http://forum.de.selfhtml.org/archiv/2005/4/t106720/#m661800 scheint etwas für mich zu sein, verstehen tue ich's aber nicht...

Besten Dank

http://www.gruss-aus-essen.de

Maik

  1. Hallo Mike,

    ich habe folgendes:

    Wenn du auf die Datenbank rekursiv zugreifst läufts du Gefahr das du bei entsprechend tiefen Verschachtelungen durch zig Query deine Datenbankverbindung verstopfst. Ich würde überlegen ob es nicht eine Möglichkeit gibt eine Query zu verwenden oder aber die Rekursionen in php zu zählen.

    Die Rekursion funktioniert, der Zähler geht entsprechend hoch, aber wie kann den Wert "aufbewahren", um die Durchgänge vernünftig zu zählen?

    Tut es hier nicht einfach ein

    return $counter;

    in der Funktion den du bei dem Funktionsaufruf von außerhalb der Funktion abfängst?!

    Gruß,

    ARne.

  2. echo $begrüßung;

    In einer DB-Tabelle habe ich eine Menge von Behandlungen, die sind untereinander rekursiv über das Feld 'folgebehandlung_zu' verknüpft.

    Das rekursive Absetzen von vielen kleinen Querys gegen die Datenbank ist nicht so günstig. Jedes Mal fallen Absetzen der Query, Ausführen der Abfrage, Ergebnis zurückliefern an. Frage lieber alle Daten im Ganzen ab und durchlaufe dann die beispielsweise in einem Array abgelegte Ergebnismenge.

    Jetzt möchte ich etwas basteln, das mir am Ende ungefähr folgendes ausgibt:
    Behandlungen:
    mit 1 Folgebehandlung: 27
    mit 2 Folgebehandlungen: 148
    mit 3 Folgebehandlungen: 42
    ...

    SELECT behandlung_id FROM ueb_behandlungen WHERE folgebehandlung_zu='$b_id'

    Bist du sicher, dass deine Datenhaltung, so wie sie ist, günstig ist? Der Vorgänger der Behandlung Y ist die Behandlung X. Das erscheint mir von hinten aufgezäumt zu sein. Es gibt Behandlung X, die Nachfolgebehandlung dazu ist Y - wäre für mich logischer, einfacher verständlich.

    http://forum.de.selfhtml.org/archiv/2005/4/t106720/#m661800 scheint etwas für mich zu sein,

    Das ist nur dann "das richtige" wenn du bereits eine Baumstruktur vorliegen hättest. Die Baumstruktur durch viele kleine Datenbankabfragen zu simulieren ist ja wie gesagt ungünstig.

    Gegeben sei eine Datenmenge in einem Array ($behandlungen), dessen Keys die Behandlungen (Behandlungs-IDs) sind und dessen Values die dazugehörenden Nachfolgebehandlungen. (Kann man mit einem einfachen Select behandlung, nachfolger from... abfragen)

    Für jede Behandlung wollen wir den nachfolgenden Schwanz durchlaufen, der aus aneinandergehängten Nachfolgebehandlungen besteht, bis irgendwann keine mehr kommt (=0). Das Ergebnis zu jeder ID wird zwischengespeichert ($countNachfolger).

    $behandlungen wird als Referenz übergeben - global $behandlungen wäre eine (unschöne) Alternative.

    function countBehandlungen(&$behandlungen, $id, $count = 0) { // ist nur "braingetestet"
      if ($behandlungen[$id]) // Nachfolger vorhanden?
        return countBehandlungen($behandlungen[$id], ++$count); // Abstieg und zählen
      else
        return $count; // keiner? Ergebnis zurückgeben
    }

    $countNachfolger = array();
    foreach ($behandlungen as $id => $unwichtig)
      $countNachfolger[$id] = countBehandlungen($behandlungen, $id);

    array_count_values($countNachfolger) liefert dann die geforderten Summen.

    (Eine Optimierung kann man noch einbauen, wenn man sich einmal gezählte Schwänze in einem weiteren Array merkt.)

    echo "$verabschiedung $name";

    1. Tach auch dedlfix,

      In einer DB-Tabelle habe ich eine Menge von Behandlungen, die sind untereinander rekursiv über das Feld 'folgebehandlung_zu' verknüpft.

      Das rekursive Absetzen von vielen kleinen Querys gegen die Datenbank ist nicht so günstig. Jedes Mal fallen Absetzen der Query, Ausführen der Abfrage, Ergebnis zurückliefern an. Frage lieber alle Daten im Ganzen ab und durchlaufe dann die beispielsweise in einem Array abgelegte Ergebnismenge.

      Theoretisch war mir das schon klar, ich hatte mich jetzt nur so in die "Einzelwert-Version" verbissen, daß ich nicht mehr zurückkam.

      SELECT behandlung_id FROM ueb_behandlungen WHERE folgebehandlung_zu='$b_id'

      Bist du sicher, dass deine Datenhaltung, so wie sie ist, günstig ist? Der Vorgänger der Behandlung Y ist die Behandlung X. Das erscheint mir von hinten aufgezäumt zu sein.

      Ist es auch, im wahrsten Sinne des Wortes. Es trudeln immer noch "Nachbehandlungseinträge" ein, die dann einfach die Referenz auf die Vorbehandlung eingetragen kriegen und man muß die Verknüpfungen nicht durch alle Datensätze schieben...

      Gegeben sei eine Datenmenge in einem Array ($behandlungen), dessen Keys die Behandlungen (Behandlungs-IDs) sind und dessen Values die dazugehörenden Nachfolgebehandlungen. (Kann man mit einem einfachen Select behandlung, nachfolger from... abfragen)

      Für jede Behandlung wollen wir den nachfolgenden Schwanz durchlaufen, der aus aneinandergehängten Nachfolgebehandlungen besteht, bis irgendwann keine mehr kommt (=0). Das Ergebnis zu jeder ID wird zwischengespeichert ($countNachfolger).

      $behandlungen wird als Referenz übergeben - global $behandlungen wäre eine (unschöne) Alternative.

      function countBehandlungen(&$behandlungen, $id, $count = 0) { // ist nur "braingetestet"
        if ($behandlungen[$id]) // Nachfolger vorhanden?
          return countBehandlungen($behandlungen[$id], ++$count); // Abstieg und zählen
        else
          return $count; // keiner? Ergebnis zurückgeben
      }

      $countNachfolger = array();
      foreach ($behandlungen as $id => $unwichtig)
        $countNachfolger[$id] = countBehandlungen($behandlungen, $id);

      Wunderbar, sorum ist es natürlich sehr elegant...

      array_count_values($countNachfolger) liefert dann die geforderten Summen.

      Ja, aber...
      wenn ich mir array_count_values($countNachfolger) ausgeben lasse, kommt die Gesamtzahl der Behandlungen als einziger Wert im Array. Fehlt da noch was (noch ein Index), oder habe ich da was übersehen?

      Danke und
      http://www.gruss-aus-essen.de

      Maik

      1. echo $begrüßung;

        array_count_values($countNachfolger) liefert dann die geforderten Summen.

        Ja, aber...
        wenn ich mir array_count_values($countNachfolger) ausgeben lasse, kommt die Gesamtzahl der Behandlungen als einziger Wert im Array. Fehlt da noch was (noch ein Index), oder habe ich da was übersehen?

        Verwechselst du count() mit array_count_values()?

        $countNachfolger enthält in den Keys die Behandlungs-ID und in den Values die Anzahl der Nachfolger:
         1 => 5
         2 => 1
         3 => 5

        Ergebnis von array_count_values() wäre dann
        5 => 2
        1 => 1
        also in den Keys die Anzahl der Nachfolger und in den Values wieviele IDs diese Anzahl hatten.

        echo "$verabschiedung $name";

        1. Tach auch dedlfix,

          array_count_values($countNachfolger) liefert dann die geforderten Summen.

          Ja, aber...
          wenn ich mir array_count_values($countNachfolger) ausgeben lasse, kommt die Gesamtzahl der Behandlungen als einziger Wert im Array. Fehlt da noch was (noch ein Index), oder habe ich da was übersehen?

          Verwechselst du count() mit array_count_values()?

          Nein, es liegt an einer Dimension zuviel im Ausgangsarray:
          Wenn ich mir $behandlungen ausgeben lasse, steht da

          Array
          (
              [0] => Array
                  (
                      [4] => 0
                  )

          [1] => Array
                  (
                      [5] => 4
                  )

          [2] => Array
                  (
                      [6] => 5
                  )
          ....

          bauen lasse ich mir das Array mit

            
           while($row = mysql_fetch_array($behandlungen_nachf_query)){  
                 $behandlungen[]=array($row[behandlung_id]=>$row[folgebehandlung_zu]);  
           }  
          
          

          und bekomme beim besten Willen den "ersten Key" nicht weg...

          http://www.gruss-aus-essen.de

          Maik

          1. echo $begrüßung;

            bauen lasse ich mir das Array mit

            while($row = mysql_fetch_array($behandlungen_nachf_query)){

            $behandlungen[]=array($row[behandlung_id]=>$row[folgebehandlung_zu]);
            }

            
            >   
            > und bekomme beim besten Willen den "ersten Key" nicht weg...  
              
            $row[behandlung\_id] ist doch eindeutig, d.h. es kommen darin keine doppelten werte vor, oder? Dann mach es doch so: $behandlungen[$row[behandlung\_id]] = $row[folgebehandlung\_zu];  
              
              
            echo "$verabschiedung $name";
            
            1. Tach auch dedlfix,

              $row[behandlung_id] ist doch eindeutig, d.h. es kommen darin keine doppelten werte vor, oder? Dann mach es doch so: $behandlungen[$row[behandlung_id]] = $row[folgebehandlung_zu];

              Aber ja, und das Array sieht gut aus!

              Jetzt zu der eigentlichen Funktion:
              Die Übergabe per Referenz gibt 'ne Fehlermeldung aus (Fatal error: Only variables can be passed by reference), also doch

              global $behandlungen... Ähh nee, geht auch ohne, wird der Funktion ja übergeben.

              Jetzt bleibt print_r( array_count_values($countNachfolger)) immer noch bei Array ( [0] => 438 ) stehen...

              noch 'ne Idee?

              http://www.gruss-aus-essen.de

              Maik

              1. echo $begrüßung;

                Die Übergabe per Referenz gibt 'ne Fehlermeldung aus (Fatal error: Only variables can be passed by reference)

                function foo($bar) {...} und dann foo(&$baz) ist nicht mehr gewünscht. Besser:
                function foo(&$bar) {...} und dann foo($baz)

                Jetzt bleibt print_r( array_count_values($countNachfolger)) immer noch bei Array ( [0] => 438 ) stehen...
                noch 'ne Idee?

                Zeig doch mal den relevanten Teil im Ganzen.

                echo "$verabschiedung $name";

                1. Tach auch dedlfix,

                  Zeig doch mal den relevanten Teil im Ganzen.

                  Aaaalso:

                    
                  //Listen holen  
                    
                   $behandlungen_nachf_query = mysql_query("SELECT b.behandlung_id, b.folgebehandlung_zu  
                    FROM blabla WHERE blabla...");  
                  echo mysql_error();  
                    
                       // Array bauen  
                   while($row = mysql_fetch_array($behandlungen_nachf_query)){  
                          $behandlungen[$row[behandlung_id]] = $row[folgebehandlung_zu];  
                   }  
                    
                  ?> <pre><?  
                   //print_r($behandlungen);  
                  ?></pre><?  
                    
                    
                    function countBehandlungen($behandlungen, $id, $count = 0) { // ist nur "braingetestet"  
                    global $behandlungen;  
                    
                     if ($behandlungen[$id]) // Nachfolger vorhanden?  
                      return countBehandlungen($behandlungen[$id], ++$count); // Abstieg und zählen  
                     else  
                      return $count; // keiner? Ergebnis zurückgeben  
                    
                     }  
                    
                    
                    $countNachfolger = array();  
                    foreach ($behandlungen as $id => $unwichtig){  
                    $countNachfolger[$id] = countBehandlungen($behandlungen, $id);  
                    
                    }  
                  print_r(array_count_values($countNachfolger));  
                    
                  
                  

                  Mehr war's eigentlich gar nicht...

                  Danke!

                  http://www.gruss-aus-essen.de

                  Maik

                  1. echo $begrüßung;

                    Aaaalso:

                    Hmmm. Ich hatte doch der Einfachkeit halber die Logik mit dem Nachfolgen umgedreht. (-:

                    //Listen holen

                    $behandlungen_nachf_query = mysql_query("SELECT b.behandlung_id, b.folgebehandlung_zu
                      FROM blabla WHERE blabla...");
                    echo mysql_error();
                         // Array bauen
                    while($row = mysql_fetch_array($behandlungen_nachf_query)){
                            $behandlungen[$row[behandlung_id]] = $row[folgebehandlung_zu];
                    }

                      
                    Also müssen die Behandlungen erstmal korrigiert werden.  
                      
                    ~~~php
                    // Logik umdrehen  
                    $falschrum = $behandlungen;  
                    foreach ($behandlungen as $id => $unwichtig)  
                      $behandlungen[$id] = 0; // IDs erhalten und mit 0 (= keine folgebehandlung) initialisieren  
                    foreach ($falschrum as $nachfolger => $id)  
                      if ($id != 0)  
                        $behandlungen[$id] = $nachfolger;
                    

                    [code lang=php]
                      function countBehandlungen($behandlungen, $id, $count = 0) { // ist nur "braingetestet"
                                                 ^

                    hier ein & einschieben und das nachfolgende global $behandlungen; weglassen

                    global $behandlungen;

                    if ($behandlungen[$id]) // Nachfolger vorhanden?
                        return countBehandlungen($behandlungen[$id], ++$count); // Abstieg und zählen
                                                 ^

                    hier muss auch noch einmal $behandlungen, eingefügt werden, also:
                        return countBehandlungen($behandlungen, $behandlungen[$id], ++$count); // Abstieg und zählen

                    Und dann sollte es gehen. Jedenfalls ging es mit meinem Test-Datenbestand.

                    echo "$verabschiedung $name";

                    1. Tach auch dedlfix,

                      Hmmm. Ich hatte doch der Einfachkeit halber die Logik mit dem Nachfolgen umgedreht. (-:

                      Ach so, deswegen...

                      Also müssen die Behandlungen erstmal korrigiert werden.

                      ja,

                      [code lang=php]
                        function countBehandlungen($behandlungen, $id, $count = 0) {
                                                   ^
                      hier ein & einschieben und das nachfolgende global $behandlungen; weglassen

                      return countBehandlungen($behandlungen[$id], ++$count); // Abstieg und zählen
                                                   ^
                      hier muss auch noch einmal $behandlungen, eingefügt werden,

                      Und dann sollte es gehen. Jedenfalls ging es mit meinem Test-Datenbestand.

                      Nicht nur das, auch mit mit meinen Echtdaten läuft es 1a!

                      Besten Dank und
                      http://www.gruss-aus-essen.de

                      Maik