Очистка LAST_INSERT_ID() перед вставкой, чтобы узнать, что получено из моей вставки
LAST_INSERT_ID()
возвращает самый последний идентификатор, сгенерированный для текущего соединения с помощью столбца с автоинкрементом, но как мне узнать, относится ли это значение к последней вставке, а не к предыдущей вставке в том же соединении?
Предположим, я использую соединение из пула, который мог вставить строку до того, как я получил соединение, и я выполняю условную вставку:
insert into mytable (colA)
select 'foo' from bar
where <some condition>;
select LAST_INSERT_ID();
У меня нет никакого способа узнать, возвращено ли значение из моей вставки.
Один из способов, о котором я подумал:
@previousId := LAST_INSERT_ID();
insert into mytable (colA)
select 'foo' from bar
where <some condition>;
select if(LAST_INSERT_ID() != @previousId, LAST_INSERT_ID(), null);
Есть ли способ "очистить" LAST_INSERT_ID()
значение, так что я знаю, что это новое значение, вызванное моим SQL, если возвращается ненулевое значение?
5 ответов
Использование ROW_COUNT()
чтобы определить, была ли ваша попытка условной вставки успешной, а затем вернуться LAST_INSERT_ID()
или значение по умолчанию на основе этого:
select IF(ROW_COUNT() > 0, LAST_INSERT_ID(), 0);
Я согласен с ответом @Digital Chris, что вы не должны определять, была ли вставка успешной или неудачной, проверяя значение, возвращаемое LAST_INSERT_ID()
: есть более прямые маршруты, такие как количество затронутых строк. Тем не менее может существовать некоторое требование для получения "чистого" значения из LAST_INSERT_ID()
,
Конечно, одна проблема с вашим предлагаемым решением (сравнения со значением перед вставкой) заключается в том, что может случиться так, что вставка прошла успешно, и что назначенное ей автоматически увеличиваемое значение совпадает с предыдущей вставкой (предположительно на другой стол). Таким образом, сравнение может привести к предположению, что вставка не удалась, тогда как на самом деле это удалось.
Я рекомендую, если это возможно, избегать использования LAST_INSERT_ID()
Функция SQL в предпочтении для mysql_insert_id()
Вызов API (через ваш драйвер). Как объяснено в документации для последнего:
mysql_insert_id()
возвращается0
если предыдущее утверждение не используетAUTO_INCREMENT
значение. Если вам нужно сохранить значение на потом, обязательно позвонитеmysql_insert_id()
сразу после утверждения, которое генерирует значение.[ делеция ]Причина различий между
LAST_INSERT_ID()
а такжеmysql_insert_id()
в том, чтоLAST_INSERT_ID()
сделано простым в использовании в сценариях, в то время какmysql_insert_id()
пытается предоставить более точную информацию о том, что происходит сAUTO_INCREMENT
колонка.
В любом случае, как указано в LAST_INSERT_ID(expr)
:
Если
expr
дается в качестве аргументаLAST_INSERT_ID()
значение аргумента возвращается функцией и запоминается как следующее значение, возвращаемоеLAST_INSERT_ID()
,
Поэтому перед выполнением вашего INSERT
Вы можете сбросить с помощью:
SELECT LAST_INSERT_ID(NULL);
Это также должно сбросить значение, возвращаемое mysql_insert_id()
, хотя документация предполагает призыв к LAST_INSERT_ID(expr)
должно происходить в течение INSERT
или же UPDATE
утверждение - может потребоваться проверка для проверки. В любом случае, создание такого неоперативного оператора должно быть довольно тривиальным, если это необходимо:
INSERT INTO my_table (my_column) SELECT NULL WHERE LAST_INSERT_ID(NULL);
Стоит отметить, что можно также установить identity
а также last_insert_id
системные переменные (однако они влияют только на значение, возвращаемое LAST_INSERT_ID()
и не mysql_insert_id()
):
SET @@last_insert_id := NULL;
Вы предполагаете, что вы можете получить предыдущий LAST_INSERT_ID()
Но практически я не понимаю, где это могло произойти. Если вы делаете условную вставку, вы должны проверить, была ли она успешной, прежде чем предпринимать следующие шаги. Например, вы всегда будете использовать ROW_COUNT()
проверить вставленные / обновленные записи. Псевдо-код:
insert into mytable (colA)
select 'foo' from bar
where <some condition>;
IF ROW_COUNT() > 0
select LAST_INSERT_ID();
-- ...use selected last_insert_id for other updates/inserts...
END IF;
Поскольку LAST_INSERT_ID()
полон ошибок, я использую AFTER INSERT
триггер для записи идентификатора в таблицу журнала в соответствии с проблемой, которую я пытаюсь решить.
В вашем сценарии, поскольку вставка является условной, используйте идентификатор unqiue, чтобы "пометить" вставку, а затем проверьте наличие этого тега в журнале вставки. Если идентификационный тег присутствует, произошла ваша вставка, и у вас есть вставленный идентификатор. Если идентификационный тег отсутствует, вставка не произошла.
Реализация ссылок
DELIMITER $$
CREATE TRIGGER MyTable_AI AFTER INSERT ON MyTable FOR EACH ROW
BEGIN
INSERT INTO MyTable_InsertLog (myTableId, ident) VALUES (NEW.myTableId, COALESCE(@tag, CONNECTION_ID()));
END $$
DELIMITER ;
CREATE TABLE MyTable_InsertLog (
myTableId BIGINT UNSIGNED PRIMARY KEY NOT NULL REFERENCES MyTable (myTableId),
tag CHAR(32) NOT NULL
);
Пример использования
SET @tag=MD5('A');
INSERT INTO MyTable SELECT NULL,colA FROM Foo WHERE colA='whatever';
SELECT myTableId FROM MyTable_InsertLog WHERE tag=@tag;
DELETE FROM MyTable_InsertLog WHERE tag=@tag;
Если вставка завершится успешно, вы получите строки из выбора - и эти строки будут иметь ваш идентификатор. Нет строк, нет вставки. В любом случае, удалите результаты из журнала вставки, чтобы вы могли использовать этот тег в последующих вызовах.
LAST_INSERT_ID()
Функция имеет довольно узкую сферу применения: так как MySQL не поддерживает SQL SEQUENCE
s, это используется для создания транзакции, которая INSERT
Данные последовательно помещаются в несколько таблиц, если одна ссылается на суррогатный ключ из другой таблицы:
CREATE TABLE foo (id INTEGER PRIMARY KEY AUTO_INCREMENT, value INTEGER NOT NULL);
CREATE TABLE bar (id INTEGER PRIMARY KEY AUTO_INCREMENT, fooref INTEGER NOT NULL);
INSERT INTO foo(value) VALUES ('1');
INSERT INTO bar(fooref) VALUES (LAST_INSERT_ID());
Если вы действительно хотите использовать это в условной вставке, вам нужно сделать второй INSERT
условно, а также путем объединения вставленных значений против SELECT
от первой INSERT
и впоследствии выбрасывая лишние столбцы (так что второй INSERT
также имеет ноль или один ряд).
Я бы посоветовал против этого, хотя. Условная вставка таким образом уже несколько хрупкая, и ее построение не добавит стабильности вашему приложению.