【问题标题】:An alternative to BEGIN;COMMIT; since the transaction affects my ability to use LAST_INSERT_ID()?BEGIN;COMMIT; 的替代方法因为交易影响了我使用 LAST_INSERT_ID() 的能力?
【发布时间】:2019-05-01 03:37:53
【问题描述】:

我需要为每个条目插入多个表,为了安全地执行此操作,我决定将整个语句设为 TRANSACTION。但是,我的代码依赖于每次 INSERT INTO 都会更新 LAST_INSERT_ID(),而且 LAST_INSERT_ID() 似乎只更新每个 TRANSACTION。因此,我需要替代典型的 START TRANSACTION;COMMIT;格式,或者我需要一个 LAST_INSERT_ID() 的替代方法,它仍然可以实现相同的目标。

personyearandpage 都有自动递增的主键,这些键是 personinschoolyearbook 表中的外键。 p>

导致我提出这个问题的最初问题是我有两个带有主键的表和一个引用两者的关系表。如何通过一个数据条目同时更新所有三个?我已经研究了级联引用完整性,这会使我的外键自动更新,但我认为这不会起作用,因为我的关系表有一个必须手动插入的非外键值(状态) .

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 的新 id 的列改为与取自 person 的 id 相同。

【问题讨论】:

  • 您确认上一年页面​​插入成功了吗?
  • 是的,它工作正常。
  • 您是否建议 LAST_INSERT_ID() 应该已更新,这意味着它与事务无关?因为我不确定是哪种情况。

标签: mysql database


【解决方案1】:

LAST_INSERT_ID() 始终报告当前会话中生成的最新自动增量值。这不受交易限制。您可以开始一个新事务,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 |
+------------------+

如果您有一个主键不是 auto-inc,则 INSERT 不会更改 LAST_INSERT_ID() 返回的值,该值是由任何 auto-inc 在当前会话中生成的最新 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 |
+------------------+

【讨论】:

  • 很高兴知道这一点,谢谢。这意味着问题不在于 LAST_INSERT_ID(),而是其他问题。我会继续排查问题。
【解决方案2】:

正如 Bill Karwin 的回答中所述,LAST_INSERT_ID() 不受交易影响。它没有更新的原因是因为当我进入 INSERT INTO yearandpage 时,如果条目已经存在,则不会输入任何数据,因此 LAST_INSERT_ID() 将没有任何价值。 Uueerdo 提出这可能是问题所在,但由于我在运行代码时不知何故错过了重复输入错误,我认为 INSERT INTO 工作正常。

重复条目是我的问题的原因,需要在项目中加以考虑,所以为了解决这个问题,我使用了 ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id),您可以在 @987654321 阅读更多信息@

这是我的最终代码:

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;

【讨论】:

    猜你喜欢
    • 2018-07-18
    • 2012-08-07
    • 2011-01-06
    • 2019-09-15
    • 2014-09-11
    • 1970-01-01
    • 2020-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多