Christian: Fehler in MySQL Abfrage

Hallo,

ich habe folgende Datenbankabfrage:

$query = "SELECT date FROM training ORDER BY date LIMIT 1";
$result = mysql_query($query);

while($row = mysql_fetch_object($result))
    {$date = $row->date;
    $firstweek = date("W",$date);
    $firstyear = date("Y",$date);
    $week = week($firstweek, $firstyear);
    $weekstart = $week[0];
    $weekend = $week[1];}

$now = time();
echo $now."<br />";

while ($weekstart <= $now) {

$query = "SELECT * FROM training WHERE date >= '".$weekstart."' AND date <= '".$weekend."' ORDER BY date";
  $result = mysql_query($query);

while($row = mysql_fetch_object($result))
    {$date = $row->date;
    $calweek = date("W",$date[0]);
    $year = date("Y",$date[0]);

echo $calweek."<br />";
    echo $year."<br />";

$numberofresults = mysql_num_rows($result);
    echo $weekstart."<br />";
    echo $numberofresults."<br />";

$calweek++;
    $week = week($calweek, $year);
    $weekstart = $week[0];
    $weekend = $week[1];
    echo $weekstart."<br />";}

}

Ein Testen dieser Abfrage ergibt folgende Ausgabe:

1189713333
01
1970
1186959600
1
342000

Fatal error: Maximum execution time of 30 seconds exceeded in /[...]/weekoutput.php on line 24

Das heißt die erste Ausgabe es die des aktuellen Timestamps, die zweite Ausgabe ist die ausgelesene Woche und die dritte das ausgelesene Jahr, und dort liegt offensichtlich der Fehler. Der erste Wochenbeginn (4. Ausgabe) ist zwar noch richtig, dafür findet die Abfrage auch noch die Anzahl der Einträge (5. Ausgabe), Dann kanns ja aber nicht mehr gehen, da das Programm mit einer Woche beginnt, die Jahrzente zurückliegt und für die es ja auch keine Einträge gibt.

Woran kann es liegen, dass das Skript aus dem ausgelesenen Datum nicht die richtige Woche bzw. Jahr auslesen kann?

Danke im Voraus,

Christian

  1. Hallo nochmal,

    ich habe mir jetzt $date[0] ausgeben lassen und kriege 1. Der nächste Eintrag unter date in der Datenbank ist aber 1187820000.

    Wo kann da der Fehler liegen?

    Danke,

    Christian

    1. Hallo Christian,

      ich habe mir jetzt $date[0] ausgeben lassen und kriege 1. Der nächste Eintrag unter date in der Datenbank ist aber 1187820000.

      erstens ist der "nächste" Datensatz in der Tabelle völlig uninteressant. Du
      fragst den kleinsten Wert in der Spalte "date" der Tabelle "training" ab. Da
      uns Deine gesamten Daten nicht vorliegen, können wir nicht wissen, was der kleinste Wert ist.

      Wo kann da der Fehler liegen?

      zweitens könnte der Fehler möglicherweise in Deiner Funktion

      week(foo, bar)

      liegen, deren Code - und deren Übergabewerte - Du uns freundlicherweise vorenthältst.
      Meine Suche auf php.net war jedenfalls erfolglos.

      Wie ich Dir bereits schrieb, ist Dein PHP-Code nicht nur falsch, sondern vermutlich auch überflüssig.

      Freundliche Grüße

      Vinzenz

  2. Hallo Christian,

    warum machst Du uns, Deinem Server und Dir selbst das Leben so schwer?

      
    
    > $query = "SELECT date FROM training ORDER BY date LIMIT 1";  
      
    [...] Es folgt eine Menge unkommentierten PHP-Codes ...  
      
    ... mit dem Einsatz einer undokumentierten Funktion:  
    
    >     $week = week($firstweek, $firstyear);  
      
    ...  
      
    
    > while ($weekstart <= $now) {  
      
    ... und dem Absetzen von Abfragen an die Datenbank in einer Schleife, ...  
      
    
    >   $query = "SELECT * FROM training WHERE date >= '".$weekstart."' AND date <= '".$weekend."' ORDER BY date";  
      
    ... bei der viele, viele Daten abgefragt werden, "SELECT * " ist ja so bequem ...  
      
    
    >   $result = mysql_query($query);  
      
    ... mögliche Fehler munter ignoriert werden,  
      
      
    
    >   while($row = mysql_fetch_object($result))  
    >     {$date = $row->date;  
      
    ... offensichtlich ein einziges Feld interessiert ...  
      
    
    >     $numberofresults = mysql_num_rows($result);  
      
    ... und die Anzahl der Datensätze, während der Rest weggeworfen wird ...  
      
    
    > }  
      
    und da Du irgendwo noch einen Fehler eingebaut hast, darfst Du Dich nicht wundern, dass Dein Skript viel zu lange braucht und zwangsweise beendet wird.  
      
    Ich vermute, Du möchtest in Erfahrung bringen, wieviele Einträge je Kalenderwoche und Jahr es in der Tabelle in einem bestimmten Zeitraum gibt. Wenn ja, so kannst Du dies mit einer relativ einfachen SQL-Abfrage bekommen, ohne irgendwelche überflüssigen PHP-Verrenkungen zu machen:  
      
    Da Du Deine Zeitangaben als UNIX-Timestamp abgespeichert hast, für den meiner  
    Meinung nach nichts spricht, als dass er in diversen Programmiersprachen  
    bequem zu bekommen ist, ist noch etwas zusätzlicher Aufwand erforderlich.  
    Grundsätzlich bin ich der Ansicht, dass man Datums- und Zeitangaben in  
    Datenbanken in Feldern entsprechenden Typs abspeichern sollte.  
      
    Zurück zu Deinem Problem:  
      
    Ein einziges Mal ausgeführt, sollte Dir  
      
    [code lang=sql]SELECT  
        -- ermittle die Kalenderwoche  
        [link:http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_week@title=WEEK]([link:http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_from-unixtime@title=FROM_UNIXTIME](date)) AS Woche,  
        -- ermittle den Jahresanteil  
        [link:http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_year@title=YEAR](FROM_UNIXTIME(date)) AS Jahr,  
        -- zähle wieviele Einträge es davon gibt  
        [link:http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_count@title=COUNT](date) AS Anzahl  
    FROM training  
    -- in einem bestimmten, von Dir vorgegebenen Zeitraum (hier als UNIX-Timestamps  
    WHERE date [link:http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_between@title=BETWEEN] dein_start AND dein_ende  
    -- gruppiert nach Kalenderwoche und Jahr, d.h. für jede Kombination von  
    -- Kalenderwoche und Jahr genau eine Zeile  
    GROUP BY Woche, Jahr
    

    Dein Ergebnis liefern. Möchtest Du das Ergebnis über die kompletten Daten Deiner Tabelle sehen, so lass die WHERE-Klausel einfach weg.

    Freundliche Grüße

    Vinzenz

    1. Hello,

      warum machst Du Deinem Server das Leben so schwer?

      ... und dem Absetzen von Abfragen an die Datenbank in einer Schleife, ...

      ohne das Resultset zwischendurch wieder freizugeben.

      Das führt i.d.R. zu lost handles, wenn PHP nicht inzwischen eine eigene Lösung eingebaut hat.
      Da bin ich im Moment überfragt, ob bei (unsachgemäßer) Wiederbelegung eines Handles der damit verknüpfte Speicherbereich wieder freigegeben wird.

      Harzliche Grüße vom Berg
      http://www.annerschbarrich.de

      Tom

      --
      Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
      Nur selber lernen macht schlau

  3. Hallo Christian,

    es hat mir keine Ruhe gelassen ...

    ich habe folgende Datenbankabfrage:

    ... die richtige Lösung für Dein Problem habe ich Dir bereits gegeben, sie
    sollte wie folgt lauten:

    SELCT  
        -- ermittle die Kalenderwoche  
        [link:http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_week@title=WEEK]([link:http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_from-unixtime@title=FROM_UNIXTIME](date)) AS Woche,  
        -- ermittle den Jahresanteil  
        [link:http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_year@title=YEAR](FROM_UNIXTIME(date)) AS Jahr,  
        -- zähle wieviele Einträge es davon gibt  
        [link:http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_count@title=COUNT](date) AS Anzahl  
    FROM training  
    -- gruppiert nach Kalenderwoche und Jahr, d.h. für jede Kombination von  
    -- Kalenderwoche und Jahr genau eine Zeile  
    GROUP BY Woche, Jahr  
    ORDER BY Jahr, Woche
    

    Warum Dein Code in eine Endlosschleife läuft, ist mir inzwischen auch klar:

      
    
    > $query = "SELECT date FROM training ORDER BY date LIMIT 1";  
    > $result = mysql_query($query);  
    >   
    > while($row = mysql_fetch_object($result))  
    >     {$date = $row->date;  
    >     $firstweek = date("W",$date);  
    >     $firstyear = date("Y",$date);  
      
    # Deine Funktion week scheint richtig den Timestamp des  
    # Wochenanfangs und des Wochenendes einer gegebenen Kalenderwoche  
    # in einem gegebenen Jahr zu berechnen  
      
    
    >     $week = week($firstweek, $firstyear);  
    >     $weekstart = $week[0];  
    >     $weekend = $week[1];}  
    >   
    > $now = time();  
    > echo $now."<br />";  
      
    # Ausgabe:  
    
    > 1189713333  
      
      
    
    > while ($weekstart <= $now) {  
      
    # Solange der Inhalt von $weekstart nicht größer ist als der aktuelle Timestamp  
    # 1. Durchlauf  
    # $weekstart = 1186959600  
    # $now       = 1189713333  
    # Die äußere Schleife wird abgearbeitet  
      
    # 2. Durchlauf  
    # $weekstart = 342000  
    # $now       = 1189713333  
    # Die äußere Schleife wird abgearbeitet :-)  
      
    # 3. Durchlauf und folgende Durchläufe  
    # Da die innere Schleife übersprungen wurde,  
    # da die Query keine Ergebnisse lieferte, bist Du in eine Endlosschleife  
    # geraten. In diese kommst Du bei Deinem Skript immer, wenn die Query in  
    # der Schleife _kein_ Ergebnis liefert. Du hast hier also einen logischen  
    # Fehler eingebaut. Denn das gilt für _jede_ Woche, in der es keinen  
    # Eintrag gibt, z.B. eine Trainingspause, Urlaub oder was weiß ich sonst ...  
      
      
    # $weekstart = 342000  
    # $now       = 1189713333  
    # Die äußere Schleife wird abgearbeitet :-)  
      
      
    
    >   
    >   $query = "SELECT * FROM training WHERE date >= '".$weekstart."' AND date <= '".$weekend."' ORDER BY date";  
    >   $result = mysql_query($query);  
      
    # 1. Durchlauf: Es gibt genau 1 Ergebnis, siehe Deine fünfte Ausgabe:  
    
    > 1  
      
    # 2. Durchlauf der äußeren Schleife  
    # Es gibt kein Ergebnis, denn Du hast keine Werte aus KW2 von 1970  
      
    
    >   while($row = mysql_fetch_object($result))  
      
    # 1. Durchlauf der äußeren Schleife:  
    # Es gibt ein Ergebnis, die innere Schleife wird abgearbeitet.  
      
    # 2. Durchlauf der äußeren Schleife  
    # Es gibt keine Ergebnisse, die innere Schleife wird nicht abgearbeitet  
      
    
    >     {$date = $row->date;  
      
    # Laut Deinem zweiten Posting bekommst Du folgenden Wert:  
    # 1187820000  
      
    # Und nun kommt einer von mehreren Fehlern:  
      
    
    >     $calweek = date("W",$date[0]);  
      
    # Du greifst auf $date, das eine Integer enthält, wie auf ein Array zu.  
    # PHP konvertiert daher $date in eine Zeichenkette und Du greifst über  
    # $date[0] auf das erste Zeichen dieser Zeichenkette zurück, hat also  
    # den Wert 1  
      
    # 1. Durchlauf:  
    #     $calweek = date("W", 1);  
    #     $year    = date("Y", 1);  
      
    # Da der UNIX-Timestamp ab dem 1. Januar 1970 läuft,  
      
    
    >     $year = date("Y",$date[0]);  
      
    # ergeben sich logischerweise die Ausgaben  
      
    
    >     echo $calweek."<br />";  
      
    #     01  
      
    
    >     echo $year."<br />";  
      
    #     1970  
      
    
    >     $numberofresults = mysql_num_rows($result);  
    >     echo $weekstart."<br />";  
      
    #     1186959600  
      
    
    >     echo $numberofresults."<br />";  
      
    #     1  
      
    # Anmerkung:  
    # Bei mehreren Einträgen in einem Intervall bekämst Du auch entsprechend  
    # viele Zeilen zu dieser Woche, das ist bestimmt nicht gewünscht.  
      
      
    
    >     $calweek++;  
      
    #     $calweek enthält nun den Wert 2  
      
    
    >     $week = week($calweek, $year);  
    >     $weekstart = $week[0];  
      
    #  $weekstart enthält nun den Timestamp des Beginns der zweiten KW 1970 :-)  
      
    
    >     $weekend = $week[1];  
      
    #  $weekend den Timestamp des Endes der zweiten KW 1970.  
    # und es ist ein ganz schlimmer Fehler, diese Änderung hier an dieser Stelle  
    # zu machen. Du landest nämlich _immer_ in einer Endlosschleife, sobald es  
    # in irgendeiner Woche keinen Eintrag gibt. Auch dann, wenn Du obigen Fehler  
    # bereinigt hast.  
      
    
    >     echo $weekstart."<br />";}  
      
    
    > 342000  
      
    
    >   
    > }  
      
    # 1. Durchlauf ist abgearbeitet, nun geht es an das Überprüfen der  
    # Abbruchbedingung - siehe oben  
      
    # 2. Durchlauf:  
    # Die innere Schleife wurde nicht abgearbeitet  
    # $weekstart hat immer noch den Wert 342000 ...  
    # Es geht zum dritten Durchlauf ...  
    
    

    Ein Testen dieser Abfrage ergibt folgende Ausgabe:

    1189713333
    01
    1970
    1186959600
    1
    342000

    Fatal error: Maximum execution time of 30 seconds exceeded in /[...]/weekoutput.php on line 24

    Wie von mir angemerkt, hilft Dir eine Beseitigung Deines fehlerhaften Zugriffs
    $date statt $date[0] nicht weiter.

    Bei mehr als einem Eintrag in der Woche bekommst Du unerwünschte
    Mehrfachausgaben für die gleiche Woche, bei einer Woche ohne Einträge
    landest Du wieder in der Endlosschleife.
    Zudem verbraucht Dein Skript unnötige Ressourcen, selbst wenn ein Index auf
    der Spalte "date" liegt.

    Löse daher Deine Aufgabe mit SQL (siehe oben) statt mit PHP.

    Freundliche Grüße

    Vinzenz