Zapobiegaj automatycznemu przyrostowi na MySQL duplicate insert

Używając MySQL 5.1.49, próbuję zaimplementować system tagowania mam problem z tabelą z dwoma kolumnami: id(autoincrement), tag(unique varchar) (InnoDB)

Podczas korzystania z zapytania, INSERT IGNORE INTO tablename SET tag="whatever", wartość auto increment id zwiększa się, nawet jeśli wkładka została zignorowana.

Normalnie nie byłoby to problemem, ale spodziewam się wielu możliwych prób Wstawienia duplikatów dla tej konkretnej tabeli, co oznacza, że moja następna wartość dla id pola nowego wiersza będzie przeskakiwać o wiele za dużo.

Na przykład skończę z tabelą z powiedzmy 3 wierszami, ale źle id's

1   | test
8   | testtext
678 | testtextt

Ponadto, jeśli nie wykonam INSERT IGNORE i po prostu wykonam zwykły INSERT INTO i zajmę się błędem, pole auto increment nadal się zwiększa, więc następna prawdziwa wstawka nadal jest błędnym auto increment.

Czy istnieje sposób, aby zatrzymać automatyczną inkrementację, jeśli istnieje próba duplikatu wiersza INSERT?

Jak rozumiem dla MySQL 4.1, ta wartość nie będzie wzrastać, ale ostatnią rzeczą, którą chcę zrobić, to skończyć albo wykonując wiele SELECT oświadczeń z góry, aby sprawdzić, czy znaczniki istnieją, lub co gorsza, obniżyć wersję MySQL.

Author: emotality, 2011-05-07

8 answers

Możesz zmodyfikować swoją wstawkę tak, aby była taka:

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

Gdzie $tag jest tagiem (poprawnie cytowanym lub oczywiście jako symbol zastępczy), który chcesz dodać, jeśli go jeszcze nie ma. Takie podejście nie spowoduje nawet Wstawienia (i późniejszego marnowania AutoIncrement), jeśli znacznik już tam jest. Prawdopodobnie mógłbyś wymyślić ładniejszy SQL niż ten, ale powyższy powinien załatwić sprawę.

Jeśli twoja tabela jest odpowiednio zindeksowana, to dodatkowe SELECT for the existence check będzie Bądź szybki, a baza danych i tak będzie musiała wykonać to sprawdzenie.

To podejście nie będzie działać dla pierwszego tagu. Możesz zalać tabelę tagów tagiem, który Twoim zdaniem zawsze będzie używany lub możesz zrobić osobne sprawdzenie pustej tabeli.

 23
Author: mu is too short,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 19:19:31

Właśnie znalazłem ten klejnot...

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
)

If affectedRows = 1 then it inserted; w przeciwnym razie if affectedRows = 0 There was a duplicate.

 14
Author: Jamie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-24 07:20:07

Dokumentacja MySQL dla v 5.5 mówi:

"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/en/information-functions.html#function_last-insert-id

Od wersji 5.1 InnoDB posiada konfigurowalne automatyczne blokowanie przyrostowe. http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html#innodb-auto-inc...

Obejście: użyj opcji innodb_autoinc_lock_mode=0 (tradycyjne).

 14
Author: Per Quested Aronsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-10 13:12:07

Znalazłem mu jest zbyt krótka odpowiedź pomocna, ale ograniczająca, ponieważ nie robi wstawek na pustej tabeli. Znalazłem prostą modyfikację:

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

Zastąpienie tabeli w klauzuli from tabelą " fałszywą "(select 1) as a pozwoliło tej części zwrócić rekord, który umożliwił wstawienie. Uruchamiam mysql 5.5.37. Dzięki mu, że załatwiłeś mi większość drogi ....

 13
Author: Landon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-11 23:43:23

Zaakceptowana odpowiedź była przydatna, jednak napotkałem problem podczas korzystania z niej, że w zasadzie, jeśli twoja tabela nie miała wpisów, nie będzie działać, ponieważ select używał podanej tabeli, więc zamiast tego wymyśliłem następujące, które wstawią nawet jeśli tabela jest pusta, to również potrzebuje tylko wstawić tabelę w 2 miejscach i wstawianie zmiennych w 1 miejscu, mniej, aby się pomylić.

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*/

Mam nadzieję, że okaże się przydatny

 2
Author: Joseph Bailey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-02 09:49:23

Zawsze możesz dodać ON DUPLICATE KEY UPDATE przeczytaj tutaj (nie do końca, ale wydaje się, że rozwiązuje twój problem).

From the comments, by @ ravi

To czy przyrost nastąpi czy nie zależy od ustawienie innodb_autoinc_lock_mode. Jeśli ustawiona jest na wartość niezerową, licznik Auto-inc zwiększy się nawet wtedy, gdy zostanie wywołany klucz ON DUPLICATE

 2
Author: Itay Moav -Malimovka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-19 10:13:16

Miałem ten sam problem, ale nie chciałem używać innodb_autoinc_lock_mode = 0, ponieważ czułem się, jakbym zabijał muchę haubicą.

Aby rozwiązać ten problem, użyłem tymczasowej tabeli.

create temporary table mytable_temp like mytable;

Następnie wstawiłem wartości z:

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

Po tym po prostu wykonaj kolejną wstawkę, ale użyj "Nie w", aby zignorować duplikaty.

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

Nie testowałem tego pod kątem wydajności, ale robi to zadanie i jest łatwe do odczytania. Przyznam, że było to ważne tylko dlatego, że pracowałem z danymi, które były stale aktualizowane, więc nie mogłem zignorować luk.

 1
Author: Thomas B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-30 18:36:51

Po zapytaniu insert/update dodałem dodatkowe oświadczenie: ALTER TABLE table_name AUTO_INCREMENT = 1 A potem automatycznie wybiera najwyższy identyfikator klucza prim plus 1.

 -1
Author: Wopke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-05 08:34:35