Вопрос

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

Это было полезно?

Решение

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

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

То есть, кроме сравнения их на равенство (=) или неравенство (< >), вы не должны делать ничего другого. Нет реляционных операторов (& Lt ;, & Gt;), нет сортировки по индексам и т. Д. Если вам нужно отсортировать по & Quot; дате добавления & Quot ;, есть & Quot; дата добавления " столбец.

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

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

Другие советы

Это не может так сработать.Рассмотреть:

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

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

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

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

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

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

Мое решение в 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, ...)

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

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

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

  

Во всех режимах блокировки (0, 1 и 2), если   сгенерированная транзакция   автоинкремент значений откатывается,   эти значения автоинкремента   & # 8220;. Потеряли # 8221 &; Как только значение сгенерировано для   столбец автоинкремента, он не может быть   откат, независимо от того,   & # 8220; ВСТАВИТЬ типа # 8221 &; заявление завершено,   и содержит ли   транзакция откатывается. Такой потерянный   значения не используются повторно. Таким образом, может   пробелы в значениях, хранящихся в   AUTO_INCREMENT столбец таблицы.

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

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

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

ALTER TABLE tbl auto_increment = 1

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

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

INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))

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

добавить цель для ошибки обновления типа mysql ОТ при SELECT

РЕШЕНИЕ:

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

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

Давайте предположим, что в таблицу уже вставлено несколько или тысяч строк, и вы больше не хотите использовать 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;

Вот и все!С тобой покончено!

Всегда пожалуйста.

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

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

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

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 будет откатываться, и счетчик больше не будет меняться. Другие параллельные транзакции будут блокироваться до тех пор, пока первая транзакция не будет зафиксирована или откатана, поэтому у них не будет доступа ни к старому счетчику, ни к новому, пока все остальные транзакции не будут завершены первыми.

$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";
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top