hawkmaster: Try & Catch sauberer Stil oder zu aufwändig?

Hallo zusammen,
Folgende Situation:
Die Spalte "dep_name" einer MySQL Tabelle ist mit "UNIQUE" definiert.
Bisher schrieb ich in die DB mit den "mysql" Funktionen.

$result = mysql_query("Select dep_name from departments") or mysql_error();
while ($row = mysql_fetch_array($result)){
$sql  = "INSERT INTO departments (dep_name) ";
$sql .= "VALUES ('$d_name')";
mysql_query($sql, $MYSQL['LINK']);
}

Hier gab es keine Fehlermeldung wenn versucht wurde einen doppelter Eintrag zu schreiben.
Mit DBO und aktiviertem;
$DBO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
kommt jedoch sofort eine Fehlermeldung und das Script bricht ab.

Man kann das ja nun mit "try und catch" abfangen.

$result = $DBO->query("SELECT dep_name FROM departments") or warning($DBO);
try {
while ($row = $result->fetch()){
$DBO->exec("INSERT INTO departments (dep_name) VALUES ('$d_name')");
$DepartmentsID = $DBO->lastInsertId();
}//while
} catch(PDOException $e) {
   print "Error / FEHLER!: " . $e->getMessage() . "<br/>";
}

Oder ist es besser einen etwaigen Fehler gleich auszuschließen?

$result = $DBO->query("SELECT dep_name FROM departments") or warning($DBO);
$alldeps = $result->fetchAll()
foreach($alldeps as $departmentname){
 if(in_array($d_name,$departmentname)){
  echo "Department schon vorhanden! <br>";
 }
}

Mich würde nun interessieren, welches ein sauberer Programmierstil wäre.
wie würdert Ihr das machen?
Mir scheint der Aufwand sehr hoch bei jedem SELECT; INSERT, UPDATE immer alles mit "try" und "catch" abzufangen?

vielen Dank und viele Grüße
hawk

  1. Yerf!

    Mir scheint der Aufwand sehr hoch bei jedem SELECT; INSERT, UPDATE immer alles mit "try" und "catch" abzufangen?

    Ein zusätzlicher SELECT mit einer Schleife über das Ergebnis ist also weniger Aufwändig? Vor allem, wie stellst du sicher, das nach der Prüfung nicht ein anderer Thread die ID ebenfalls einfügt?

    Du kannst ja mehrere Datenbankzugriffe in einem Try-Block zusammenfassen.

    Gruß,

    Harlequin

    --
    <!--[if IE]>This page is best viewed with a webbrowser. Get one today!<![endif]-->
    1. Hi Harlequin,

      danke für deine Hilfe.
      Ja du hast schon recht. Andere Prüfungen oder Fehler- Abfangmethoden sind auch aufwendig.

      Also würdest du es auch so IMMER mit "try" und "catch" machen?
      Zumindest bei SQL Abfragen bzw. Inserts die Probleme machen könnten.

      Gruss
      hawk

      1. Yerf!

        Also würdest du es auch so IMMER mit "try" und "catch" machen?
        Zumindest bei SQL Abfragen bzw. Inserts die Probleme machen könnten.

        Ja.

        Außer die Abfrage wäre sehr einfach zu bewerkstelligen und ein Fehlerfall würde recht häufig eintreten. Dann könnte man mit Performanceüberlegungen kommen, dass ein vorheriges Prüfen evtl. schneller ist als die Catch-Behandlung.

        Gruß,

        Harlequin

        --
        <!--[if IE]>This page is best viewed with a webbrowser. Get one today!<![endif]-->
  2. echo $begrüßung;

    Es ist für alle Beteiligten besser, wenn du deine Probleme an einer Stelle diskutierst und nicht jedes Mal einen neuen Beitrag eröffnest. Die Antwortenden können dann an einer Stelle übersichtlich sehen, was bisher gelaufen ist, und du kannst dann zielgenauere Antworten bekommen.

    Bisher schrieb ich in die DB mit den "mysql" Funktionen.
    mysql_query($sql, $MYSQL['LINK']);
    Hier gab es keine Fehlermeldung wenn versucht wurde einen doppelter Eintrag zu schreiben.

    Das ist so nicht richtig. Den Fehler gab es, nur hast du ihn bisher ignoriert, weil du ihn nicht abgefragt hast. MySQL kennt für das INSERT-Statement das Schlüsselwort IGNORE, dass im Falle einer Unique-Verletzung die Ausführung unterlässt, dabei aber keinen Fehler erzeugt.

    Man kann das ja nun mit "try und catch" abfangen.

    $result = $DBO->query("SELECT dep_name FROM departments") or warning($DBO);
    try {
      while ($row = $result->fetch()){
        $DBO->exec("INSERT INTO departments (dep_name) VALUES ('$d_name')");
        $DepartmentsID = $DBO->lastInsertId();
      }//while
    } catch(PDOException $e) {
      print "Error / FEHLER!: " . $e->getMessage() . "<br/>";
    }

    So wie du hier das try-catch mit dem while geschachtelt hast, wird der komplette Fetch-Vorgang abgebrochen, wenn eines der Inserts misslingt. Vermutlich willst du aber nicht den Fetch-Vorgang versuchen (try) sondern das Einfügen.

    Oder ist es besser einen etwaigen Fehler gleich auszuschließen?

    Besser wäre es vielleicht, die Abfragen gleich so zu formulieren, dass nur die gewünschte Datenmenge entsteht. Mit Subselects (sofern vom DBMS unterstützt) sollte sich eine weitere Verbesserung erreichen lassen. Außerdem kennt MySQL ein INSERT ... SELECT ....

    Mich würde nun interessieren, welches ein sauberer Programmierstil wäre.

    Das musst du mit dir und deinem Gewissen ausmachen. :-) Jeder hat ja von "sauber" so seine eigenen Vorstellungen.

    wie würdert Ihr das machen?

    Vermutlich als ein SQL-Statement. Ohne mir das Problem genauer angesehen zu haben, scheint es mir so, als ob es sich mit Nur-SQL lösen lässt.

    Mir scheint der Aufwand sehr hoch bei jedem SELECT; INSERT, UPDATE immer alles mit "try" und "catch" abzufangen?

    Fehler abzufangen gehört zum Programmieren dazu. Mitunter muss für das Fehlerabfangen mehr Code geschrieben werden als für den Gutfall. Doch als Resultat erhält man ein robustes Programm, das nicht gleich bei jedem Windstoß umfällt.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,

      Danke dir für deine wie immer fachkundige Antwort.

      Es ist für alle Beteiligten besser, wenn du deine Probleme an einer Stelle diskutierst und nicht jedes Mal einen neuen Beitrag eröffnest. Die Antwortenden können dann an einer Stelle übersichtlich sehen, was bisher gelaufen ist, und du kannst dann zielgenauere Antworten bekommen.

      Nur eines muss ich doch mal loswerden.
      Ich verstehe ja schon das man zu einem gleichen Problem nicht laufend neue Threads aufmacht, erst recht nicht am gleichen Tag.

      Was hat nun aber diese Frage mit meinen alten Fragen vom 14.2 Sicherheitsfrage
      oder 15.2 zu tun?
      Und ist es nicht so das man vermutlich keine Antworten mehr bekommt wenn das Ursprungs Posting mehre Tage zurück liegt?

      vielen Dank und viele Grüße
      hawk

      1. echo $begrüßung;

        Was hat nun aber diese Frage mit meinen alten Fragen vom 14.2 Sicherheitsfrage oder 15.2 zu tun?

        Die sind schon im Archiv und zählen nicht mehr. Doch du knabberst immer noch am gleichen Problem von gestern rum: Unterschiede bei PDO::ERRMODE_EXCEPTION an oder aus, UNIQUE. Da hätte diese Fortsetzung gut dazugepasst. Die Frage nach den Begriffsdefinitionen und der Migration sind hingegen in eigenen Threads gut aufgehoben.

        Und ist es nicht so das man vermutlich keine Antworten mehr bekommt wenn das Ursprungs Posting mehre Tage zurück liegt?

        Nein, das täuscht. Es mag vielleicht sein, dass weniger konditionierte Seltenbesucher nach den ersten paar Beiträgen schon schlapp machen, doch das Stammpublikum nutzt die Vorteile der benutzerdefinierten Ansicht und sieht auch bei den Threads von "ganz unten", wenn sich dort was tut.

        echo "$verabschiedung $name";

        1. Hallo dedlfix,

          komme jetzt erst zum antworten.

          Nein, das täuscht. Es mag vielleicht sein, dass weniger konditionierte Seltenbesucher nach den ersten paar Beiträgen schon schlapp machen, doch das Stammpublikum nutzt die Vorteile der benutzerdefinierten Ansicht und sieht auch bei den Threads von "ganz unten", wenn sich dort was tut.>>>

          Ich habe die benutzerdefinierte Ansicht auch seit einiger Zeit aktiv und habe dann auch zum Beispiel dich als User anders farbig markiert.
          Wie handelst du das aber dann mit älteren Threads das sie dir auffallen?
          So eine Email Benachrichtigung wenn jemand auf etwas geantwortet hat, gibt es ja hier nicht oder?

          vielen Dank und viele Grüße
          hawk

          1. Hallo hawk,

            So eine Email Benachrichtigung wenn jemand auf etwas geantwortet hat, gibt es ja hier nicht oder?

            Nein, das Feature gab's mal, hat aber nicht richtig funktioniert und war ne potentielle Spamschleuder. Wir haben's daher erstmal deaktiviert, bis irgendjemand mal die Muße findet, das korrekt zu implementieren.

            Dafür gibt's aber RSS-Feeds für Threads und auch für die Forumshauptseite.

            Viele Grüße,
            Christian

            1. Hi Christian,

              Nein, das Feature gab's mal, hat aber nicht richtig funktioniert und war ne potentielle Spamschleuder. Wir haben's daher erstmal deaktiviert, bis irgendjemand mal die Muße findet, das korrekt zu implementieren.

              Hm, eigentlich gab es doch zwei Features: „Mail bei Antwort” und „Thread abonnieren” - ersteres hätte bei jeder Antwort auf ein eigenes Posting eine Mail schicken sollen, das hat nie wirklich funktioniert. Letzteres sollte bei jedem neuen Posting in einem Thread, welchen man vorher durch einen expliziten Klick auf „Thread abonnieren” wählen musste, eine E-Mail schreiben - und das *hat* funktioniert, zumindest bei mir und das über längere Zeit hinweg ;-)

              Warum wurde also das Feature „Thread abonnieren” deaktiviert? Oder ist dies einfach nur in einem Aufwasch mit der Deaktivierung von „Mail bei Antwort” geschehen?

              Viele Grüße,
                ~ Dennis.

              1. Moin!

                Warum wurde also das Feature „Thread abonnieren” deaktiviert? Oder ist dies einfach nur in einem Aufwasch mit der Deaktivierung von „Mail bei Antwort” geschehen?

                Weil die Mail ohne Autorisierung des Mailadressinhabers einfach an die im Posting angegebe Adresse geschickt wurde.

                Das hat zwei negative Effekte:
                1. Wer sich bei der Adresse vertippt, generiert unzustellbare Mails, die irgendwann bei uns Admins auftauchen.
                2. Wer dort die Mailadresse seines oder unseres ärgsten Feindes eingibt, provoziert unangenehme Abmahnungen gegen uns als Mailversender.

                Da überdies dieses Mailfeature praktisch keinerlei Administrationsoberfläche anbietet, mit der man in solchen Fällen den weiteren Versand abschalten kann, war die Abschaltung die einzige Alternative.

                - Sven Rautenberg

                --
                "Love your nation - respect the others."
          2. Moin!

            Ich habe die benutzerdefinierte Ansicht auch seit einiger Zeit aktiv und habe dann auch zum Beispiel dich als User anders farbig markiert.
            Wie handelst du das aber dann mit älteren Threads das sie dir auffallen?

            Serverseitig Postings als gelesen markieren. Dann sieht man beim Durchscrollen sofort, wenn in einem Thread, den man gelesen hat und in dem man u.U. auch gepostet hat, neue Postings stehen.

            - Sven Rautenberg

            --
            "Love your nation - respect the others."
  3. Hallo nochmals,
    ich hoffe mal das hier vielleicht noch jemand reinschaut auch wenn der Thread schon ein paar Tage alt ist.
    Ich möchte ja verhindern das doppelte Einträge in eine Tabelle geschrieben werden. Die Spalte "dep_name" steht auf "UNIQUE".

    Folgendes habe ich nun mit "try - catch" versucht.
    $result = $DBO->query("SELECT dep_name FROM departments") or warning($DBO);
    while ($row = $result->fetch()){
    if (strtoupper($d_name) != strtoupper($row['dep_name'])){
    try {
    $DBO->exec("INSERT INTO departments (dep_name) VALUES ('$d_name')");
    $DepartmentsID = $DBO->lastInsertId();
    } catch(PDOException $e) {
    print "Error / FEHLER!: " . $e->getMessage() . "<br/>";
    }
    }else{
    $department_check = "same";
    }
    }//while

    Ich habe jetzt also den "try" Zweig nur beim Insert Versuch.
    Wenn ich z.b. 8 Abteilungs Namen in der Tabelle habe kommt 8 mal im Catch Block die Fehlermeldung. Das wäre ja ok so.

    Was ich nun aber nicht verstehe.
    Nachdem die While Schleife fertig ist kommt aber trotzdem dann noch ein Fatal Error:
    Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ...

    Warum denn dies?

    vielen Dank
    Gruss
    hawk

    1. echo $begrüßung;

      Ich möchte ja verhindern das doppelte Einträge in eine Tabelle geschrieben werden. Die Spalte "dep_name" steht auf "UNIQUE".

      Die Option INSERT IGNORE ... ist keine für dich?

      Ich habe jetzt also den "try" Zweig nur beim Insert Versuch.

      Das ist schon mal gut. Aber aus dem gegebenen Stück (mit der nicht vorhandenen Code-Formatierung) kann ich keine Ursache erkennen.

      Nachdem die While Schleife fertig ist kommt aber trotzdem dann noch ein Fatal Error:
      Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ...

      Woraus schließt du, dass die while-Schleife fertig ist? Auf welche Zeile bezieht sich denn die Fehlermeldung? Ist das Problem immer noch da, wenn du es mal nachbaust und dabei alles entfernst oder weglässt, das nicht unbedingt nötig ist?

      echo "$verabschiedung $name";

      1. Hallo deldfix,
        nochmals 1000 Dank für deine Hilfe und das du soagr soweit hinten nachschaust :-)

        Woraus schließt du, dass die while-Schleife fertig ist? Auf welche Zeile bezieht sich denn die Fehlermeldung? Ist das Problem immer noch da, wenn du es mal nachbaust und dabei alles entfernst oder weglässt, das nicht unbedingt nötig ist?>>

        Wenn ich dieses Stück Code ausführe:
        --------------------------------------------
        $result = $DBO->query("SELECT dep_name FROM departments") or warning($DBO);
        while ($row = $result->fetch()){
        if (strtoupper($d_name) != strtoupper($row['dep_name'])){
        try {
        $DBO->exec("INSERT INTO departments (dep_name) VALUES ('$d_name')");
        $DepartmentsID = $DBO->lastInsertId();
        } catch(PDOException $e) {
        print "Error / FEHLER!: " . $e->getMessage() . "<br/>";
        }
        }else{
        $department_check = "same";
        }
        }//while

        die();
        --------------------------------------------
        Und ich habe 8 Zeilen in der Tabelle, dann ist die Ausgabe wie folgt:
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2
        Error / FEHLER!: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2

        Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Systemsupport' for key 2' in C:\Programme\dbo\include\newuser.php:100 Stack trace: #0 C:\Programme\dbo\include\newuser.php(100): PDOStatement->fetch() #1 {main} thrown in C:\Programme\dbo\include\newuser.php on line 100

        Die Zeile 100 bezieht sich dann auf;
        while ($row = $result->fetch()){

        Dieser letzte Fatal Error ist mir eigentlich nicht klar.

        vielen Dank und viele Grüße
        hawk

        1. echo $begrüßung;

          Mittlerweile hab ich das Problem erfolgreich nachgestellt. Es sieht so aus, als ob PDO die Verschachtlung nicht verkraftet, und nicht merkt, dass der Fehler zum inneren Statement gehört.

          Wenn du zuerst mit fetchAll() die komplette Ergebnismenge abholst, dann darüber iterierst (foreach) und dabei das INSERT ausführst, kommt die extra Exception nicht mehr.

          Eine Frage bleibt aber:


          $result = $DBO->query("SELECT dep_name FROM departments") or warning($DBO);
          while ($row = $result->fetch()) {
            if (strtoupper($d_name) != strtoupper($row['dep_name'])) {
              try {
                $DBO->exec("INSERT INTO departments (dep_name) VALUES ('$d_name')");
                $DepartmentsID = $DBO->lastInsertId();
              } catch(PDOException $e) {
                print "Error / FEHLER!: " . $e->getMessage() . "<br/>";
              }
            } else {
              $department_check = "same";
            }
          }//while

          Welche Aufgabe hat dieses Codestück eigentlich? Ich sehe je eine Zuweisung an $DepartmentsID und an $department_check innerhalb der Schleife, doch ihre Werte werden gar nicht verwendet. Stattdessen kann es vorkommen, dass sie ein paar Mal einfach nur neu gesetzt werden.

          Mir scheint, das Konstrukt soll nur dann etwas einfügen, wenn es bisher noch nicht vorkam. Wenn dem so ist, tät ich das wohl ungefähr so machen:

          if ((INSERT IGNORE INTO departments (dep_name) VALUES($d_name))->rowCount())
              // eingefügt
            else
              // schon vorhanden

          oder (ohne IGNORE)

          try
              INSERT INTO departments (dep_name) VALUES($d_name)
              // eingefügt
            catch
              // schon vorhanden

          oder

          if ((SELECT count(*) ... WHERE dep_name = $d_name)->fetchColumn(0))
              // schon vorhanden
            else
              INSERT INTO departments (dep_name) VALUES($d_name)
              // eingefügt

          echo "$verabschiedung $name";

          1. Hallo dedlfix,

            ja du hast natürlich recht.
            Ich habe mittlerweile auch gemerkt das mein Konstrukt etwas umständlich bzw. nicht gut ist.
            Es geht darum.
            Ein Anwender kann sich neu an einem System anmelden. Dazu gibt es seinen Namen, Login, Passwort, Email und halt auch seine Abteilung an.

            Wenn es diese Abteilung noch nicht in der Tabelle "departments" gibt soll sie hinzugefügt werden. Damit doppelte Abteilungen wirklich ausgeschlossen werden habe ich die Spalte al UNIQUE gesetzt.
            Bisher mit mysql() sind mir die Fehlermeldungen bei Schreibversuche nicht aufgefallen, weil wie du ja so gut erklärt hast, das mysql ignoriert hat.

            Deine Lösungen sind natürlich viel eleganter und sauberer.
            Ich muss gestehen das ich den "Insert Ignore" noch nie benutzt hatte.
            Auch wäre ich vermutlich nicht auf deine letzte Lösung gekommen.
            <<
            if ((SELECT count(*) ... WHERE dep_name = $d_name)->fetchColumn(0))
                // schon vorhanden
              else
                INSERT INTO departments (dep_name) VALUES($d_name)
                // eingefügt

            fetchColumn(0) holt doch die erste Spalte der nächsten Zeile.
            Warum machst du hinten noch die Zuweisung ->fetchColumn(0)?
            Würde es nicht auch so gehen?
            if($DBO->query("SELECT count(*) FROM departments WHERE dep_name = '$d_name' ") ){

            viele Grüße und nochmals vielen Dank
            hawk

            1. echo $begrüßung;

              fetchColumn(0) holt doch die erste Spalte der nächsten Zeile.
              Warum machst du hinten noch die Zuweisung ->fetchColumn(0)?
              Würde es nicht auch so gehen?
              if($DBO->query("SELECT count(*) FROM departments WHERE dep_name = '$d_name' ") ){

              Nein. Ein SELECT count(*) (ohne GROUP BY) liefert immer eine einzeilige Ergebnismenge. Das Ergebnis von ->query() sagt ja nur aus, dass die Abfrage erfolgreich war. Selbst wenn ein SELECT eine leere Ergebnismenge liefert war das eine erfolgreiche Abfrage. Erst beim Fetch stellt sich heraus, dass keine Daten vorliegen. Das fetchColumn(0) liefert die von count(*) ermittelte Summe. Das ist entweder 0 oder ein größerer Wert.

              echo "$verabschiedung $name";

              1. Hallo dedlfix,

                Das fetchColumn(0) liefert die von count(*) ermittelte Summe. Das ist entweder 0 oder ein größerer Wert.

                ja das habe ich jetzt auch durch testen verstanden und hinbekommen. Ohne dem
                ->fetchColumn(0) geht es nicht bzw. habe ich immer ein Ergebnis.

                Ich möchte mich abschliesend nochmals ganz herzlich für deine Mühe, Geduld und den guten Erklärungen bedanken. Ich habe durch dich schon viel gelernt.
                Und ich sehe immer wieder das ich noch viel lesen und lernen muss.

                By the way:
                Mich würde einfach mal interessieren wie du dir deine so fundierten Kenntnisse in MySQL und PHP angeeignet hat. Hast du studiert?
                Vielleicht hast du ja mal Lust mir zu antworten.

                vielen Dank und viele Grüße
                hawk

                1. echo $begrüßung;

                  Mich würde einfach mal interessieren wie du dir deine so fundierten Kenntnisse in MySQL und PHP angeeignet hat.

                  Im Grunde genommen kenne ich nur die Handbücher und weiß, in welchem Kapitel die Antwort zu finden sein muss. Dann habe ich auch gelernt, die Beschreibung nicht nur wörtlich zu nehmen sondern auch komplett durchzulesen, denn wenn man denkt, die Lösung stand grad eben da, steht weiter hinten eine Ausnahme von der Regel, und die hat man garantiert vorliegen. Den Rest ist einfach nur Zeit, die bei der Beschäftigung mit eigenen und fremden Problemen vergeht.

                  echo "$verabschiedung $name";

                  1. Hi dedlfix,

                    ich wünschte ich könnte mir das auch so gut merken und so gut umsetzen wie du. Ich habe mittlerweile 2 Bücher über PHP und MySQL und dennnoch merke ich dass bei vielen Dingen die man meint zu wissen, doch einiges unklar ist.

                    Erst gestern hatte ich wieder so eine Situation mit folgender SQL:

                    Select AccountID, DepartmentsID from accdep GROUP BY DepartmentsID having count(AccountID)>1

                    Das hatte ich mal vor ca. 2 Jahren eingebaut. Wenn ich es heute in PhpMyAdmin ausführe und auf die Tabelle anwende verstehe ich nicht ganz wie das Ergebnis zustande kommt.

                    Na ja, vielleicht geht mir ja noch ein Licht auf.

                    vielen Dank nochmals für deine gute Hilfe und Erklärungen.

                    viele Grüße
                    hawk