MySQL AUTO_INCREMENT не ROLLBACK - PullRequest
       54

MySQL AUTO_INCREMENT не ROLLBACK

58 голосов
/ 16 января 2009

Я использую поле MySQL AUTO_INCREMENT и InnoDB для поддержки транзакций. Я заметил, что при откате транзакции поле AUTO_INCREMENT не откатывается? Я узнал, что это было разработано таким образом, но есть ли обходные пути к этому?

Ответы [ 11 ]

74 голосов
/ 16 января 2009

Так не может быть. Рассмотрим:

  • запрограммировав один, вы открываете транзакцию и вставляете в таблицу FOO, у которой есть первичный ключ autoinc (произвольно, мы говорим, что он получает 557 в качестве значения ключа).
  • Запускается вторая программа, она открывает транзакцию и вставляет в таблицу FOO, получая 558.
  • Запрограммируйте две вставки в таблицу BAR, столбец которой является внешним ключом для FOO. Так что теперь 558 находится как в FOO, так и в BAR.
  • Программа два теперь фиксирует.
  • Программа три запускается и генерирует отчет из таблицы FOO. Запись 558 напечатана.
  • После этого программа 1 откатывается назад.

Как база данных восстанавливает значение 557? Идет ли он в FOO и уменьшает все остальные первичные ключи больше 557? Как это исправить БАР? Как стереть 558 напечатанных на отчете программы три вывода?

Порядковые номера Oracle также не зависят от транзакций по той же причине.

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

Теперь, если у вас есть требование, чтобы в вашем поле автоинкремента никогда не было пробелов (скажем, для целей аудита). Тогда вы не сможете откатить свои транзакции. Вместо этого вам нужно иметь флаг статуса в ваших записях. При первой вставке статус записи «Незавершенный», затем вы начинаете транзакцию, выполняете свою работу и обновляете статус до «конкурировать» (или все, что вам нужно). Затем, когда вы фиксируете, запись в прямом эфире. Если транзакция откатывается, неполная запись все еще там для аудита. Это вызовет много других головных болей, но это один из способов справиться с контрольными журналами.

53 голосов
/ 16 января 2009

Позвольте мне указать на кое-что очень важное:

Вы никогда не должны зависеть от числовых характеристик автоматически сгенерированных ключей.

То есть, кроме сравнения их на равенство (=) или неравенство (<>), вы не должны делать ничего другого. Без реляционных операторов (<,>), без сортировки по индексам и т. Д. Если вам нужно выполнить сортировку по дате добавления, добавьте столбец «дата добавления».

Рассматривайте их как яблоки и апельсины. Имеет ли смысл спрашивать, является ли яблоко таким же, как апельсин? Да. Есть ли смысл спрашивать, больше ли яблоко, чем апельсин? Нет. (На самом деле это так, но ты понимаешь мою точку зрения.)

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

12 голосов
/ 09 июня 2011

У меня был клиент, которому нужен идентификатор для отката в таблице счетов, где заказ должен быть последовательным

Мое решение в MySQL состояло в том, чтобы удалить AUTO-INCREMENT и извлечь последний Id из таблицы, добавить один (+1) и затем вставить его вручную.

Если таблица называется «TableA», а столбец автоинкремента - «Id»

INSERT INTO TableA (Id, Col2, Col3, Col4, ...)
VALUES (
(SELECT Id FROM TableA t ORDER BY t.Id DESC LIMIT 1)+1,
Col2_Val, Col3_Val, Col4_Val, ...)
11 голосов
/ 16 января 2009

Почему тебя волнует, если он откатился? Ключевые поля AUTO_INCREMENT не должны иметь никакого значения, поэтому вам действительно не важно, какое значение используется.

Если у вас есть информация, которую вы хотите сохранить, возможно, нужен еще один неключевой столбец.

8 голосов
/ 16 января 2009

Я не знаю ни одного способа сделать это. Согласно документации MySQL , это ожидаемое поведение, которое будет происходить со всеми режимами блокировки innodb_autoinc_lock_mode Конкретный текст:

Во всех режимах блокировки (0, 1 и 2), если сгенерированная транзакция автоинкремент значений откатывается, эти значения автоинкремента «Потерян». Как только значение генерируется для столбец автоинкремента, он не может быть откат, независимо от того, «INSERT-подобный» оператор завершен, и содержит ли транзакция откатывается. Такой потерянный значения не используются повторно. Таким образом, может пробелы в значениях, хранящихся в AUTO_INCREMENT столбец таблицы.

4 голосов
/ 16 февраля 2012

Если вы установите auto_increment на 1 после отката или удаления, при следующей вставке MySQL увидит, что 1 уже используется, и вместо этого получит значение MAX() и добавит к нему 1.

Это обеспечит повторное использование строки с последним значением (или откат вставки).

Чтобы установить для auto_increment значение 1, сделайте что-то вроде этого:

ALTER TABLE tbl auto_increment = 1

Это не так эффективно, как просто переходить к следующему номеру, потому что MAX() может быть дорогостоящим, но если вы удаляете / откатываете редко и одержимы повторным использованием наибольшего значения, тогда это реалистичный подход.

Имейте в виду, что это не предотвращает пропуски в записях, удаленных посередине, или если должна произойти другая вставка до того, как вы установите auto_increment обратно в 1.

2 голосов
/ 11 ноября 2011
INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))

Если таблица не содержит значений или нулевых строк

добавить цель для ошибки обновления типа MySQL ОТ ОТ ВЫБРАТЬ

1 голос
/ 19 ноября 2015

РЕШЕНИЕ:

Давайте используем tbl_test в качестве примера таблицы и предположим, что поле 'Id' имеет атрибут AUTO_INCREMENT

CREATE TABLE tbl_test (
Id int NOT NULL AUTO_INCREMENT ,
Name varchar(255) NULL ,
PRIMARY KEY (`Id`)
)
;

Предположим, что таблица содержит houndred или тысячу строк уже вставлена, и вы больше не хотите использовать AUTO_INCREMENT; потому что при откате транзакции поле Id всегда добавляет +1 к значению AUTO_INCREMENT. Поэтому, чтобы избежать этого, вы можете сделать это:

  • Давайте удалим значение AUTO_INCREMENT из столбца 'Id' (это не удалит вставленные вами строки):
ALTER TABLE tbl_test MODIFY COLUMN Id  int(11) NOT NULL FIRST;
  • Наконец, мы создаем триггер BEFORE INSERT для автоматической генерации значения Id. Но использование этого способа не повлияет на значение идентификатора, даже если вы откатите любую транзакцию.
CREATE TRIGGER trg_tbl_test_1
BEFORE INSERT ON tbl_test
FOR EACH ROW
  BEGIN
    SET NEW.Id= COALESCE((SELECT MAX(Id) FROM tbl_test),0) + 1;
  END;

Вот и все! Вы сделали!

Не за что.
0 голосов
/ 06 июля 2018
$masterConn = mysql_connect("localhost", "root", '');
mysql_select_db("sample", $masterConn);

for($i=1; $i<=10; $i++) {
    mysql_query("START TRANSACTION",$masterConn);
    $qry_insert = "INSERT INTO `customer` (id, `a`, `b`) VALUES (NULL, '$i', 'a')";
    mysql_query($qry_insert,$masterConn);
    if($i%2==1) mysql_query("COMMIT",$masterConn);
    else mysql_query("ROLLBACK",$masterConn);

    mysql_query("ALTER TABLE customer auto_increment = 1",$masterConn);

}



echo "Done";
0 голосов
/ 01 сентября 2014

Конкретный ответ на эту конкретную дилемму (которая у меня также была) следующий:

1) Создайте таблицу, в которой будут храниться разные счетчики для разных документов (счета, квитанции, RMA и т. Д.); Вставьте запись для каждого из ваших документов и добавьте начальный счетчик к 0.

2) Перед созданием нового документа выполните следующие действия (например, для счетов):

UPDATE document_counters SET counter = LAST_INSERT_ID(counter + 1) where type = 'invoice'

3) Получите последнее значение, которое вы только что обновили, например:

SELECT LAST_INSERT_ID()

или просто используйте PHP (или любую другую) функцию mysql_insert_id (), чтобы получить то же самое

4) Вставьте новую запись вместе с основным идентификатором, который вы только что вернули из БД. Это переопределит текущий индекс автоинкремента и убедится, что между записями нет пробелов в идентификаторе.

Все это, конечно, нужно обернуть внутри транзакции. Прелесть этого метода в том, что при откате транзакции ваш оператор UPDATE из шага 2 будет откатываться, и счетчик больше не будет меняться. Другие параллельные транзакции будут блокироваться до тех пор, пока первая транзакция не будет зафиксирована или откатана, поэтому у них не будет доступа ни к старому счетчику, ни к новой, пока все остальные транзакции не будут завершены первыми.

...