Erik_: zusammenfassen merhere (fast) gleichen Anweisungen beim Löschen (DELETE)

Hallo, ich beschäftige mich jetzt sein einiger Zeit mit mysql. Doch nun habe ich einige Fragen, um das ganze sauberer, schöner hinzubekommen.

$statement = $pdo->prepare("DELETE FROM table_1  WHERE id =:id");	
$statement->execute(array('id' => $id));
$statement = $pdo->prepare("DELETE FROM table_2  WHERE id =:id");	
$statement->execute(array('id' => $id));
$statement = $pdo->prepare("DELETE FROM table_3  WHERE id =:id");	
$statement->execute(array('id' => $id));
$statement = $pdo->prepare("DELETE FROM table_4  WHERE id =:id");	
$statement->execute(array('id' => $id));
$statement = $pdo->prepare("DELETE FROM table_5  WHERE id =:id");
$statement->execute(array('id' => $id));	

wäre es nicht irgendwie möchliche die $statement->execute nur einmal auszuführen.

und:

die ganzen Delete in einer Anweisung zu bündeln.

Erik

  1. Hallo Erik,

    $statement = $pdo->prepare("DELETE FROM table_1  WHERE id =:id");	
    $statement->execute(array('id' => $id));
    $statement = $pdo->prepare("DELETE FROM table_2  WHERE id =:id");	
    $statement->execute(array('id' => $id));
    $statement = $pdo->prepare("DELETE FROM table_3  WHERE id =:id");	
    $statement->execute(array('id' => $id));
    $statement = $pdo->prepare("DELETE FROM table_4  WHERE id =:id");	
    $statement->execute(array('id' => $id));
    $statement = $pdo->prepare("DELETE FROM table_5  WHERE id =:id");
    $statement->execute(array('id' => $id));	
    

    wäre es nicht irgendwie möchliche die $statement->execute nur einmal auszuführen.

    das wäre in der Tat schön, und die Idee drängt sich auf. Aber ein DELETE auf mehrere Tabellen in einem einzigen Statement gibt SQL meines Wissens nicht her.

    Du könntest das höchstens in PHP als Schleife formulieren und den Tabellen-Namen im String jeweils dynamisch einbauen. Das sieht dann vielleicht etwas "ordentlicher" aus; im Hinblick auf Performance gewinnst du damit aber nichts. Es bleiben mehrere (im Beispiel fünf) SQL-Statements.

    Live long and pros healthy,
     Martin

    --
    Paradox: Wieso heißen die Dinger Kühlkörper, obwohl sie höllisch heiß werden?
  2. Hallo Erik_,

    es gibt die Möglichkeit, in einem SQL Aufruf mehrere Anweisungen zu schicken. Die werden dann durch ein Semikolon getrennt. Das funktioniert dem Vernehmen nach allerdings nur, wenn Du über PDO auf MYSQL zugreifst. Bei anderen Datenbanken kann es sein, dass PDO das Mehrfachstatement nur simuliert und unter der Haube doch mehrere Aufrufe durchführt.

    Wenn PHP und SQL Server auf dem gleichen Computer laufen (sprich: du connectest zu localhost oder 127.0.0.1), dann ist es aber auch ziemlich egal, ob Du einen oder fünf Aufrufe machst. Das fällt nur ins Gewicht, wenn der Server auf einem separaten Computer läuft.

    Wenn's Dir um lesbaren Code geht, schreibe eine Funktion, die einen Delete durchführt:

    function delete_id($db, $table, $id) {
       $statement = $db->prepare("DELETE FROM $table WHERE id = :id");
       if ($statement === FALSE) return false;
       return $statement->execute([ 'id' => $id] );
    }
    
    ...
    
    delete_id($pdo, "table_1", $id);
    delete_id($pdo, "table_2", $id);
    delete_id($pdo, "table_3", $id);
    delete_id($pdo, "table_4", $id);
    delete_id($pdo, "table_5", $id);
    

    Heißten deine Tabellen tatsächlich table_1 bis table_5? Dann könntest Du das noch in eine Schleife stecken:

    for ($i=1; $i<=5; $i++) {
       delete_id($pdo, "table_$i", $id);
    }
    

    Ob das unterm Strich die beste Lösung ist, sei dahingestellt. Wird die ID immer aus den Tabellen 1-5 gelöscht? In dem Fall könntest Du im SQL Server eine Stored Procedure (Routine) anlegen, die die Löschung in allen 5 Tabellen durchführt, und nur die id übergeben.

    Die Frage ist aber auch, wie kritisch diese Löschoperation ist. Steckt hier eine Performance-Bremse für deine Anwendung? Hast Du das gemessen? Wenn der Code nicht kritisch ist, belasse es bei "ist schnell genug und funktioniert". Man optimiert dort, wo es weh tut. Andernfalls bleibt man bei möglichst lesbarem Code.

    Eine Optimierung ist hier übrigens auch, wenn man auf den prepare() verzichtet. Das braucht nämlich zwei SQL Server Roundtrips, ein prepare lohnt nur, wenn man das Statement mehr als einmal, aber mit unterschiedlichen Parameterwerten ausführt. Wenn Du den Wert in $id ordentlich in den SQL Kontext überträgst, brauchst Du kein prepare. Das tust Du je nach Datentyp von $id mit der intval()-Funktion oder mit der $pdo->quote() Methode.

    Zum Beispiel:

    $sql = "DELETE FROM table_1 WHERE id = " . intval($id);
    $rc = $pdo->exec($sql);
    if ($rc === FALSE)
       // DELETE lief auf einen Error
    else
       // $rc enthält die Anzahl gelöschter Sätze
    

    Was übrigens grundsätzlich nicht geht, ist das Einsetzen des Tabellennamens als PDO Parameter.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. function delete_id($db, $table, $id) {
        $statement = $db->prepare("DELETE FROM $table WHERE id = :id");
        if ($statement === FALSE) return false;
        return $statement->execute([ 'id' => $id] );
      }
      

      Heißten deine Tabellen tatsächlich table_1 bis table_5? Dann könntest Du das noch in eine Schleife stecken:

      for ($i=1; $i<=5; $i++) {
         delete_id($pdo, "table_$i", $id);
      }
      

      Nicht nur dann. Der Name kann beliebig sein:

      $arTables' = [ 'foo', 'bar', 'baz' ];
      # …
      foreach ( $arTables as $item ) {
          delete_id( $pdo, $item, $id );
      }
      

      Aber, es könnten Probleme auftreten. Wie ich das sehe braucht es hier, besonders im Hinblick aufhier möglicherweise korrekt verwendete Constraints, wohl etwas mehr Aufmerksamkeit bei Fehlern.

      Denkbar wäre z.B. mit $pdo->beginTransaction() zu beginnen (Handbuch) , nach dem letzten erfolgreichen DELETE mit $pdo->commit() zu reagieren und im Fehlerfall $dbo->rollBack(); anzuwenden. Nicht zu vergessen die Reaktionen im User-Interface und Logeinträge fürs Debugging…

      <?php
      $arTables = [ 'foo', 'bar', 'baz' ];
      # …
      $pdo -> beginTransaction();
      $has_error = false;
      foreach ( $arTables as $item ) {
          if ( false === delete_id( $pdo, $item, $id ) ) {
              $dbo -> rollBack();
              $has_error = true;
              trigger_error( "Fehler beim Löschen von ID $id aus Tabelle $item", E_USER_WARNING );
              break;
          }
      }
      if ( false === $has_error )  {
          $pdo -> commit();
      }
      

      Hinweis: Der hier gezeigte Code ist ein Beispiel und im Hinblick auf den Aufwand mit der hierfür zu schaffende Testumgebung außer mit dem Linter (reine Syntaxprüfung) nicht getestet.


      Wenn jemand grundlos auf [-1] klicken will ohne einen Fehler benennen zu wollen (weil er es nicht kann), dann soll ES das halt in der hier schon oftmals gezeigten Absicht tun, die Stimmung im Forum zu „verbessern“…

      1. Hallo,

        Wenn jemand grundlos auf [-1] klicken will ohne einen Fehler benennen zu wollen ...

        ... dann kratzt das natürlich an deinem übersteigerten Ego, schon klar.

        Live long and pros healthy,
         Martin

        --
        Paradox: Wieso heißen die Dinger Kühlkörper, obwohl sie höllisch heiß werden?
        1. ... dann kratzt das natürlich an deinem übersteigerten Ego, schon klar.

          Nein, das ist falsch. Richtig ist, dass ich natürlich „angepisst“ bin und es als Anzeichen eines fachlich unbegründeten, rein persönlichen Angriffs ansehe, wenn ich mir für einen Beitrag hier Mühe gebe und [irgendeiner wiederholt schweigend - also vermutlich grundlos und aus einem sachfremden Motiv heraus - auf [-1] klickt.

          Ich könnte mir ja immerhin vorstellen, dass es an einer früheren Begegnung auf dem Schulhof oder in bzw. vor einer Diskothek liegt… in letztere hab ich vielleicht nicht reingelassen…

      2. Hallo Raketenwilli,

        du hast mit deinen Zusatzvorschlägen natürlich grundsätzlich recht, ich wollte aber erstmal abwarten, was der OP antwortet um darauf dann aufzubauen. Meine Erfahrung ist eigentlich, dass eine zu viel an Text nicht gelesen wird.

        Ich gehe nämlich davon aus, dass wir hier beim B eines AB-Problems sind. Mein Verdacht ist, dass table_1 bis table_5 am liebsten eine einzige Table sein würden.

        Dass es auch ein Stern-Layout sein könnte, und 4 der 5 Tabellen Satelliten sind, so dass man über einen ON DELETE CASCADE mit einem einzigen DELETE auskommen könnte, ja, das ist eine gute Idee.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. ich wollte aber erstmal abwarten, was der OP antwortet um darauf dann aufzubauen.

          Damit hast Du natürlich recht. Ich habe mich ja auch schon oft geärgert, weil keine Reaktion mehr nachkam, nachdem dem TO geholfen wurde, ihm aber Hinweise auf weitere Probleme gegeben oder Fragen gestellt wurden…

          Mein Verdacht ist, dass table_1 bis table_5 am liebsten eine einzige Table sein würden.

          Das kann sogar sein, denn

          "DELETE FROM table_1  WHERE id =:id",
          "DELETE FROM table_2  WHERE id =:id",
          ...
          "DELETE FROM table_n  WHERE id =:id"
          

          ist ein Hinweis darauf. Möglicherweise ist aber die tatsächlich unterschiedliche Bezeichnung der Spalten (hier immer: "id") der Abstraktion für die Problemdarstellung zum Opfer gefallen … sagt jedenfalls meine Glaskugel.