hawkmaster: Welche Variante zum prüfen ob Datensatz vorhanden? (PDO)

Hallo zusammen,
Ich stelle gerade meine "mysql()" Funktionen und Abfragen auf PDO um.
An mehreren Stellen im Code kommt in etwa folgendes vor.
Ein "Select" Element.
Wenn kein Eintrag in Tabelle => dann Anzeige im Select "Keine Einträge".
Wenn Einträge gefunden => Anzeigen
Bisher hatte ich es so gemacht:
--------------------------------------------------------------------------
<select name="users">
<?php
$result = mysql_query("SELECT username, uservorname FROM testuser");
$anzahl=mysql_num_rows($result);
if ($anzahl == 0){
 echo"<option selected value="Keine Eintraege">Keine Eintraege</option>\n";
}
else{
while($var = mysql_fetch_array($result)){
 echo"<option value="$var[username]">$var[uservorname]</option>\n";
}
}
?>
</select>
--------------------------------------------------------------------------
mich würde eure Expertenmeinung interessieren welche der nachfolgenden Varianten am besten ist oder ob es hinsichtlich Perforance Unterschiede gibt.

Möglichkeit 1:

$result = $DBO->query("SELECT username, uservorname FROM testuser");
if ($result->fetchColumn() < 1) {
 echo"<option selected value="Keine Eintraege">Keine Eintraege</option>\n";
}
else{
while($var = $result->fetch(PDO::FETCH_ASSOC)){
 echo"<option value="$var[username]">$var[uservorname]</option>\n";
}

Möglichkeit 2:

$sqlcheckuser = $DBO->query("SELECT count(*) as anzahl FROM testuser");
$sql_datauser = $sqlcheckuser->fetch(PDO::FETCH_ASSOC);
$anzahl = $sql_datauser['anzahl'];
if ($anzahl == 0){
 echo"<option selected value="Keine Eintraege">Keine Eintraege</option>\n";
}else{
 $result = $DBO->query("SELECT username, uservorname FROM testuser");
 while($var = $result->fetch(PDO::FETCH_ASSOC)){
  echo"<option value="$var[username]">$var[uservorname]</option>\n";
 }
}

Möglichkeit 3:

try {
    $result = $pdo->query('SELECT username, uservorname FROM testuser')->fetchAll();
  } catch (PDOException $ex) {
    $result = array();
 }
$anzahl = count($result);
if ($anzahl == 0){
 echo"<option selected value="Keine Eintraege">Keine Eintraege</option>\n";
}else{
 foreach ($result as $row) {
  echo"<option value="$row[0][username]">$row[0][uservorname]</option>\n";
 }
}

Mir persönlich gefällt eigentlich Variante 1 mit "fetchColumn" ganz gut.
Möglichkeit 2 ist vom Code her am aufwändigsten.

Oder besser Variante 3 mit "try" und "catch"?

Oder gibt es noch eine viel einfachere Lösung?
Bin auf eure Meinung gespannt.
vielen Dank und viele Grüße
hawk

  1. echo $begrüßung;

    Mir persönlich gefällt eigentlich Variante 1 mit "fetchColumn" ganz gut.
    Möglichkeit 2 ist vom Code her am aufwändigsten.
    Oder besser Variante 3 mit "try" und "catch"?

    Eine Fehlerbehandlung brauchst du in jedem Fall. try-catch fehlt also in den beiden anderen Varianten.
    Da du die Ergebnismenge in jedem Fall brauchst, halte ich eine Extra-Zählung für weniger sinnvoll, da aus der Ergebnismenge die Anzahl der Datensätze feststellbar ist. Die abgefragte Datenmenge wird sicher auch überschaubar klein sein, so dass die Zwischenspeicherung in einem Array durch fetchAll() nicht sonderlich ins Gewicht fallen wird.
    Ansonsten solltest du dir selbst ein Urteil über die beste Variante in diesem Fall bilden. Dafür kannst du zum einen Laufzeitmessungen der drei Varianten anstellen, und zum anderen den Aufwand beim Verstehen und Pflegen des jeweiligen Codestücks bewerten. Stell dir dabei vor, es sei einige Zeit vergangen, in der du nichts mit PDO zu tun hattest, und dir die genaue Arbeitsweise der verwendeten Methoden wieder entfallen ist. Kannst du dann sofort beim Anblick des Codes darauf schließen, was du damals beim Verfassen damit bezweckt hattest? Möglicherweise fehlen auch noch ein paar Kommentare, die dir beim Verständnis helfen.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,
      danke für deinen Kommentar.

      Klar Kommentare sind wichtig. Habe ich eigentlich auch immer im Code drin.
      Mir ging es ja eigentlich nur drum wie DU das als Experte oder zumindest als erfahrener User so etwas umsetzen würdest.
      Ich möchte gerne gleich von Anfang an Fehler vermeiden.

      Würdest du denn die 3 Variante für vertretbar halten?
      try {
          $result = $pdo->query('SELECT username, uservorname FROM testuser')->fetchAll();
        } catch (PDOException $ex) {
          $result = array();
       }
      $anzahl = count($result);
      if ($anzahl == 0){
       echo"<option selected value="Keine Eintraege">Keine Eintraege</option>\n";
      }else{
       foreach ($result as $row) {
        echo"<option value="$row[0][username]">$row[0][uservorname]</option>\n";
       }
      }

      vielen Dank und viele Grüße
      hawk

      1. echo $begrüßung;

        Mir ging es ja eigentlich nur drum wie DU das als Experte oder zumindest als erfahrener User so etwas umsetzen würdest.

        Bestmöglich. Wie das konkret aussieht kommt auf die Begleitumstände an. Das Verhältnis von Aufwand zu Nutzen ist ebenfalls nicht zu vernachlässigen. Bei wenigen Datensätzen nehmen sich die Methoden nichts. Welche davon ist am ehesten verständlich, wartbar und dabei möglichst elegant? Das ist die Frage, die dann zur Entscheidung führt.

        Ich möchte gerne gleich von Anfang an Fehler vermeiden.

        Das ist nicht unbedingt erstrebenswert, denn aus Fehlern kann man am besten lernen. Deswegen gab ich dir ja nur den allgemeinen Tipp, selbst Messungen durchzuführen und daraufhin die Varianten zu bewerten.

        Würdest du denn die 3 Variante für vertretbar halten?

        Ja, allerdings mit Änderungen.

        try {
            $result = $pdo->query('SELECT username, uservorname FROM testuser')->fetchAll();

        Statt $result nähme ich hier einen sprechenderen Variablennamen, z.B. $users. Besonders wenn man sein Programm nach EVA gliedert, ist das günstiger als das nichtssagende $result.

        $anzahl = count($result);
        if ($anzahl == 0){

        Die Variable $anzahl anzulegen ist überflüssig, wenn du sie nicht noch im weiteren Verlauf benötigst. Außerdem ließe ich das == 0 weg. count() liefert bereits ein als boolean auswertbares Ergebnis, wenn auch ein negiertes im Vergleich zu == 0.

        if (count($users)) {
            foreach ...
          else
            echo ...

        echo"<option value="$row[0][username]">$row[0][uservorname]</option>\n";

        Kontextwechsel beachten! Wenn du Werte in den HTML-Kontext bringst, musst du sie HTML-gerecht behandeln, z.B: mit htmlspecialchars(). Es gibt Leute, die gegen ihren Kindern komische Namen (das ist zwar ein Datenbank-Kontext, das Prinzip ist aber das gleiche).

        echo "$verabschiedung $name";

        1. Vielen Dank dedlfix,
          diese erklärung war für mich jetzt besonders hilfreich.

          Irgendwie bin ich aber auch etwas betrübt und frustriert.
          Da mache ich doch nun schon einige Zeit mit PHP und MySQL und merke doch immer wieder das mir viele Grundlagen fehlen oder ich vieles einfach nicht berücksichtigt habe.
          Na man lernt eben nie aus.

          vielen Dank nochmals.

          Gruss
          hawk

  2. Hi,

    Mir persönlich gefällt eigentlich Variante 1 mit "fetchColumn" ganz gut.

    Kann man machen, finde ich aber irgendwie trotzdem unschoen.

    Möglichkeit 2 ist vom Code her am aufwändigsten.

    Und erfordert bei dieser Art der Umsetzung auch zwei Queries.

    Oder besser Variante 3 mit "try" und "catch"?

    Nee - try ... catch sollte man nur in Szenarios einsetzen, in denen wirkliche (Programmablauf-)Fehler auftreten koennen, auf die man anders nicht gescheit reagieren kann, die einem u.U. sogar das ganze Script zum Abbruch bringen wuerden.

    Hier liegt aber kein solcher Fehler vor - lediglich zwei unterschiedliche Arten von Abfrageergebnis, entweder "enthaelt Datensaetze" oder "enthaelt keine Datensaetze".

    Oder gibt es noch eine viel einfachere Lösung?

    Ich wuerd's nach dem KISS-Prinzip einfach so umsetzen:

      
    $result = $DBO->query(...);  
    $foundRows = false; // Flag, welches aussagt, ob Datensaetze gefunden wurden; wird mit false initialisiert  
    while($result->fetch(...)) {  
      $foundRows = true; // wenn die Schleife mindestens ein mal durchlaufen wird, dann gab's auch  
                         // mindestens einen Datensatz - also Flag auf true setzen  
      // hier erfolgt weitere Verarbeitung der Datensaetze  
    }  
    if(!$foundRows) {  
      // Reaktion auf den Fall, dass keine Ergebnisdatensaetze vorhanden waren  
    }
    

    MfG ChrisB

    1. Hallo Chris,
      vielen Dank für deine tolle Erklärung.

      Deine Variante leuchtet ein und gefällt mir recht gut.
      Werde ich mir mal für weitere Arbeiten zu Herzen nehmen.

      vielen Dank und viele Grüße
      hawk

  3. Hallo zusammen,
    ein ähnliches Thema, daher kein neues Posting:

    Wie würdet ihr es machen wenn es um Insert oder Update geht?
    Also, mache einen Insert wenn kein Datensatz vorhanden, ansonsten ein Update.

    Es macht ja hier vermutlich keinen Sinn vorher mit Select die ganze Tabelle zu durchlaufen nur um zu schauen ob ein Datensatz vorhanden ist.

    Ich habe im Manual die Option "ON DUPLICATE KEY UPDATE" gefunden.

    also etwa,
    INSERT INTO layout (logo) VALUES ('yes')
    ON DUPLICATE KEY UPDATE logo='yes';

    Wäre das eine Möglichkeit?
    Andere wiederum empfehlen, immer zuerst einen Update zu machen.
    Dann das Ergebnis zu prüfen und im Fehlerfall einen Insert.

    was meint ihr?

    vielen Dank und viele Grüße
    hawk

    1. echo $begrüßung;

      Also, mache einen Insert wenn kein Datensatz vorhanden, ansonsten ein Update.
      Es macht ja hier vermutlich keinen Sinn vorher mit Select die ganze Tabelle zu durchlaufen nur um zu schauen ob ein Datensatz vorhanden ist.

      Wenn man gescheite Indexe setzt, muss das DBMS dies nicht tun, sondern kann gezielt im Index nachschauen.

      Ich habe im Manual die Option "ON DUPLICATE KEY UPDATE" gefunden.
      Wäre das eine Möglichkeit?

      Ja.

      Andere wiederum empfehlen, immer zuerst einen Update zu machen.
      Dann das Ergebnis zu prüfen und im Fehlerfall einen Insert.

      Auch eine Möglichkeit. Wobei es aber keinen Fehler gibt, nur die Anzahl der betroffenen Datensätze wäre 0.

      Noch eine weitere Möglicheit ist REPLACE. Das geht aber etwas anders vor. Es löscht erst, falls ein Datensatz vorhanden ist, und erzeugt dann einen neuen.

      was meint ihr?

      Auch hier wieder: Lerne alle Möglichkeiten kennen, nebst ihren Eigenschaften. Welche Eigenschaft im konkreten Anwendungsfall positiv oder negativ anzusehen ist, musst du dann für diesen Fall bewerten.

      echo "$verabschiedung $name";

      1. Hi dedlfix,
        danke nochmals für deine Erklärungen.

        Auch hier wieder: Lerne alle Möglichkeiten kennen, nebst ihren Eigenschaften. Welche Eigenschaft im konkreten Anwendungsfall positiv oder negativ anzusehen ist, musst du dann für diesen Fall bewerten.

        Das versuche ich ja gerade :-)

        wenn ich es recht verstanden habe ist "REPLACE" und auch die Option "ON DUPLICATE KEY UPDATE" rein MySQL spezifisch.
        Im Hinblick auf PDO und event. anderen DBs in Zukunft würden dann diese Optionen eher wegfallen.

        Ich werde einfach mal weiter ausprobieren was für mich am besten ist bzw. wie es in der jeweiligen Situation ok ist.

        Darf ich dich abschließend noch was anderes fragen. Gehört auch zu PDO.
        Meist findet man in Verbindung mit Insert und Delete oder Update die exec() Funktion.
        $count = $dbh->exec("DELETE FROM fruit WHERE colour = 'red'");

        Ab und zu habe ich aber auch Beispiele mit query() gesehen.

        $pdo->query("INSERT INTO users (loginname,password)
        VALUES ('test','123')");
        $pdo->query("UPDATE users SET password = 'abc' WHERE loginname = 'test'");

        Wenn der Rückgabewert egal ist, was ist dann vorzuziehen oder gibt es sonst noch Unterschiede?

        vielen Dank und viele Grüße
        hawk

        1. echo $begrüßung;

          Darf ich dich abschließend noch was anderes fragen. Gehört auch zu PDO.
          Meist findet man in Verbindung mit Insert und Delete oder Update die exec() Funktion.
          Ab und zu habe ich aber auch Beispiele mit query() gesehen.
          Wenn der Rückgabewert egal ist, was ist dann vorzuziehen oder gibt es sonst noch Unterschiede?

          Ein kurzer Blick ins Handbuch sagt mir (ich verwende PDO nicht, deswegen muss ich nachschlagen), dass query() ein PDOStatement-Objekt zurückliefert, exec() dagegen nur einen Integer-Wert mit der Anzahl der betroffenen Zeilen. exec() ist also der kleine Bruder von query(), nicht ganz so umfangreich, kommt aber ohne den Overhead des PDOStatement-Objekts aus.

          echo "$verabschiedung $name";