PsyCamari: insert_id mit autocommit(false); funktioniert nicht

Hallo,

und schon die nächste Frage: Bin am rumtesten mit commit, rollback und der Voreinstellung autocommit(false); und schon ist mir etwas komisches aufgefallen.

error_reporting(E_ALL);  
ini_set('display_errors', 'On');  
  
$test = new mysqli('localhost','root','','testdb');  
  
$test->autocommit(false);  
$test->query("INSERT INTO table (id,name) VALUES (0,'TEST')");  
echo $test->insert_id;  
$test->rollback();

Erstmal stelle ich autocommit auf false und anschließend rufe ich dieses Script mehrmals auf. Er zählt immer weiter rauf, bis die AI ID irgendwann hohe Werte erreicht. Schön und gut, aber eigentlich will ich das garnicht, da ich ja ein autocommit auf false gestellt habe und jedesmal vorsorglich rollback() ausführe. Wieso zählt der da trotzdem die AI ID rauf? Und wie verhindere ich das?

Danke:)

  1. Tach!

    Erstmal stelle ich autocommit auf false und anschließend rufe ich dieses Script mehrmals auf. Er zählt immer weiter rauf, bis die AI ID irgendwann hohe Werte erreicht. Schön und gut, aber eigentlich will ich das garnicht, da ich ja ein autocommit auf false gestellt habe und jedesmal vorsorglich rollback() ausführe. Wieso zählt der da trotzdem die AI ID rauf? Und wie verhindere ich das?

    Warum willst du das verhindern? Eine ID soll identifizieren und nichts weiter. Wenn du da aus anderen Gründen eine bestimmte Reihenfolge/Lückenlosigkeit/wasimmer benötigst, solltest du dein Konzept überdenken. Dafür ist der AI-ID-Mechanismus nicht ausgelegt.

    Und was wäre, wenn die ID nicht global raufgezählt würde? Der erste Request bekommt die 1. Noch während die Transaktion läuft, kommt ein weiterer Request. Was soll dieser denn für eine ID bekommen? Doch nicht etwa dieselbe wie in der gerade nebenan laufenden Transaktion, auf das es beim Commit zur Katastrophe kommt? Die ID beim Rollback wieder zurückzustellen, ist unnötiger Aufwand und würde auch nur dann sinnvoll sein, wenn zwischenzeitlich keine (auto)committete Aktion stattfand.

    dedlfix.

    1. Warum willst du das verhindern? Eine ID soll identifizieren und nichts weiter. Wenn du da aus anderen Gründen eine bestimmte Reihenfolge/Lückenlosigkeit/wasimmer benötigst, solltest du dein Konzept überdenken. Dafür ist der AI-ID-Mechanismus nicht ausgelegt.

      Und was wäre, wenn die ID nicht global raufgezählt würde? Der erste Request bekommt die 1. Noch während die Transaktion läuft, kommt ein weiterer Request. Was soll dieser denn für eine ID bekommen? Doch nicht etwa dieselbe wie in der gerade nebenan laufenden Transaktion, auf das es beim Commit zur Katastrophe kommt? Die ID beim Rollback wieder zurückzustellen, ist unnötiger Aufwand und würde auch nur dann sinnvoll sein, wenn zwischenzeitlich keine (auto)committete Aktion stattfand.

      Es geht nur um folgendes Problem: Wie bereits in meiner ersten Frage hier, bastel ich an einem kleinen Forum. Wenn man nun ein neues Thema eröffnet, werden 3 SQL-Statements fällig.

      a) Query für die Tabelle boards
      b) Query für die Tabelle threads
      c) Query für die Tabelle posts

      Da ich in der Tabelle posts bei jedem post jeweils einfügen muss zu welcher ThreadID er gehört, fange ich mit der Query für threads an und bekomme mit insert_id die gerade erstelle AI ID. Diese benutze ich bei der Query für die Tabelle posts um Sicherzustellen, dass alle posts auch dem richtigen Thread zugestellt sind.

      Was aber wenn eine der 3 Teilquerys fehlschlägt? Dann kommt es zu Inkonsistenzen, die ich zu vermeiden versuche. Daher das autocommit(false) und die Überprüfung bei jeder der drei Teilquerys, ob sie nun valid durchgelaufen sind oder nicht. Wenn dem so war, führe ein commit() aus, wenn nicht rollback(). So weit ganz einleuchtend, oder? Mir ist dann nur eben aufgefallen, dass auch bei einem rollback() die AI ID immer weiter hochgezählt wird und da frage ich mich warum und was man dagegen tun kann.

      Grob in Code:

      if(QUERY1) !== false) { // Threads table  
      						$ThreadID = $GLOBALS['Database']->getAffectedID();  
      						if($ThreadID !== false) {  
      						     // Did find last affected ID Row  
      							QUERY 2 und 3  
      						else {  
      							// Last affected Row didn't work  
      							$Valid = false;  
      						}  
      					}  
      					else {  
      					     // Threads table operation went wrong  
      						$Valid = false;  
      					}
      

      Wenn ihr irgendwelche Verbesserungsvorschläge habt, nur raus damit. Ich bin zwar kein Anfänger mehr, aber ich würde mich schon gerne verbessern.

      1. Tach!

        Was aber wenn eine der 3 Teilquerys fehlschlägt? Dann kommt es zu Inkonsistenzen, die ich zu vermeiden versuche. Daher das autocommit(false) und die Überprüfung bei jeder der drei Teilquerys, ob sie nun valid durchgelaufen sind oder nicht. Wenn dem so war, führe ein commit() aus, wenn nicht rollback(). So weit ganz einleuchtend, oder?

        Ja, so macht man das, wenn man ein transaktionsorientiertes DBMS zur Verfügung hat (unter MySQL nur mit InnoDB-Tabellen).

        Mir ist dann nur eben aufgefallen, dass auch bei einem rollback() die AI ID immer weiter hochgezählt wird und da frage ich mich warum und was man dagegen tun kann.

        Nichts, das ist, wie ich schon schrieb, ja auch nicht sinnvoll. Konkret wird schon bei jedem INSERT das zur Tabelle gehörige AI-Feld hochgezählt. Jedes INSERT braucht seine eigene eindeutige ID - über alle laufenden, erfolgreichen und abgebrochenen Transaktionen hinweg.

        Wenn ihr irgendwelche Verbesserungsvorschläge habt, nur raus damit. Ich bin zwar kein Anfänger mehr, aber ich würde mich schon gerne verbessern.

        Was definierst du in dem Fall als besser? Der Rollback-Fall wird hoffentlich nicht der Standard werden. Die paar "verlorenen" IDs spielen keine Rolle. Niemand wird die Vollständigkeit der IDs nachprüfen. Wichtig ist nur, dass die Beziehungen konsistent sind/bleiben, was man ja mit Foreign Key Constraints in InnoDB hinbekommen kann.

        dedlfix.

      2. hi,

        Wenn ihr irgendwelche Verbesserungsvorschläge habt, nur raus damit. Ich bin zwar kein Anfänger mehr, aber ich würde mich schon gerne verbessern.

        • Lose Kopplung an die Datenbank,
        • Abstraktion der Datenhaltung persistenter Daten,
        • Verzicht auf das Transaktionskonzept,
        • nur ein Statement zum Einfügen einer neuen Nachricht in das Forum,
        • mehr Performance.

        Möchtest Du über sowas nachdenken?

        Viele Grüße,
          Horst

          • Lose Kopplung an die Datenbank,
          • Abstraktion der Datenhaltung persistenter Daten,
          • Verzicht auf das Transaktionskonzept,
          • nur ein Statement zum Einfügen einer neuen Nachricht in das Forum,
          • mehr Performance.

          Ich habe mich jetzt mal gerade schlau gemacht was unter Loser Kopplung verstanden wird. Gehe ich recht in der Annahme, dass du damit meinst, dass meine drei Tabellen nicht mehr so "stark gekoppelt" sind, dass wenn Inhalt X in Tabelle A geändert wird auch Inhalt X in Tabelle B geändert werden muss?

          Dann frage ich dich, wie das in meinem Beispiel zu bewerkstelligen ist. Wie gesagt habe ich 3 Tabellen, die die gesamten Daten des Forums beinhalten. Boards, Threads, Posts. Boards sieht ungefähr so aus:

          id name threads posts
          1       Testforum  5     8

          Threads:

          id name board author posts views timestamp
          1       Testthread  1     137     2      10       xyz

          Posts:

          id text thread author timestamp
          1        blabla    1      137      xyz

          Die Beispieldaten sagen aus, dass Post ID 1 in Thread ID 1 vorliegt, von Autor # 137 erstellt wurde und dieser Testthread ID 1 im Forum ID 1 dem Testforum vorliegt. Diese DB-Struktur für das Forum fand ich ganz sinnvoll. Ok die threads Spalte im boards Forum und die posts Spalte im threads board hätte ich mir sparen können, da diese jeweils mit einem SELECT * FROM (Tabelle dadrunter) WHERE id = xy zu ermitteln sind.

          Aber wie ist hier bitte lose Kopplung anzuwenden? Diese 3 Tabellen hängen nunmal zusammen, da ich die Daten logisch trennen wollte und sie deshalb in diese 3 Tabellen aufgeteilt habe. Wird ein neuer Thread angelegt, werden 3 Statements fällig:

          a) im Board table für das betreffende Board threads += 1
          b) im Threads table den thread einfügen
          c) im Posts table den post hinzufügen

          Ich sehe nicht, wo ich hier etwas lose koppeln kann. Wird etwas bei mir in Tabelle C geänder respektive gelöscht, muss ich auch A und B anpassen. So wie ich es derzeit habe funktioniert es ganz gut, also was wäre der Vorteil wenn ich es ändern würde (und vorallem: Wie?)?

          Danke. :)

            • Lose Kopplung an die Datenbank,
            • Abstraktion der Datenhaltung persistenter Daten,
            • Verzicht auf das Transaktionskonzept,
            • nur ein Statement zum Einfügen einer neuen Nachricht in das Forum,
            • mehr Performance.

            Ich habe mich jetzt mal gerade schlau gemacht was unter Loser Kopplung verstanden wird. Gehe ich recht in der Annahme, dass du damit meinst, dass meine drei Tabellen nicht mehr so "stark gekoppelt" sind, dass wenn Inhalt X in Tabelle A geändert wird auch Inhalt X in Tabelle B geändert werden muss?

            Lose Kopplung geht in Richtung Abstraktion der Datenhaltung. Ich vertehe das so, dass eine lose Kopplung an eine DB bedeutet, dass mein Programm nur wenig oder gar nicht mehr abhängig ist, von dem was eine DB-Engine zur Programmlogik beiträgt.

            Viele Grüße, danke der Nachfrage,
            Hotti

          1. a) im Board table für das betreffende Board threads += 1

            Und wenn ein Thread gelöscht wird, wieder einen Abziehen? Unnötig, lass doch das DBMS auf Anfrage für dich zählen.

            b) im Threads table den thread einfügen
            c) im Posts table den post hinzufügen

            Die drei Tabellen wären also ca. wie folgt:

            [boards]
            * id int auto_increment primary_key
            * title varchar(150)

            [threads]
            * id int auto_increment primary_key
            * board int
            * title varchar(150)
            * views int

            [posts]
            * id int auto_increment primary_key
            * thread int
            * author int
            ...

            Ich sehe nicht, wo ich hier etwas lose koppeln kann. Wird etwas bei mir in Tabelle C geänder respektive gelöscht, muss ich auch A und B anpassen. So wie ich es derzeit habe funktioniert es ganz gut, also was wäre der Vorteil wenn ich es ändern würde (und vorallem: Wie?)?

            Threads im Board #1 zählen geht dann a la SELECT COUNT(*) FROM threadsWHEREboard = 1;. In ähnlicher Form dann um Posts zu zählen.

            posts sind abhängig von threads, aber nicht mehr umgekehrt, gleiches gilt boards.

            Für einen neuen Thread werden somit gerade mal 2 Statements fällig.

            MfG
            bubble

            --
            If "god" had intended us to drink beer, he would have given us stomachs. - David Daye