改变MySQL中TRIGGER中的LAST_INSERT_ID()

Xeo*_*eos 7 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'
Run Code Online (Sandbox Code Playgroud)

我有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;
Run Code Online (Sandbox Code Playgroud)

它完美地工作,除了现在LAST_INSERT_ID()有错误的值(它返回0).很多代码都取决于LAST_INSERT_ID()正确性.但是,自MySQL 5.0.12以来,对TRIGGERS LAST_INSERT_ID内所做的任何更改都不会影响全局值.有没有办法绕过这个?我可以通过调用轻松设置AFTER UPDATE TRIGGER更改,但任何客户端都将设置为0.LAST_INSERT_IDLAST_INSERT_ID(NEW.id_2)LAST_INSERT_ID

是否有任何工作解决方法迫使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
Run Code Online (Sandbox Code Playgroud)

例:

INSERT INTO tbl (id_1, id_2, data) VALUES (1, NULL, 'd');
SELECT LAST_INSERT_ID();
Run Code Online (Sandbox Code Playgroud)

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

正如Ravinder Reddy所说,添加关于系统的简短说明:

我有一个包含篮子的表,我有另一个tbl包含项目的表().该由应用程序创建的,并从分配一个ID AUTO_INCREMENT上筐表.任务是在id = ,into的篮子中插入项目,在该篮子的范围内id_1tbl它们分配一个唯一的ID.每个项目都有一些data与之关联,可能会在同一个篮子中重复.因此,在实践中,我想所有的存储data条目的单个内,然后可以参考(和检索)通过这些个别条目id_1- id_2对.

Rav*_*ddy 2

根据您的表结构描述,很明显它没有可以自动生成值的主键字段。MySQLinformation_schema.tables不保存任何auto_increment值,除非null那些未定义的字段auto_increment

触发问题

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

根据MySQL 关于 LAST_INSERT_ID 的文档

LAST_INSERT_ID()返回一个 BIGINT UNSIGNED(64 位)值,表示 由于最近执行的 INSERT 语句而为AUTO_INCRMENT列 成功插入的
第一个自动生成的值。

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

更改表以设置auto_increment为这些字段之一id_x,然后开始插入值。需要注意的是,在插入期间将值显式传递给auto_increment字段将导致last_insert_id返回一个zero或最近自动生成的值,但不会返回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)
Run Code Online (Sandbox Code Playgroud)

以下语句显示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)
Run Code Online (Sandbox Code Playgroud)

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

mysql> insert into so_q27476005 values( null );
ERROR 1048 (23000): Column 'i' cannot be null
Run Code Online (Sandbox Code Playgroud)

上述语句失败,因为输入已进入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)
Run Code Online (Sandbox Code Playgroud)

由于输入是明确的,并且该字段不属于auto_increment
因此调用last_insert_id结果为0。请注意,如果在同一数据库连接会话中insert对另一个表的任何其他字段进行了另一次调用,则这也可以是其他值。auto_increment

我们来看看表中的记录。

mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

现在,让我们应用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
Run Code Online (Sandbox Code Playgroud)

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

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

您可以交叉检查蒸馏器last_insert_id是否相同。

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                0 |
+------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

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

mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.03 sec)
Run Code Online (Sandbox Code Playgroud)

虽然将 a 传递nullprimary key字段,但它成功了,因为该字段归因于auto_increment
让我们看看生成并插入了哪些值。

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

现在,让我们观察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)
Run Code Online (Sandbox Code Playgroud)

您可以看到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)
Run Code Online (Sandbox Code Playgroud)

现在,让我们观察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)
Run Code Online (Sandbox Code Playgroud)

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