Альтернатива BEGIN; COMMIT;поскольку транзакция влияет на мою способность использовать LAST_INSERT_ID ()? - PullRequest
0 голосов
/ 25 апреля 2019

Мне нужно ВСТАВИТЬ INTO несколько таблиц для каждой записи, и чтобы сделать это безопасно, я решил сделать весь оператор транзакцией.Однако мой код основан на том, что LAST_INSERT_ID () обновляется с каждым INSERT INTO, и похоже, что LAST_INSERT_ID () обновляется только каждый TRANSACTION .Поэтому мне либо нужна альтернатива типичному соглашению START TRANSACTION; COMMIT;или мне нужна альтернатива LAST_INSERT_ID (), которая по-прежнему будет достигать той же цели.

Оба person и yearandpage имеют первичные ключи, которые автоматически увеличиваются,и эти ключи являются внешними ключами в таблице personinschoolyearbook .

Исходная проблема, которая заставила меня задать этот вопрос, состоит в том, что у меня есть две таблицы с первичными ключами и реляционная таблица, которая ссылается на оба,Как я могу обновить все три одновременно с одной записи данных?Я рассмотрел каскадную ссылочную целостность , которая автоматически обновит мои внешние ключи, но я не думаю, что это сработает, поскольку в моей реляционной таблице есть значение не-внешнего ключа (состояние), которое необходимо вставить вручную.

START TRANSACTION;
INSERT INTO person (firstname, middlename, lastname, maidenname)
VALUES ('Test2fname', NULL, 'Test2lname', NULL);
INSERT INTO personinschoolyearbook (personid, yearandpageid, status)
VALUES (LAST_INSERT_ID(), 0, 'Test2status'); --The zero is a placeholder, to be replaced with the primary key id that will be created in the next INSERT INTO
SELECT @personid := LAST_INSERT_ID();
INSERT INTO yearandpage (yearincluded, page, schoolid, url) --Here is where the LAST_INSERT_ID() should update but doesn't
VALUES (0000, 00, 2, 'Test2url');
UPDATE personinschoolyearbook SET yearandpageid=LAST_INSERT_ID()
WHERE personinschoolyearbook.personid = @personid;
COMMIT;

В данный момент значение LAST_INSERT_ID () не изменяется, и в моей реляционной таблице personinschoolyearbook столбец, который должен иметь новый идентификатор, который был взят из yearandpage *Вместо этого 1021 * - это тот же идентификатор, что и у персона .

Ответы [ 2 ]

1 голос
/ 26 апреля 2019

LAST_INSERT_ID () всегда сообщает о самом последнем значении auto-inc, созданном в текущем сеансе. Это не ограничено транзакциями. Вы можете начать новую транзакцию, и LAST_INSERT_ID () по-прежнему будет возвращать самое последнее значение auto-inc.

mysql> create table foo (id serial primary key);

mysql> begin;

mysql> insert into foo () values ();
Query OK, 1 row affected (0.01 sec)

mysql> select last_insert_id() into @id1;
Query OK, 1 row affected (0.00 sec)

mysql> insert into foo () values ();
Query OK, 1 row affected (0.00 sec)

mysql> select last_insert_id() into @id2;
Query OK, 1 row affected (0.00 sec)

mysql> select @id1, @id2;
+------+------+
| @id1 | @id2 |
+------+------+
|    1 |    2 |
+------+------+

Если вы вставляете значение в таблицу, в которой есть auto-inc, но вы указываете ненулевое значение и, следовательно, не допускаете, чтобы auto-inc генерировал новый id, оно также не изменяет значение, возвращаемое LAST_INSERT_ID () ;

mysql> insert into foo set id = 10;
Query OK, 1 row affected (0.01 sec)

mysql> select * from foo;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
| 10 |
+----+

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                3 |
+------------------+

Если у вас есть первичный ключ, который не авто-inc, INSERT не изменяет значение, возвращаемое LAST_INSERT_ID () для самого последнего идентификатора, сгенерированного в текущем сеансе любым авто-inc , даже если это было для другой таблицы или в другой транзакции.

mysql> create table bar (id bigint primary key); -- not AUTO_INCREMENT

mysql> insert into bar set id = 42;
Query OK, 1 row affected (0.01 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                3 |
+------------------+
0 голосов
/ 30 апреля 2019

Как указано в ответе Билла Карвина, транзакции не влияют на LAST_INSERT_ID (). Причина, по которой он не обновлялся, заключалась в том, что, когда я перешел на INSERT INTO yearandpage , если запись уже существовала, тогда никакие данные не были бы введены, и поэтому LAST_INSERT_ID () не могло бы принимать никакого значения. Ууэрдо рассказал, как это может быть проблемой, но, поскольку я как-то пропустил ошибку повторяющегося ввода при запуске кода, я подумал, что INSERT INTO работает нормально.

Дублирующие записи были причиной моей проблемы и должны были быть учтены в проекте, поэтому, чтобы обойти эту проблему, я использовал ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID (id), о котором вы можете прочитать подробнее на https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html

Это мой окончательный код:

START TRANSACTION;
INSERT INTO person (firstname, middlename, lastname, maidenname)
VALUES ('Test2fname', NULL, 'Test2lname', NULL);
INSERT INTO personinschoolyearbook (personid, yearandpageid, status)
VALUES (LAST_INSERT_ID(), 0, 'Test2status');
SELECT @personid := LAST_INSERT_ID();
INSERT INTO yearandpage (yearincluded, page, schoolid, url)
VALUES (0000, 00, 2, 'Test2url') 
ON DUPLICATE KEY UPDATE yearandpageid = LAST_INSERT_ID(yearandpageid);
UPDATE personinschoolyearbook SET yearandpageid=LAST_INSERT_ID()
WHERE personinschoolyearbook.personid = @personid;
COMMIT;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...