Ответ 1
Используйте ROW_COUNT()
, чтобы определить, была ли ваша попытка условной вставки успешной, а затем верните LAST_INSERT_ID()
или значение по умолчанию на основе этого:
select IF(ROW_COUNT() > 0, LAST_INSERT_ID(), 0);
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, если возвращается ненулевое значение?
Используйте 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()
сразу после инструкции, которая генерирует значение.[ deletia ]Причина различий между
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
вы могли бы reset с помощью:
SELECT LAST_INSERT_ID(NULL);
Это также должно reset значение, возвращаемое mysql_insert_id()
, хотя в документации предлагается вызов LAST_INSERT_ID(expr)
должно происходить в INSERT
или UPDATE
statement — может потребоваться проверка для проверки. В любом случае, должно быть довольно тривиально создавать такой оператор no-op, если это необходимо:
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()
полный gotchas, я использую 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 [email protected];
DELETE FROM MyTable_InsertLog WHERE [email protected];
Если вставка удалась, вы получите строки из select - и эти строки будут иметь ваш ID. Нет строк, нет вставки. В любом случае удалите результаты из журнала вставки, чтобы вы могли повторно использовать этот тег при последующих вызовах.
Функция 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
также имеет нуль или одну строку).
Я бы советовал против этого. Условная вставка таким образом уже немного хрупка, и построение поверх нее не добавит стабильности вашему приложению.