PHPKram: mysql_insert_id()

Hallo,

wenn ich die letzte ID, die für ein Autoincrement der Tabelle X erstellt wurde, auslesen will, kann ich das per mysql_insert_id(); tun. Vorraussetzung hierfür ist allerdings, dass vorher eine Query wie z.B. meist INSERT ausgeführt wurde. Was aber, wenn der letzte insert vor einiger Zeit ausgeführt wurde, die Verbindung seit dem geschlossen wurde, und jetzt an einem ganz anderem Datum erst wieder eine Verbindung zur DB hergestellt wird und ich dann über mysql_insert_id(); die Autoincrement ID abfrage? Ist dies dann auch in jedem Fall die zuletzt erstelle? Gibt es keine Sonderfälle in denen das nicht der Fall ist? Funktioniert das überhaupt? Wenn nein, wie bekomme ich ohne vorher ein Insert auszuführen die zuletzt verwendete Autoincrement ID unabhängig davon wann diese erstellt wurde?

Vielen Dank!

  1. Hello,

    wenn ich die letzte ID, die für ein Autoincrement der Tabelle X erstellt wurde, auslesen will, kann ich das per mysql_insert_id(); tun. Vorraussetzung hierfür ist allerdings, dass vorher eine Query wie z.B. meist INSERT ausgeführt wurde. Was aber, wenn der letzte insert vor einiger Zeit ausgeführt wurde, die Verbindung seit dem geschlossen wurde, und jetzt an einem ganz anderem Datum erst wieder eine Verbindung zur DB hergestellt wird und ich dann über mysql_insert_id(); die Autoincrement ID abfrage? Ist dies dann auch in jedem Fall die zuletzt erstelle? Gibt es keine Sonderfälle in denen das nicht der Fall ist? Funktioniert das überhaupt? Wenn nein, wie bekomme ich ohne vorher ein Insert auszuführen die zuletzt verwendete Autoincrement ID unabhängig davon wann diese erstellt wurde?

    Interessante Frage.

    Ich _vermute_ jetzt mal: Neue Connetction, neues Glück.
    Die PHP-Funktion müsste bei erster Vermutung false liefern.

    Die Abfrage in der Datenbank mittels "select last_insert_id();" liefert 0 und keine Warnung. Dann könnte die PHP-Version das auch liefern.

    Da 0 ein unzulässiger Wert ist für Autoincrement-Keys, kann er als "neutrales Element" herangezogen werden.

    Wenn Du die nächste Autoincrement-ID herausfinden willst, die vergeben würde, wenn Du ein Insert auf die Tabelle ausführst, dann könntest Du das infomation_schema abfragen. Diese ist aber nur solange gültig, bis über _irgendeine_ Verbindung ein Insert durchgeführt wurde, lässt sich also ohne weitere Maßnahmen nicht für die Referenzbildung verwenden.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hi,

      also ich habs jetzt über mysql_insert_id() also das PHP Equivalent zur mySQL Funktion probiert und ich habs über die SQL interne Funktion SELECT LAST_INSERT_ID probiert. Wie bereits von dir gesagt, liefert der 0 und es funktioniert in beiden Fällen nicht - auch ohne Warnmeldung. Was ich möchte ist einfach nur die letzte Autoincrement ID die vergeben möchte herausbekommen, OHNE dass ich nochmal vorher ein insert oder so ausführen muss.

      An das information_shema hab ich gar nicht gedacht, bzw. kannte ich gar nicht. Ich nehme vom Namen her an, dass im information_shema alle wichtigen Daten über eine Table gespeichert sind und damit auch der derzeite Autoincrement Wert? Und wenn ja, reicht es doch einfach diese Werte auszulesen und dann anzuzeigen - sie müsste sich ja verändern, wenn man eine neue Reihe von Werten nachher einfügt. Aber wie komme ich an dieses information_shema ran?

      Gibt's vllt. noch andere Wege?

      1. Moin,

        also ich habs jetzt über mysql_insert_id() also das PHP Equivalent zur mySQL Funktion probiert und ich habs über die SQL interne Funktion SELECT LAST_INSERT_ID probiert. Wie bereits von dir gesagt, liefert der 0 und es funktioniert in beiden Fällen nicht - auch ohne Warnmeldung. Was ich möchte ist einfach nur die letzte Autoincrement ID die vergeben möchte herausbekommen, OHNE dass ich nochmal vorher ein insert oder so ausführen muss.

        Das Problem ist, dass mysql_insert_id() und auch die Mysql-Funktion LAST_INSERT_ID() nicht dazu gedacht sind, die wirklich letzte ID zu erfahren, sondern sie geben "den ersten automatisch erzeugten Wert zurück, der für eine AUTO_INCREMENT-Spalte durch die aktuelle INSERT- oder UPDATE-Anweisung eingestellt wurde, die eine solche Spalte modifiziert hat."

        Hintergrund ist, dass "dieses Verhalten gewährleistet, dass jeder Client seine eigene Kennung abrufen kann, ohne die Aktivitäten anderer Clients berücksichtigen oder Sperren setzen bzw. Transaktionen verwenden zu müssen."

        Quelle

        Grüße Marco

      2. Hello,

        also ich habs jetzt über mysql_insert_id() also das PHP Equivalent zur mySQL Funktion probiert und ich habs über die SQL interne Funktion SELECT LAST_INSERT_ID probiert. Wie bereits von dir gesagt, liefert der 0 und es funktioniert in beiden Fällen nicht - auch ohne Warnmeldung. Was ich möchte ist einfach nur die letzte Autoincrement ID die vergeben möchte herausbekommen, OHNE dass ich nochmal vorher ein insert oder so ausführen muss.

        An das information_shema hab ich gar nicht gedacht, bzw. kannte ich gar nicht. Ich nehme vom Namen her an, dass im information_shema alle wichtigen Daten über eine Table gespeichert sind und damit auch der derzeite Autoincrement Wert? Und wenn ja, reicht es doch einfach diese Werte auszulesen und dann anzuzeigen - sie müsste sich ja verändern, wenn man eine neue Reihe von Werten nachher einfügt. Aber wie komme ich an dieses information_shema ran?

        Dann musst Du das im Datendesign so vorsehen, dass Du es abrafen kannst, z.B. über eine Timestamp-Spalte, die automatisch bei Insert geführt wird (Granularität beachten!), oder über ein Log.

        Die nächste automatisch zu vergebende kannst Du ja, wie schon erwähnt, aus dem Imformation_Schema ermitteln.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
      3. Tach!

        Was ich möchte ist einfach nur die letzte Autoincrement ID die vergeben möchte herausbekommen, OHNE dass ich nochmal vorher ein insert oder so ausführen muss.

        Welches eigentliche Problem möchtest du lösen? Der Datensatz mit der letzten vergebenen ID kann bereits wieder gelöscht sein. Wozu also denkst du, diese ID im Nachhinein noch zu beötigen?

        Aber wie komme ich an dieses information_shema ran?

        Mit Select-Statements. Das information_schema ist wie eine normale View abfragbar.

        Gibt's vllt. noch andere Wege?

        Erst das Ziel benennen, dann kann man über konkrete Wege nachdenken.

        dedlfix.

      4. Hello,

        An das information_schema hab ich gar nicht gedacht, bzw. kannte ich gar nicht. Ich nehme vom Namen her an, dass im information_schema alle wichtigen Daten über eine Table gespeichert sind und damit auch der derzeite Autoincrement Wert?

        select auto\_increment from information\_schema.TABLES where table\_name ='$tablename';

        $tablename bitte ersetzen.

        Aber er hat eben nur Gültigkeit im Moment der Abfrage!

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hallo Tom,

          select auto\_increment from information\_schema.TABLES where table\_name ='$tablename';

          Das funktioniert auch nur wenn es auf dem Server genau eine Tabelle mit dem Namen gibt - der Name der Datenbank muss schon auch noch mit in die WHERE-Bedingung.

          Gruß,
          Tobias

          1. Hello Tobias,

            select auto\_increment from information\_schema.TABLES where table\_name ='$tablename';

            Das funktioniert auch nur wenn es auf dem Server genau eine Tabelle mit dem Namen gibt - der Name der Datenbank muss schon auch noch mit in die WHERE-Bedingung.

            Stimmt irgendwie. Ich war allerdings mit der DB verbunden beim Ausprobieren. Wahrscheinlich hat MySQL deshalb nicht gemeckert.

            select auto\_increment
                from information\_schema.TABLES
                where table\_name = '$tablename'
                   and table\_schema = '$databasename';

            So müsste es jetzt passen,

            oder willst Du lieber auch noch den Servernamen und den Host drin haben? :-P

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
  2. Moin,

    Was aber, wenn der letzte insert vor einiger Zeit ausgeführt wurde, die Verbindung seit dem geschlossen wurde, und jetzt an einem ganz anderem Datum erst wieder eine Verbindung zur DB hergestellt wird und ich dann über mysql_insert_id(); die Autoincrement ID abfrage? Ist dies dann auch in jedem Fall die zuletzt erstelle?

    Der Datensatz, der den größten Autoincrement-Wert hat ist der neueste, sofern keine neueren erstellt aber schon wieder gelöscht wurden. Wenn du diesen Wert meinst, dann geht das mit einem "SELECT MAX(feld) FROM bla".

    Wenn du den Wert wissen willst, den Mysql dem nächsten Datensatz geben wird, der eingefügt wird, dann geht das per "SHOW TABLE STATUS LIKE bla".

    Hier gibts den zugehörigen PHP Code.

    Grüße Marco

    1. Hello marco,

      Was aber, wenn der letzte insert vor einiger Zeit ausgeführt wurde, die Verbindung seit dem geschlossen wurde, und jetzt an einem ganz anderem Datum erst wieder eine Verbindung zur DB hergestellt wird und ich dann über mysql_insert_id(); die Autoincrement ID abfrage? Ist dies dann auch in jedem Fall die zuletzt erstelle?

      Der Datensatz, der den größten Autoincrement-Wert hat ist der neueste,

      Das stimmt nicht. NySQL lässt jederzeit zu, dass man Lücken im Schlüsselkreis durch gezielte Angabe wieder schließt. Ob das sinnvoll ist, steht auf einem anderen Blatt.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Moin,

        Das stimmt nicht. NySQL lässt jederzeit zu, dass man Lücken im Schlüsselkreis durch gezielte Angabe wieder schließt. Ob das sinnvoll ist, steht auf einem anderen Blatt.

        Es wird zugelassen; dieser Datensatz ist dann aber manuell erstellt und sollte nicht erfordern, dass man die ID dieses Datensatzes noch benötigt, weil man sie ja schon irgendwo hat.

        Grüße Marco

        1. Hello,

          Das stimmt nicht. NySQL lässt jederzeit zu, dass man Lücken im Schlüsselkreis durch gezielte Angabe wieder schließt. Ob das sinnvoll ist, steht auf einem anderen Blatt.

          Es wird zugelassen; dieser Datensatz ist dann aber manuell erstellt und sollte nicht erfordern, dass man die ID dieses Datensatzes noch benötigt, weil man sie ja schon irgendwo hat.

          Na, und wenn MySQL dann eines Tages auch Random-Keys unterstützt, wie sie für verschiedene Datenschutzbestimmungen gefordert sind, dann geht es auch nicht mehr. Das ist, da sie ja jetzt offiziell zu Oracle gehören, sogar sehr wahrscheinlich!

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. Moin,

            Na, und wenn MySQL dann eines Tages auch Random-Keys unterstützt, wie sie für verschiedene Datenschutzbestimmungen gefordert sind, dann geht es auch nicht mehr. Das ist, da sie ja jetzt offiziell zu Oracle gehören, sogar sehr wahrscheinlich!

            Dann muss man sich - noch mehr als jetzt schon - selbst darum kümmern, wenn man eventuell mal den neuesten Datensatz aus der Tabelle anzeigen lassen will, beispielsweise durch einen Timestamp.

            Den nächsten Autoincrement-Wert wird man gar nicht ermitteln können, da der ja sicherlich erst zum Zeitpunkt des Einfügens generiert wird.

            Den zuletzt eingetragenen Autoincrement-Wert bekommt man, wenn die Verbindung einmal geschlossen ist, auch nur über eine eigene Vorrichtung, wie den Timestamp.

            Grüße Marco

            1. Hello,

              Den nächsten Autoincrement-Wert wird man gar nicht ermitteln können, da der ja sicherlich erst zum Zeitpunkt des Einfügens generiert wird.

              Den kann man, wie ich nun schon zweimal erwähnt habe, per Abfrage aus dem Information_Schema erfahren.

              Er hat aber keine wirkliche Relevanz, weil man für eine Kompletterstellung-vor-Eintragung von Relationen auch ein pessimistic Locking einsetzen müsste, also alle betroffenen Tabellen solange sperren müsste, bis alle verbundenen Datensätze eingetragen sind.

              Wenn man aber stufenweise vorgeht, die ID also erst erhält, _nachdem_ ein DS eingetragen ist, kann man die dann in der nächsten Relationsstufe verwenden usw.

              Liebe Grüße aus dem schönen Oberharz

              Tom vom Berg

              --
               ☻_
              /▌
              / \ Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
    2. Servus,

      danke ihr beiden! Ich glaube ich war gerade etwas zu blöd, um auf den weg mit MAX zu kommen. Genau das wollte ich. Ich nehme das einfach mit SELECT MAX(itemID) und füge stumpf danach +1 hinzu. Schon haben wir die jetzt zu verwendende ID für den nächsten Datensatz.

      Hier schließt sich direkt eine weitere Frage von mir an zum Thema Datenbankstruktur (bin da noch nicht unbedingt so erfahren).

      Ich habe wie bereits hier in dem Thema gemerkt das Feld itemID, das ich über die gesamte Datenbank verwende, um Datensätze eindeutig zu identifizieren. Ich habe 12 Tables, von denen eine über das Datum ist, eine über allgemeine Daten, eine über private Daten, eine über Attachments, eine über Metadata etc. Das heißt überall in den jeweiligen Tabellen gibt es die Spalte itemID, die die Daten der jeweiligen Tabellen Datenbankübergreifend miteinander in Relation setzt. Ich hoffe ihr habt den Aufbau ungefähr verstanden.

      Was ist jetzt sinnvoll? Soll ich ganz am Anfang, wenn ein neuer Datensatz eingefügt werden soll einfach schonmal in jeder der Tabellen einen leeren Datensatz einfügen, damit überall schonmal itemID X eingetragen ist und ich dann nur noch mit Update drüber fahren muss, um die einzelnen Werte einzutragen oder wie soll ich das machen?

      Ich habs bisher ungefähr so:

      // Insert DateCreatedYear  
      						if($Split[0] == "DateCreated" && $Split[1] == "Year") {  
            						$this->Connect->query("INSERT INTO itemdates (itemID,DateCreatedYear) VALUES ('$itemID','" . $Split[2] . "')");  
      						}  
      						// Insert DateCreatedMonth  
      						elseif($Split[0] == "DateCreated" && $Split[1] == "Month") {  
            						$this->Connect->query("INSERT INTO itemdates (itemID,DateCreatedMonth) VALUES ('$itemID','" . $Split[2] . "')") or die($this->Connect->error);  
      						}  
                                    // Insert DateCreatedDay  
      						elseif($Split[0] == "DateCreated" && $Split[1] == "Day") {  
            						$this->Connect->query("INSERT INTO itemdates (itemID,DateCreatedDay) VALUES ('$itemID','" . $Split[2] . "')");  
      						}  
      						// Insert DateCompletedYear  
      						elseif($Split[0] == "DateCompleted" && $Split[1] == "Day") {  
            						$this->Connect->query("INSERT INTO itemdates (itemID,DateCompletedYear) VALUES ('$itemID','" . $Split[2] . "')");  
      						}
      

      Klar funktioniert der Code so wie er da steht nicht ganz - das merke ich auch gerade. Mein Problem ist nur, dass ich a) nicht weiß ob alle Daten des Datensatzes angegeben sind und b) wenn nein welche denn nun angegeben sind. Deswegen muss ich immer mit if überprüfen, welche Daten angegeben sind, um sie in die Datenbank zu schreiben. Wenn dieser Datensatz dann vorhanden ist, soll er natürlich auch in die Datenbank geschrieben werden. Also:

      Erst nen leeren Datensatz in die jeweiligen Tabellen schreiben, damit itemID mit dem aktuellen Wert schon innerhalb der Tabellen vorhanden ist und dann mit einem UPDATE ... WHERE itemID = '$CurrentItemID' drüber gehen?

      Gibt es hier andere Ansätze?

      Vielen Dank.

      1. Tach!

        Ich nehme das einfach mit SELECT MAX(itemID) und füge stumpf danach +1 hinzu. Schon haben wir die jetzt zu verwendende ID für den nächsten Datensatz.

        Warum willst du das auf diese unsichere Art machen? Warum fügst du nicht einfach den Datensatz ein und liest dann die dafür vergebene ID über mysql(i)_insert_id() aus?

        Was ist jetzt sinnvoll? Soll ich ganz am Anfang, wenn ein neuer Datensatz eingefügt werden soll einfach schonmal in jeder der Tabellen einen leeren Datensatz einfügen, damit überall schonmal itemID X eingetragen ist und ich dann nur noch mit Update drüber fahren muss, um die einzelnen Werte einzutragen oder wie soll ich das machen?

        Warum hast du getrennte Tabellen, wenn anscheinend zu jedem Datensatz exakt ein einzelner Datensatz in anderen Tabellen vorhanden ist? Und warum soll der leer vorbereitet werden? Beim Abfragen bringt das keine Vorteile. Zum einen sind Joins erforderlich, zum anderen sieht man nicht vorhandene Werte auch beim (Left) Join. Wenn du Daten nicht auf einmal sondern stückweise in die Felder schreiben willst, dann kannst du INSERT ... ON DUPLICATE KEY UPDATE ... verwenden, ohne immer zusätzlich auf die Existenz zu prüfen.

        Klar funktioniert der Code so wie er da steht nicht ganz - das merke ich auch gerade. Mein Problem ist nur, dass ich a) nicht weiß ob alle Daten des Datensatzes angegeben sind und b) wenn nein welche denn nun angegeben sind. Deswegen muss ich immer mit if überprüfen, welche Daten angegeben sind, um sie in die Datenbank zu schreiben. Wenn dieser Datensatz dann vorhanden ist, soll er natürlich auch in die Datenbank geschrieben werden. Also:

        Kommen die Daten denn kleckerweise oder stehen sie alle auf einmal zur Verfügung? Wenn du mal diese und mal jene Felder übergeben bekommst, dann iterier doch über all die aktuell vorhandenen Informationen, vergleiche mit eine Liste an erlaubten Feldnamen und füge sie in eines temporäres Array ein. Aus diesem baust du dann das Statement zusammen (mit implode() über die Keys für die Feldnamen und über die Werte für den VALUES-Teil).

        dedlfix.

        1. Warum willst du das auf diese unsichere Art machen? Warum fügst du nicht einfach den Datensatz ein und liest dann die dafür vergebene ID über mysql(i)_insert_id() aus?

          Weil dafür erstmal ein Datensatz eingefügt werden müsste. Ich habe das aber bisher so gemacht, dass alle einzutragenden Daten einzeln eingetragen werden, da ich im vorhinein nicht weiß welche Daten vorhanden sind und wieviele. Also: Erst neue itemID bekommen, und dann einzeln die vorhandenen Daten eintragen, zugehörig zur itemID, die gerade erstellt wurde.

          Warum hast du getrennte Tabellen, wenn anscheinend zu jedem Datensatz exakt ein einzelner Datensatz in anderen Tabellen vorhanden ist? Und warum soll der leer vorbereitet werden? Beim Abfragen bringt das keine Vorteile. Zum einen sind Joins erforderlich, zum anderen sieht man nicht vorhandene Werte auch beim (Left) Join. Wenn du Daten nicht auf einmal sondern stückweise in die Felder schreiben willst, dann kannst du INSERT ... ON DUPLICATE KEY UPDATE ... verwenden, ohne immer zusätzlich auf die Existenz zu prüfen.

          Ich habe getrennte Tabellen, weil pro Tabelle nicht nur itemID und ein weiterer Datensatz vorhanden sind, sondern bis zu mehreren Dutzend. Will heißen es handelt sich hier durchaus um eine sehr große Datenbank! Alles in eine table zu klatschen wäre absolut daneben. Leer vorbereiten war von mir nur eine Idee, um nachher Statements der Art UPDATE... WHERE itemID = '$itemID' ausführen zu können - da ich ja vor dem Eintragen der eigentlichen Daten eine neue itemID bekomme und danach erst die Daten eintrage. Ablauf wäre also folgender:

          1. Schaue nach was die aktuelle itemID ist
          2. itemID += 1
          3. schreibe in alle Tabellen in eine neue row schonmal die itemID rein, lasse die anderen Felder leer
          4. Füge die vorhandenen Daten alle nach dem Muster UPDATE ... WHERE itemID = '$itemID' rein.

          Kommen die Daten denn kleckerweise oder stehen sie alle auf einmal zur Verfügung? Wenn du mal diese und mal jene Felder übergeben bekommst, dann iterier doch über all die aktuell vorhandenen Informationen, vergleiche mit eine Liste an erlaubten Feldnamen und füge sie in eines temporäres Array ein. Aus diesem baust du dann das Statement zusammen (mit implode() über die Keys für die Feldnamen und über die Werte für den VALUES-Teil).

          Die Daten stehen über $this->Data in einem großen mehrdimensionalem Array zur Verfügung. Und über diese iteriere ich bereits. Will heißen jedes Element von $this->Data[$i][] ist ein weiteres Stück, das in die DB geschrieben wurden muss. Wobei $i hier angibt zu welchem Datensatz das gehört.

          Was ich eigentlich nur wissen wollte ist, ob das so wie ich es mir dachte ok ist, oder es bessere Lösungen gäbe.

          1. Tach!

            Was ich eigentlich nur wissen wollte ist, ob das so wie ich es mir dachte ok ist, oder es bessere Lösungen gäbe.

            Die ID zuerst zu ermitteln, ist nur in Einzelnutzer-Umgebungen sinnvoll, weil ansonsten nicht sichergestellt werden kann, dass zwischen dem Abfragen und dem Eintragen nicht schon jemand anderes schneller war.

            dedlfix.

  3. Hallo,

    ... die Verbindung seit dem geschlossen wurde, und jetzt an einem ganz anderem Datum erst wieder eine Verbindung zur DB hergestellt

    Wenn 'id' Deine Auto-Spalte ist und Du hast eine neue Verbindung, mach einfach ein Update auf die id selbst wie folgt:

    UPDATE table set id = LAST_INSERT_ID(id);
        Beachte: (id) in der Klammer

    und frage danach in der gleichen Session LAST_INSERT_ID ab.

    SELECT LAST_INSERT_ID();
    oder halt mit der entsprechenden PHP-Funktion.

    Du bekommst exakt den Wert für die zuletzt vergebene id in der Tabelle.

    Hotti