Achot: Ausgabe eingrenzen

Hallo,

ich möchte gerne nur Daten auslesen, die älter sind als der Wert, der in $tage steht

<?php 

$tage = 2;

$wk = $mysqli->prepare("SELECT id, datum FROM warenkorb WHERE datum");
$wk->execute();
$wk->bind_result($id, $datum);

while($wk->fetch()) { ?>
	
	<div><?php echo htmlspecialchars($id . " = " . $datum) ?></div>

<?php } ?>

Im Feld $datum steht z.B. der Wert 1483704072. Ich muss jetzt entweder die Tage oder das Datum umwandeln, dass beides irgendwie zusammen passt?

Der Hintergrund, ich möchte im zweiten Schritt wo jetzt das <div> steht ein Delete Befehl einbauen, der die Daten aus der Datenbank löscht.

Kleines Bespiel:

Im Feld Datum steht: 10.01.2017 In $tage steht der Wert 2. Also sollte morgen, sprich am 13.01.2017 alles gelöscht werden, wo im Feld Datum der Wert 10.01.2017 steht.

  1. Tach!

    ich möchte gerne nur Daten auslesen, die älter sind als der Wert, der in $tage steht

    Im Feld $datum steht z.B. der Wert 1483704072.

    Ist das ein Feld vom Typ Integer? Wenn ja, warum ist das kein DATE/DATETIME?

    Ich muss jetzt entweder die Tage oder das Datum umwandeln, dass beides irgendwie zusammen passt?

    Wenn es DATE/DATETIME wäre, gäbe es die Möglichkeit mit datum - INTERVAL 2 DAYS zwei Tage abziehen zu lassen.

    Wenn es Integer ist, dann wird es wohl ein Unix-Timestamp sein, so wie er in PHP gebräuchlich ist. Den musst du entweder in PHP berechnen oder von MySQL in ein Datumswert umwandeln (da gibt es zwei Funktionen, die das in beide Richtungen wandeln können) lassen und dann obiges Konstrukt dazunehmen.

    dedlfix.

    1. Hallo,

      Wenn es Integer ist, dann wird es wohl ein Unix-Timestamp sein, so wie er in PHP gebräuchlich ist. Den musst du entweder in PHP berechnen oder von MySQL in ein Datumswert umwandeln (da gibt es zwei Funktionen, die das in beide Richtungen wandeln können) lassen und dann obiges Konstrukt dazunehmen.

      wenn ich

      $wk = $mysqli->prepare("SELECT id, DATE( FROM_UNIXTIME( datum ) ) FROM warenkorb;
      

      nehme, erhalte ich folgende Ausgabe

      2017-01-06
      2017-01-25
      2017-01-06

      das heißt, die Umwandlung hat geklappt? Jetzt wollte ich mein WHERE um folgende Werte ergänzen

      WHERE DATE( FROM_UNIXTIME( datum ) ) < datum - INTERVAL 2 DAY
      

      jetzt erhalte ich keine Ausgabe mehr, allerdings auch keine Fehlermeldung.

      1. Tach!

        Jetzt wollte ich mein WHERE um folgende Werte ergänzen

        WHERE DATE( FROM_UNIXTIME( datum ) ) < datum - INTERVAL 2 DAY
        

        jetzt erhalte ich keine Ausgabe mehr, allerdings auch keine Fehlermeldung.

        Das ist ja auch nicht sinnvoll. datum ist weiterhin ein Integer-Wert und eine Datumsberechnung damit ist immer noch nicht zielführend.

        DATE( FROM_UNIXTIME( datum ) ) ermittelt erstmal einen ordentlichen Datumswert. Den willst du nun vergleichen, ob er älter als x Tage ist. "x Tage zurück" wäre welches Datum? NOW() - INTERVAL x DAY). Also den ersten Ausdruck kleiner-als-vergleichen mit diesem.

        dedlfix.

        1. Hallo,

          Das ist ja auch nicht sinnvoll. datum ist weiterhin ein Integer-Wert und eine Datumsberechnung damit ist immer noch nicht zielführend.

          DATE( FROM_UNIXTIME( datum ) ) ermittelt erstmal einen ordentlichen Datumswert. Den willst du nun vergleichen, ob er älter als x Tage ist. "x Tage zurück" wäre welches Datum? NOW() - INTERVAL x DAY). Also den ersten Ausdruck kleiner-als-vergleichen mit diesem.

          danke für deine Hilfe und Erklärung. So klappt es

          $wk = $mysqli->prepare("SELECT id FROM warenkorb 
                                   WHERE DATE( FROM_UNIXTIME( datum ) ) > NOW() - INTERVAL 2 DAY");
          $wk->execute();
          $wk->bind_result($id);
          
          while($wk->fetch()) { ?>
          	
          	<div><?php echo htmlspecialchars($id) ?></div>
          
          <?php } ?>
          

          Zu deiner Frage ob es DATE/DATETIME ist, nein ist es derzeit nicht. In vielen Tutorials lese ich, dass man darauf verzichten soll und lieber einen Timestamp nutzen soll. Was ist denn nun besser oder richtig?

          1. Tach!

            Zu deiner Frage ob es DATE/DATETIME ist, nein ist es derzeit nicht. In vielen Tutorials lese ich, dass man darauf verzichten soll und lieber einen Timestamp nutzen soll. Was ist denn nun besser oder richtig?

            Schreiben die Tutorials das einfach so ohne Begründung hin, oder hast du dir diese nur nicht gemerkt?

            Wenn das DBMS nichts mit dem Datum anfangen soll, kann das in beliebiger Form gespeichert werden. Dann spart man sich gegebenenfalls Umwandlungen, wenn das abfragende System ein anderes Format verwendet. Das ist aber, wie du soeben gesehen hast, nachteilig, wenn man dann doch das DBMS Datumsberechnungen durchführen lassen möchte. Es ist auch Mist, wenn man erstmal irgendwie speichert, und später feststellt, dass man es doch in einer verarbeitbaren Form benötigt. Deshalb ist es wohl eher sinnvoll, die im DBMS eingebauten Typen zu verwenden, und Ausnahmen nur mit einer guten Begründung zuzulassen. Das trifft übrigens für alle Typen zu, nicht nur für Datümer.

            Im Fall der Datums- und Zeitwerte ist es vergleichsweise einfach, PHP und MySQL zu kombinieren. Beim Schreiben nimmt man FROM_UNIXTIME(), beim Lesen UNIX_TIMESTAMP(), und beide Systeme sind glücklich.

            dedlfix.

  2. Hallo,

    ich muss nochmals etwas fragen. Kann ich innerhalb einer while-Schleifen kein DELETE ausführen? Meine Idee war

    <?php 
    
    $wk = $mysqli->prepare("SELECT id FROM warenkorb 
                              WHERE DATE( FROM_UNIXTIME( datum ) ) > NOW() - INTERVAL 2 DAY");
    $wk->execute();
    $wk->bind_result($id);
    
    while($wk->fetch()) { 
    	
    	$stmt = $mysqli->prepare("DELETE FROM warenkorb WHERE id=?");
    	$stmt->bind_param("s", $id);
    	$stmt->execute();
    	$stmt->close();
    
     } ?>
    

    Dadurch erhalte ich folgende Meldung

    Fatal error: Call to a member function bind_param() on a non-object

    Lass ich nur $id ausgeben, erhalte ich wieder eine Ausgabe.

    1. Tach!

      Fatal error: Call to a member function bind_param() on a non-object

      Das ist ein Folgefehler. Der eigentliche Fehler passiert im prepare().

      Beim Programmieren muss man stets beachten, wie sich die Funktionen im Fehlerfall verhalten. Bei PHP ist meist der Rückgabewert ein false statt dem sonst üblichen Wert. Das muss man auswerten und darf nicht blind weiterarbeiten, wenn man robuste Programme schreiben möchte, die nicht nur bei Schönwetter funktionieren sollen.

      dedlfix.

      1. Hallo,

        Das ist ein Folgefehler. Der eigentliche Fehler passiert im prepare().

        Beim Programmieren muss man stets beachten, wie sich die Funktionen im Fehlerfall verhalten. Bei PHP ist meist der Rückgabewert ein false statt dem sonst üblichen Wert. Das muss man auswerten und darf nicht blind weiterarbeiten, wenn man robuste Programme schreiben möchte, die nicht nur bei Schönwetter funktionieren sollen.

        auf http://stackoverflow.com/questions/2552545/mysqli-prepared-statements-error-reporting habe ich gelesen, dass ich die Fehler so abfangen kann

        while($wk->fetch()) { 
        	
        	$stmt = $mysqli->prepare("DELETE FROM warenkorb WHERE id =? ");
        
        	if ( false===$stmt ) {
        	  die('prepare() failed: ' . htmlspecialchars($mysqli->error));
        	}
        
        	$sc = $stmt->bind_param("i", $id);
        
        	if ( false===$rc ) {
        	  die('bind_param() failed: ' . htmlspecialchars($stmt->error));
        	}
        
        	$sc = $stmt->execute();
        	
        	if ( false===$rc ) {
        	  die('execute() failed: ' . htmlspecialchars($stmt->error));
        	}
        
        	$stmt->close();
        
         }
        

        Damit erhalte ich folgende Meldung

        prepare() failed: Commands out of sync; you can't run this command now

        Was mich wundert, ich nutze diesen Löschbefehl auf anderen Unterseiten und dort funktioniert er einwandfrei.

        1. Tach!

          auf http://stackoverflow.com/questions/2552545/mysqli-prepared-statements-error-reporting habe ich gelesen, dass ich die Fehler so abfangen kann

          Ja, aber die() ist die schlechtestmögliche Art und Weise, auf Fehler zu reagieren. Das kann man mal für das Testen machen, aber im Produktivcode ist es meist nicht sinnvoll, dem Anwender die Ausgabe abzubrechen und eine solche detaillierte Fehlermeldung zu präsentieren. Da sollte man sich schon etwas mehr Gedanken machen, wie die Anwendung im Fehlerfall reagieren soll - aus der Sicht des Anwenders der zu seinem Ziel kommen will, und aus der Sicht des Betreibers, der einerseits keinen Anwender (Kunden) verlieren möchte und andererseits Informationen zur Ursache benötigt.

          Damit erhalte ich folgende Meldung

          prepare() failed: Commands out of sync; you can't run this command now

          Was mich wundert, ich nutze diesen Löschbefehl auf anderen Unterseiten und dort funktioniert er einwandfrei.

          Das Löschen ist nicht das Problem an sich. Hast du mal nach dem Fehlermeldungstext gesucht? Du hast grad noch die Ergebnismenge des Select offen und kannst derzeit keine weiteren Statements absetzen.

          Wenn ich mich recht erinnere, holt PHP im Hintegrund die komplette Ergebnismenge ab und schließt das Resultset bereits beim Query, so dass das Problem gar nicht auftreten sollte. Ob das allerdings auch bei Prepared Statements so passiert, weiß ich grad nicht.

          Alternativen:

          • Das Delete mit WHERE-Klausel direkt ausführen und nicht viele Deletes mit je einer Löschung.
          • Erstmal alle Ergebnisse holen, in einem Array ablegen und dann erst über dieses Array laufen.

          dedlfix.

          1. Hallo dedlfix,

            • Das Delete mit WHERE-Klausel direkt ausführen und nicht viele Deletes mit je einer Löschung.
            • Erstmal alle Ergebnisse holen, in einem Array ablegen und dann erst über dieses Array laufen.

            oder in einer while-schleife die where-klausel zusammenbauen.

            Bis demnächst
            Matthias

            --
            Dieses Forum nutzt Markdown. Im Wiki erhalten Sie Hilfe bei der Formatierung Ihrer Beiträge.
          2. Hallo,

            hab den ganzen Nachmittag bisschen getestet und hab es auch hinbekommen und zwar so

            function test123($mysqli) {
                $stmt = $mysqli->prepare("SELECT id FROM warenkorb 
                                            WHERE DATE( FROM_UNIXTIME( datum ) ) > NOW() - INTERVAL 2 DAY");
                $stmt->execute();
                $stmt->bind_result($id);
                $stmt->store_result();
                    
                if($stmt->num_rows() >  0) {     
                        
                    while ($stmt->fetch()){
                        $test123[] = array( 
                           'id'  => $id
                        );
                    }
                  return $test123;
                 }
            }
            
            $EinfachTest = test123($mysqli); 
            
            if($EinfachTest > 0) {
            
            foreach($EinfachTest as $array){
            
                $stmt = $mysqli->prepare("DELETE FROM warenkorb WHERE id=? ");
                $stmt->bind_param("i", $array['id']);
                $stmt->execute();
                $stmt->close();
             }
            }
            else { echo "Leer"; }
            
            1. Hallo

              function test123($mysqli) {
                  $stmt = $mysqli->prepare("SELECT id FROM warenkorb 
                                              WHERE DATE( FROM_UNIXTIME( datum ) ) > NOW() - INTERVAL 2 DAY");
                  $stmt->execute();
                  $stmt->bind_result($id);
                  $stmt->store_result();
                      
                  if($stmt->num_rows() >  0) {     
                          
                      while ($stmt->fetch()){
                          $test123[] = array( 
                             'id'  => $id
                          );
                      }
                    return $test123;
                   }
              }
              
              $EinfachTest = test123($mysqli); 
              
              if($EinfachTest > 0) {
              
              foreach($EinfachTest as $array){
              
                  $stmt = $mysqli->prepare("DELETE FROM warenkorb WHERE id=? ");
                  $stmt->bind_param("i", $array['id']);
                  $stmt->execute();
                  $stmt->close();
               }
              }
              else { echo "Leer"; }
              

              Mal 'ne grundsätzliche Manöverkritik.

              Du holst dir in der Funktion test123 die Spalte „id“ aller Datensätze, in denen der Wert von datum mehr als zwei Tage in der Vergangenheit liegt. Danach nimmst du das Array der in der Funktion ermittelten Datensätze und führst in einer Schleife über das Array jeweils eine Löschanfrage aus.

              Das ganze Procedere funktioniert auch in einem einzigen Query. In dem ermittelst du mit einem Subquery die zu löschenden Datensätze und übergibst diese Liste in die WHERE-Klausel des Hauptquerys, der diese Datensätze löschen soll.

              DELETE FROM warenkorb
                WHERE id IN(
                  (SELECT id FROM warenkorb WHERE DATE(FROM_UNIXTIME(datum)) > NOW() - INTERVAL 2 DAY)
                );
              

              Da in der Abfrage keine variablen Werte vorhanden sind, ist nicht mal ein Parameter zu binden. Weiterhin kann der PHP-Code hier stark vereinfacht werden, da entweder die Funktion oder, bei Behalt der Funktion, die Ergebnisverarbeitung wegfällt.

              Tschö, Auge

              --
              Wenn man ausreichende Vorsichtsmaßnahmen trifft, muss man keine Vorsichtsmaßnahmen mehr treffen.
              Toller Dampf voraus von Terry Pratchett