Запретить автоматическое увеличение при вставке дубликата MySQL - PullRequest
37 голосов
/ 08 мая 2011

Используя MySQL 5.1.49, я пытаюсь реализовать систему тегов, у меня проблема с таблицей из двух столбцов: id(autoincrement), tag(unique varchar) (InnoDB)

При использовании запроса INSERT IGNORE INTO tablename SET tag="whatever" значение автоинкремента id увеличивается, даже если вставка была проигнорирована.

Обычно это не будет проблемой, но я ожидаю много возможных попыток вставитьдубликаты для этой конкретной таблицы, что означает, что мое следующее значение для поля id новой строки будет слишком сильно перепрыгивать.

Например, я получу таблицу с, скажем, 3 рядами, но плохая id

1   | test
8   | testtext
678 | testtextt

Кроме того, если я не выполняю INSERT IGNORE, а просто выполняю обычный INSERT INTO и обрабатываю ошибку, поле автоматического приращения все еще увеличивается, поэтому следующая истинная вставка все ещенеправильное автоинкремент.

Есть ли способ остановить автоинкремент, если есть попытка дублирования строки INSERT?

Как я понимаю для MySQL 4.1, это значение не будет увеличиваться, но последнеечто я хочу сделать, это в конечном итоге либо делать много SELECT заявления заранее, чтобы проверить, существуют ли теги, или еще хуже, понизить мою версию MySQL.

Ответы [ 8 ]

22 голосов
/ 08 мая 2011

Вы можете изменить вашу ВСТАВКУ на что-то вроде этого:

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

Где $tag - это тег (должным образом заключенный в кавычки или, конечно, в качестве заполнителя), который вы хотите добавить, если его там еще нет. Этот подход даже не вызовет INSERT (и последующую потерю автоинкремента), если тег уже существует. Вы, вероятно, могли бы придумать более хороший SQL, чем этот, но вышеприведенное должно помочь.

Если ваша таблица правильно проиндексирована, тогда дополнительный SELECT для проверки существования будет быстрым, и база данных все равно должна будет выполнить эту проверку.

Этот подход не будет работать для первого тега. Вы можете заполнить свою таблицу тегов тегом, который, по вашему мнению, всегда будет использоваться, или вы можете выполнить отдельную проверку для пустой таблицы.

14 голосов
/ 09 ноября 2012

Документация MySQL для v 5.5 гласит:

"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."

Ссылка: http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id

Начиная с версии 5.1 InnoDB имеет настраиваемую блокировку автоматического увеличения.См. Также http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html#innodb-auto-inc...

Обходной путь: используйте параметр innodb_autoinc_lock_mode = 0 (традиционный).

12 голосов
/ 24 августа 2011

Я только что нашел этот драгоценный камень ...

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
)

Если disabledRows = 1, то он вставлен;в противном случае, если lostRows = 0, был дубликат.

11 голосов
/ 12 августа 2014

Я нашел, что mu - слишком короткий ответ, полезный, но ограничивающий, потому что он не делает вставки в пустую таблицу. Я нашел простую модификацию и сделал свое дело:

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

Замена таблицы в предложении from на "поддельную" таблицу (select 1) as a позволила этой части вернуть запись, которая позволила выполнить вставку. Я использую MySQL 5.5.37. Спасибо, мю, за то, что вы проделали большую часть пути ...

3 голосов
/ 08 мая 2011

Вы всегда можете добавить ON DUPLICATE KEY UPDATE Читайте здесь (не совсем, но, похоже, это решает вашу проблему).

Из комментариев @ ravi

Будет ли увеличение или нет, зависит от настройка innodb_autoinc_lock_mode. Если установлено ненулевое значение, Счетчик авто-инк будет увеличиваться, даже если клавиша ON DUPLICATE срабатывает

1 голос
/ 06 августа 2015

Принятый ответ был полезен, однако при его использовании я столкнулся с проблемой, заключающейся в том, что если в вашей таблице нет записей, она не будет работать, так как выборка использует данную таблицу, поэтому вместо этого я придумал следующее:вставить, даже если таблица пуста, вам также нужно только вставить таблицу в 2-х местах и ​​вставить переменные в 1-м месте, меньше, чтобы ошибиться.

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

Надеюсь, вы найдете это полезным

1 голос
/ 30 июня 2012

У меня была та же проблема, но я не хотел использовать innodb_autoinc_lock_mode = 0, так как мне казалось, что я убил муху с помощью гаубицы.

Чтобы решить эту проблему, я использовал временную таблицу.

create temporary table mytable_temp like mytable;

Затем я вставил значения с помощью:

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

После этого вы просто делаете другую вставку, но используете "not in", чтобы игнорировать дубликаты.

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

Я не проверял это на производительность, но он делает работу и легко читается.Конечно, это было важно только потому, что я работал с данными, которые постоянно обновлялись, поэтому я не мог игнорировать пробелы.

0 голосов
/ 05 апреля 2016

Я просто поставил дополнительный оператор после запроса вставки / обновления: ALTER TABLE table_name AUTO_INCREMENT = 1 И тогда он автоматически выбирает самый высокий идентификатор первичного ключа плюс 1.

...