mysql - 从 MySQL 中的 TRIGGER 中更改 LAST_INSERT_ID()

标签 mysql triggers innodb auto-increment last-insert-id

我有一个 BEFORE INSERT TRIGGER用于计算 AUTO_INCREMENT列的值 ( id_2 )。

id_1 | id_2 | data
1    | 1    | 'a'
1    | 2    | 'b'
1    | 3    | 'c'
2    | 1    | 'a'
2    | 2    | 'b'
2    | 3    | 'c'
2    | 4    | 'a'
3    | 1    | 'b'
3    | 2    | 'c'

我有 PRIMARY(id_1, id_2) 并且我正在使用 InnoDB。之前,该表使用的是 MyISAM,我没有遇到任何问题:id_2被设置为 AUTO_INCREMENT ,因此 id_1 的每个新条目会生成新的 id_2在其自己的。现在,在切换到 InnoDB 之后,我有这个触发器来做同样的事情:

SET @id = NULL;
SELECT COALESCE(MAX(id_2) + 1, 1) INTO @id FROM tbl WHERE id_1 = NEW.id_1;
SET NEW.id_2= @id;

它工作得很好,除了现在 LAST_INSERT_ID()有错误的值(它返回 0)。很多代码依赖于 LAST_INSERT_ID()是正确的。然而,自 MySQL 5.0.12 以来,对 LAST_INSERT_ID 所做的任何更改在 TRIGGERS 内不影响全局值。有什么办法可以绕过这个吗?我可以轻松设置 AFTER UPDATE TRIGGER这改变了 LAST_INSERT_ID调用LAST_INSERT_ID(NEW.id_2) ,但是任何客户端都会得到 LAST_INSERT_ID设置为 0。

是否有任何可行的解决方法来强制 MySQL 维持 LAST_INSERT_ID 的状态?触发器内部发生了哪些变化?除了切换回支持开箱即用的 MyISAM 或运行另一个 SELECT max(id_2) FROM tbl WHERE id_1 = :id 之外,还有其他选择吗?作为事务的一部分以确保找到的行将是之前插入的行?

> SHOW CREATE TABLE tbl;

CREATE TABLE `tbl` (
   `id_1` int(11) NOT NULL,
   `id_2` int(11) NOT NULL,
   `data` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
   PRIMARY KEY (`id_1`,`id_2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

示例:

INSERT INTO tbl (id_1, id_2, data) VALUES (1, NULL, 'd');
SELECT LAST_INSERT_ID();

第一个语句将插入行 1 | 4 | 'd'到表中。第二条语句将返回 0 ,但我需要它返回 4 .

Ravinder Reddy 询问, 添加有关系统的简短说明:

我有一个包含篮子的表,还有另一个包含项目的表(tbl)。 basket 由应用程序创建并分配了一个来自 AUTO_INCREMENT 的 ID在篮子的 table 上。任务是将 items 插入 id = id_1 的篮子中, 进入 tbl ,在该篮子的范围内为他们分配一个唯一的 ID。每个 item 都有一些 data与之关联,可能会在同一个篮子中重复。所以在实践中,我试图存储所有 data单个篮子中的条目,然后能够通过其 id_1 引用(和检索)这些单独的条目- id_2对。

最佳答案

根据你的表结构描述,很明显它没有可以自动生成值的主键字段。 MySQL 的 information_schema.tables 不保存 auto_increment 值,但对于那些未定义 auto_increment 的字段,null

触发问题:

您的触发器主体中使用的代码块似乎取决于 id 字段的显式计算和输入。它没有使用 auto_increment 字段的默认行为。

根据 MySQL's documentation on LAST_INSERT_ID :

LAST_INSERT_ID() returns a BIGINT UNSIGNED (64-bit) value
representing the first automatically generated value
successfully inserted for an AUTO_INCREMENT column
as a result of the most recently executed INSERT statement.

很明显,它仅适用于auto_increment 字段。
id_1id_2 字段都没有属性 auto_increment
由于这个原因,尽管您在插入时将 null 作为这些字段的输入传递,但不会自动生成任何值并将其分配给它们。

更改您的表以将 auto_increment 设置为那些 id_x 字段之一,然后开始插入值。一个警告是,在插入期间将值显式传递给 auto_increment 字段将导致 last_insert_id 返回一个 或最近自动生成的值,但不会NEW.id。在插入期间传递 null 或不选择 auto_increment 字段将触发为该字段生成新值,last_insert_id 可以选择并返回它。

以下示例演示了上述行为:

mysql> drop table if exists so_q27476005;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> create table so_q27476005( i int primary key );
Query OK, 0 rows affected (0.33 sec)

以下语句显示字段的下一个适用 auto_increment 值。

mysql> select auto_increment
    ->   from information_schema.tables
    ->  where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
|           NULL |
+----------------+
1 row in set (0.00 sec)

让我们尝试在该字段中插入一个 null 值。

mysql> insert into so_q27476005 values( null );
ERROR 1048 (23000): Column 'i' cannot be null

上述语句失败,因为输入到 not null primary key 字段但未归因于 auto_increment。仅对于 auto_increment 字段,您可以传递 null 输入。

现在让我们看看 last_insert_id 的行为:

mysql> insert into so_q27476005 values( 1 );
Query OK, 1 row affected (0.04 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                0 |
+------------------+
1 row in set (0.00 sec)

由于输入是显式的,并且该字段未归因于auto_increment
调用 last_insert_id 结果为 0。请注意,这也可以是其他值, 如果对另一个表的任何其他 auto_increment 字段有另一个 insert 调用, 在同一个数据库连接 session 中。

让我们看看表中的记录。

mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

现在,让我们将 auto_increment 应用于字段 i

mysql> alter table so_q27476005 change column i i int auto_increment;
Query OK, 1 row affected (0.66 sec)
Records: 1  Duplicates: 0  Warnings: 0

以下语句显示字段 i 的下一个适用 auto_increment 值。

mysql> select auto_increment
    ->   from information_schema.tables
    ->  where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
|              2 |
+----------------+
1 row in set (0.00 sec)

您可以交叉检查 last_insert_id 是否仍然相同。

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                0 |
+------------------+
1 row in set (0.00 sec)

让我们在字段 i 中插入一个 null 值。

mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.03 sec)

它成功地将一个null传递给一个primary key字段, 因为该字段属于 auto_increment
让我们看看生成并插入了哪个值。

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

i 字段的下一个适用auto_increment 值是:

mysql> select auto_increment
    ->   from information_schema.tables
    ->  where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
|              3 |
+----------------+
1 row in set (0.00 sec)

mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
| 2 |
+---+
2 rows in set (0.00 sec)

现在,让我们观察一下当为字段提供显式输入时 last_insert_id 的结果。

mysql> insert into so_q27476005 values( 3 );
Query OK, 1 row affected (0.07 sec)

mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)


mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

可以看到last_insert_id由于显式输入没有捕获到值。
但是,信息模式确实注册了下一个适用的值。

mysql> select auto_increment
    ->   from information_schema.tables
    ->  where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
|              4 |
+----------------+
1 row in set (0.08 sec)

现在,让我们观察当字段输入是自动/隐式时 last_insert_id 的结果。

mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.10 sec)

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                4 |
+------------------+
1 row in set (0.00 sec)

希望这些细节对您有所帮助。

关于mysql - 从 MySQL 中的 TRIGGER 中更改 LAST_INSERT_ID(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27476005/

相关文章:

MySQL:选择查询执行和结果获取时间随着连接数的增加而增加

MYSQL查询仅当引擎不是innodb时才更改表引擎

mysql - 删除 0 次浏览的 WordPress 帖子 [wp_postviews 插件]

php - 如何将json值数据库转换成html

Mysql - 选择查询后有条件插入

c# - 触发内容模板

php 循环 mysqli_fetch_array - 它可以像普通循环一样灵活吗?

mysql - 如何使用 mysql 触发器划分到另一列?

jquery - 如果通过 JQuery 创建 html,则鼠标触发无法正确发生

mysql - myISAM 和 InnoDB 表的外键