it-swarm.com.de

Automatisches Inkrementieren bei der doppelten Einfügung von MySQL verhindern

Mit MySQL 5.1.49 versuche ich, ein Tagging-System zu implementieren. Das Problem, das ich habe, ist eine Tabelle mit zwei Spalten: id(autoincrement), tag(unique varchar) (InnoDB)

Wenn Sie die Abfrage INSERT IGNORE INTO tablename SET tag="whatever" verwenden, erhöht sich der Wert für die automatische Inkrementierung id, auch wenn die Einfügung ignoriert wurde.

Normalerweise wäre dies kein Problem, aber ich erwarte viele mögliche Versuche, Duplikate für diese bestimmte Tabelle einzufügen, was bedeutet, dass mein nächster Wert für das id-Feld einer neuen Zeile zu viel springt.

Zum Beispiel werde ich mit einer Tabelle enden, die 3 Zeilen enthält, aber schlechte id's

1   | test
8   | testtext
678 | testtextt

Wenn ich INSERT IGNORE nicht mache und einfach nur INSERT INTO und den Fehler handle, wird das Feld für die automatische Erhöhung immer noch größer, so dass die nächste echte Einfügung immer noch eine falsche automatische Erhöhung ist.

Gibt es eine Möglichkeit, das automatische Inkrementieren zu stoppen, wenn es einen doppelten Zeilenversuch von INSERT gibt?

Wie ich es für MySQL 4.1 verstehe, würde sich dieser Wert nicht erhöhen, aber als letztes möchte ich entweder eine Menge von SELECT -Anweisungen machen, um zu prüfen, ob die Tags vorhanden sind, oder, schlimmer noch, mein Downgrade MySQL-Version.

37
robert

Sie können Ihre INSERT so ändern, dass sie ungefähr so ​​aussieht:

INSERT INTO tablename (tag)
SELECT $tag
FROM tablename
WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)
LIMIT 1

Wo $tag das Tag ist (richtig zitiert oder natürlich als Platzhalter), das Sie hinzufügen möchten, wenn es nicht bereits vorhanden ist. Dieser Ansatz löst nicht einmal eine INSERT-Anweisung (und die anschließende automatische Inkrementierungsverschwendung) aus, wenn das Tag bereits vorhanden ist. Sie könnten wahrscheinlich eine schönere SQL-Version entwickeln als das, aber das sollte der Trick sein.

Wenn Ihre Tabelle ordnungsgemäß indiziert ist, ist das zusätzliche SELECT für die Existenzprüfung schnell und die Datenbank muss diese Prüfung trotzdem durchführen.

Dieser Ansatz funktioniert jedoch nicht für das erste Tag. Sie könnten Ihre Tag-Tabelle mit einem Tag versehen, von dem Sie denken, dass es immer verwendet wird, oder Sie können eine separate Überprüfung auf eine leere Tabelle durchführen.

22
mu is too short

Die MySQL-Dokumentation für Version 5.5 besagt: 

"If you use INSERT IGNORE and the row is ignored, the AUTO_INCREMENT counter 
is **not** incremented and LAST_INSERT_ID() returns 0, 
which reflects that no row was inserted."

Ref: http://dev.mysql.com/doc/refman/5.5/de/information-funktionen.html#function_last-insert-id

InnoDB verfügt seit Version 5.1 über eine konfigurierbare Auto-Increment-Sperre. Siehe auch http://dev.mysql.com/doc/refman/5.1/de/innodb-auto-increment-handling.html#innodb-auto-inc ...

Umgehung: Verwenden Sie die Option innodb_autoinc_lock_mode = 0 (traditionell).

14

Ich habe gerade dieses Juwel gefunden ...

http://www.timrosenblatt.com/blog/2008/03/21/insert-where-not-exists/

INSERT INTO [table name] SELECT '[value1]', '[value2]' FROM DUAL
WHERE NOT EXISTS(
    SELECT [column1] FROM [same table name]
    WHERE [column1]='[value1]'
    AND [column2]='[value2]' LIMIT 1
)

Wenn betroffene Zeilen = 1, wird es eingefügt. Andernfalls war, wenn betroffeneRows = 0, ein Duplikat vorhanden.

10
Jamie

Ich fand die Antwort von mu zu kurz hilfreich, aber einschränkend, da es keine Einfügungen auf einer leeren Tabelle gibt. Ich habe eine einfache Modifikation gefunden, die den Trick gemacht hat:

INSERT INTO tablename (tag)
SELECT $tag
FROM (select 1) as a     #this line is different from the other answer
WHERE NOT EXISTS(
    SELECT tag
    FROM tablename
    WHERE tag = $tag
)
LIMIT 1

Durch Ersetzen der Tabelle in der from-Klausel durch eine "falsche" Tabelle (select 1) as a konnte dieser Teil einen Datensatz zurückgeben, der die Einfügung erlaubte. Ich verwende Mysql 5.5.37. Vielen Dank, dass Sie mich am meisten dort hin gebracht haben ....

10
Landon

Sie können immer ON DUPLICATE KEY UPDATELesen Sie hier (nicht genau, aber löst Ihr Problem so, wie es scheint).

Aus den Kommentaren von @ravi

Ob das Inkrement auftritt oder nicht, hängt von der .__ ab. Einstellung für innodb_autoinc_lock_mode. Wenn auf einen Wert ungleich Null eingestellt ist, wird der Der Auto-Inc-Zähler erhöht sich auch dann, wenn die ON DUPLICATE-Taste ausgelöst wird

Ich hatte das gleiche Problem, wollte aber nicht innodb_autoinc_lock_mode = 0 verwenden, da ich das Gefühl hatte, ich würde eine Fliege mit einer Haubitze töten.

Um dieses Problem zu lösen, habe ich eine temporäre Tabelle verwendet.

create temporary table mytable_temp like mytable;

Dann habe ich die Werte mit eingefügt:

insert into mytable_temp values (null,'valA'),(null,'valB'),(null,'valC');

Danach nehmen Sie einfach eine weitere Einfügung vor, aber verwenden Sie "nicht in", um Duplikate zu ignorieren.

insert into mytable (myRow) select mytable_temp.myRow from mytable_temp 
where mytable_temp.myRow not in (select myRow from mytable);

Ich habe das nicht auf Leistung getestet, aber es funktioniert gut und ist einfach zu lesen. Zugegeben, dies war nur wichtig, weil ich mit Daten arbeitete, die ständig aktualisiert wurden, sodass ich die Lücken nicht ignorieren konnte.

1
Thomas B

Die akzeptierte Antwort war nützlich, jedoch stieß ich bei der Verwendung auf ein Problem, dass, wenn Ihre Tabelle keine Einträge enthält, diese nicht funktionieren würde, da die Auswahl die angegebene Tabelle verwendete. Ich habe stattdessen die folgenden Informationen gefunden, die auch eingefügt werden Die Tabelle ist leer. Außerdem müssen Sie die Tabelle nur an zwei Stellen einfügen und die Variablen an einer Stelle einfügen.

INSERT INTO database_name.table_name (a,b,c,d)
SELECT 
    i.*
FROM
    (SELECT 
        $a AS a, 
            $b AS b,
            $c AS c,
            $d AS d
            /*variables (properly escaped) to insert*/
    ) i
        LEFT JOIN        
    database_name.table_name o ON i.a = o.a AND i.b = o.b /*condition to not insert for*/
WHERE
    o.a IS NULL
LIMIT 1 /*Not needed as can only ever be one, just being sure*/

Ich hoffe, Sie finden es nützlich

1
Joseph Bailey

Ich füge nach der insert/update-Abfrage nur eine zusätzliche Anweisung ein: ALTER TABLE table_name AUTO_INCREMENT = 1 Dann nimmt er automatisch die höchste Prim-Schlüssel-ID plus 1 auf.

0
Wopke