MySQL INSERT ….ON DUPLICATE UPDATE – Fügt dem Autoinkrement eins hinzu

Lesezeit: 6 Minuten

Benutzer-Avatar
M. von CA

Ich verfolge alle http_user_agents, die mich besuchen, mit einem einfachen Zugriffszähler. Fügen Sie unten den http_user_agent in die DB ein, dieses Feld unterscheidet nicht zwischen Groß- und Kleinschreibung und ist eindeutig. Wenn wir also versuchen, es einzufügen und es findet einen DUPLICATE KEY, fügt es 1 zum Trefferfeld hinzu.

Das Problem ist, dass mein Auto Increment-Feld immer noch zunimmt, obwohl wir kein Feld eingefügt haben. wie kann ich das verhindern?

$sql = "INSERT INTO `db_agency_cloud`.`tblRefHttpUsersAgent` SET `http_users_agent` = :UsersAgent, `created_ts` = NOW() ON DUPLICATE KEY UPDATE `hits` = `hits` + 1";

Hier ist die Tabellenstruktur:

CREATE TABLE `tblRefHttpUsersAgent`
(
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`http_users_agent` varchar(255) NOT NULL,
`hits` int(20) unsigned NOT NULL DEFAULT '1',
`created_ts` datetime NOT NULL,
`activity_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `http_users_agent` (`http_users_agent`)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

  • Möglicherweise verwandt: stackoverflow.com/questions/2634152/…

    – Leichtigkeitsrennen im Orbit

    17. August 2011 um 23:38 Uhr

Benutzer-Avatar
John Flachheit

INSERT ... ON DUPLICATE KEY UPDATE wird für die Zwecke von InnoDB als “Mixed-Mode-Insert” beschrieben AUTO_INCREMENT Handhabung. Mixed-Mode-Einfügungen sind im Grunde solche, bei denen die maximal Anzahl erforderlich AUTO_INCREMENT Werte ist bekannt, aber die Menge, die wird eigentlich gebraucht werden ist nicht.

Einfügungen im gemischten Modus werden standardmäßig speziell behandelt, wie in beschrieben MySQL-Dokumente:

…für „mixed-mode inserts“… InnoDB weist mehr Auto-Increment-Werte zu als die Anzahl der einzufügenden Zeilen. Alle automatisch zugewiesenen Werte werden jedoch nacheinander generiert (und sind daher höher als) der Autoinkrementwert, der von der zuletzt ausgeführten vorherigen Anweisung generiert wurde. „Überschüssige“ Nummern gehen verloren.

Wenn Sie InnoDB verwenden, sind Ihre Alternativen:

  1. Vermeiden INSERT ... ON DUPLICATE KEY UPDATE.
  2. Stellen Sie die ein innodb_autoinc_lock_mode Parameter zu 0für den “traditionellen” Autoinkrement-Sperrmodus, der das alles garantiert INSERT Anweisungen weisen fortlaufende Werte für zu AUTO_INCREMENT Säulen. Dies wird jedoch durch Sperren während der Anweisung erreicht, sodass mit dieser Einstellung ein Leistungsverlust verbunden ist.
  3. (Empfohlen) Ignorieren Sie die Lücken in der AUTO_INCREMENT Säule.

Notiz: AUTO_INCREMENT Unter MyISAM, das dieses Verhalten nicht zeigt, ist die Handhabung völlig anders.

  • Der Versuch, die einzustellen innodb_autoinc_lock_mode während der Laufzeit gab mir den Fehler: SQLSTATE[HY000]: General error: 1238 Variable 'innodb_autoinc_lock_mode' is a read only variable', wenn man bedenkt, dass ich das für einen Massen-UPSERT brauche, weil einzelne Einfügungen wahnsinnig langsam sind. Ich kann Option 1, 2 oder 3 nicht verwenden, weil die Lücken die Autoinc-IDs zu wahnsinnigen Zahlen verbrennen. Können Sie eine andere Lösung vorschlagen?

    – Timo Huovinen

    27. Oktober 2013 um 13:09 Uhr


  • Würden ähnliche Lücken im Auto-Increment-Feld beobachtet, wenn ich PostgreSQL verwende?

    – WM

    7. August 2016 um 9:36 Uhr

  • Vielen Dank für die tolle Erklärung. Obwohl von Ihnen empfohlen, war die 3. Option nicht auf meinen Fall anwendbar, in dem wir versuchen, mehrere zu tun INSERT ... ON DUPLICATE KEY UPDATE in verschiedenen Transaktionen gleichzeitig, was zu ständigen Deadlocks führte. Nur meine zwei Cent, zur Erinnerung.

    – Empfang

    13. August 2021 um 17:36 Uhr

  • Die Lücken zu ignorieren ist eine Sache, aber meine ID-Spalten in BIGINTs ändern zu müssen, nur damit Tausende von INSERT ... ON DUPLICATE KEY UPDATE Abfragen können täglich ausgeführt werden, ist bestenfalls ärgerlich.

    – Twitney

    22. März um 18:01 Uhr

Die Speicher-Engine muss die inkrementieren AUTO_INCREMENT Wert vor dem Einfügen einer Zeile. Es weiß noch nicht, ob die Einfügung zu diesem Zeitpunkt fehlschlagen wird. Es kann das Inkrement nicht einfach rückgängig machen, da andere Einfügungen gleichzeitig auf anderen Verbindungen stattfinden können. Dies ist ein normales Verhalten und nichts, was Sie ändern sollten (oder können). Der Zweck von AUTO_INCREMENT ist die Bereitstellung eindeutiger Kennungen, nicht einer ununterbrochenen Zahlenfolge.

  • Ja, aber ich wollte nicht zwei Aufrufe an die Datenbank machen. Ich hatte auf eine Lösung gehofft, die dies überspringt. Ich denke vielleicht an eine if-Anweisung. dann Überprüfung auf Datenbankebene, um zu sehen, ob ein Datensatz eingefügt oder aktualisiert werden soll.

    – M. von CA

    17. August 2011 um 6:46 Uhr

  • +1: Und interessant! OK, ich groke warum dies geschieht (dank Ihrer Antwort); es scheint jedoch unglücklich zu sein. Es ist überhaupt nicht intuitiv und ich würde es sogar in Betracht ziehen Insekt (entweder im SQL-Standard oder in MySQL) … selbst wenn es einer ist, der immer als aufgelöst würde WONTFIX.

    – Leichtigkeitsrennen im Orbit

    17. August 2011 um 23:25 Uhr


  • dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html verwirrt mich etwas. Es scheint nichts Relevantes für die Aktualisierung des zu sagen AUTO_INCREMENT Counter oder so, aber als Kuriosum das letzte bisschen über das Machen LAST_INSERT_ID() nützlich hat mich völlig ausgegrenzt. Ich habe keine Ahnung, wovon es spricht.

    – Leichtigkeitsrennen im Orbit

    17. August 2011 um 23:41 Uhr

  • @Tomalak: Das Zeug spricht über was LAST_INSERT_ID() tut, wenn Sie ihm ein Argument übergeben: es gibt diesen Wert zurück und legt diesen Wert als was fest nächste Aufruf LAST_INSERT_ID() sollte zurückkehren. Also auch die LAST_INSERT_ID() Aufruf in dieser Abfrage erzwingt einen nachfolgenden Aufruf von LAST_INSERT_ID() um die ID der von der Anweisung betroffenen Zeile zurückzugeben, unabhängig davon, ob eine neue Zeile eingefügt wurde (was ohne diesen “Hack” bereits funktioniert) oder eine alte Zeile aktualisiert wurde.

    – John Flatness

    18. August 2011 um 0:14 Uhr


Leider befindet sich die Lösung auf Anwendungsebene, wenn Sie die Auto-Increment-IDs nicht beeinflussen möchten. Mach ein SELECT zuerst und zähle die Ergebniszeilen. Wenn 0 ergibt, INSERT die Daten. Wenn mehr als 0, UPDATE diese Reihe.

  • Ja, aber ich wollte nicht zwei Aufrufe an die Datenbank machen. Ich hatte auf eine Lösung gehofft, die dies überspringt. Ich denke vielleicht an eine if-Anweisung. dann Überprüfung auf Datenbankebene, um zu sehen, ob ein Datensatz eingefügt oder aktualisiert werden soll.

    – M. von CA

    17. August 2011 um 6:46 Uhr

Sie können zuerst die maximale Anzahl der eingefügten Zeilen berechnen und 1 dazu addieren,

(SELECT MAX(`id`)+1 FROM `tblRefHttpUsersAgent`)

Dann ändern Sie die Tabelle AUTO_INCREMENT mit einigen Variablen SET @NEW_ID und PREPARE / EXECUTE Aussagen.

Hier ist eine einfache Lösung für dasselbe Problem und unten ist die fertige Version, wenn Ihnen die Lösung Ihres eigenen spezifischen Problems gefällt:

    $sql="SET @NEW_AI = (SELECT MAX(`id`)+1 FROM `tblRefHttpUsersAgent`);
                    SET @ALTER_SQL = CONCAT("ALTER TABLE `tblRefHttpUsersAgent` AUTO_INCREMENT =", @NEW_AI);
                    PREPARE NEWSQL FROM @ALTER_SQL;
                    EXECUTE NEWSQL;";

    $sql .= 'INSERT INTO `db_agency_cloud`.`tblRefHttpUsersAgent` SET `http_users_agent` = :UsersAgent, `created_ts` = NOW() ON DUPLICATE KEY UPDATE `hits` = `hits` + 1';

1018520cookie-checkMySQL INSERT ….ON DUPLICATE UPDATE – Fügt dem Autoinkrement eins hinzu

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy