LAST_INSERT_ID 返回最后一个 INSERT 的 AUTOINCREMENT 列的值

在 MySQL 中,LAST_INSERT_ID(), LAST_INSERT_ID(expr) 函数可以用来返回最后一个 INSERT 语句的 AUTOINCREMENT 列的值。

LAST_INSERT_ID 函数在没有参数的情况下,返回一个 BIGINT UNSIGNED(64 位)值,表示作为最近执行的 INSERT 语句的结果为 AUTO_INCREMENT 列成功插入的第一个自动生成的值。如果没有成功插入行,则 LAST_INSERT_ID() 的值保持不变。

使用参数带有参数的 LAST_INSERT_ID 函数,则返回一个无符号整数。

假如您使用一个语句生成 AUTO_INCREMENT 值,然后在多行 INSERT 语句中引用 LAST_INSERT_ID(),该语句将行插入到具有自己的 AUTO_INCREMENT 列的表中。LAST_INSERT_ID() 的值在第二条语句中保持稳定;它对第二行和后面的行的值不受早期行插入的影响。但是,如果您混合对 LAST_INSERT_ID() 和 LAST_INSERT_ID(expr) 的引用,则效果未定义。

如果前面的语句返回错误,则 LAST_INSERT_ID() 的值未定义。对于事务表,如果语句由于错误而回滚,则 LAST_INSERT_ID() 的值未定义。对于手动 ROLLBACK,LAST_INSERT_ID() 的值不会恢复到事务之前的值;它保持在回滚点的状态。

在存储例程(过程或函数)或触发器的主体内,LAST_INSERT_ID() 的值更改方式与在这些类型的对象主体之外执行的语句相同。 以下语句看到的存储例程或触发器对 LAST_INSERT_ID() 值的影响取决于例程的类型:

  • 如果存储过程执行更改 LAST_INSERT_ID() 值的语句,则更改后的值会被过程调用之后的语句看到。

  • 对于更改值的存储函数和触发器,该值会在函数或触发器结束时恢复,因此它们后面的语句不会看到更改的值。

生成的 ID 在每个连接的基础上维护在服务器中。这意味着函数返回给给定客户端的值是为影响该客户端的 AUTO_INCREMENT 列的最新语句生成的第一个 AUTO_INCREMENT 值。此值不受其他客户端的影响,即使它们生成自己的 AUTO_INCREMENT 值。 这种行为确保每个客户端都可以检索自己的 ID,而不用担心其他客户端的活动,也不需要锁或事务。

如果将行的 AUTO_INCREMENT 列设置为非“魔术”值(即非 NULL 且非 0 的值),则不会更改 LAST_INSERT_ID() 的值。

如果使用单个 INSERT 语句插入多行,则 LAST_INSERT_ID() 仅返回为第一个插入行生成的值。这样做的原因是可以轻松地针对其他服务器重现相同的 INSERT 语句。

示例用法

(1)使用 LAST_INSERT_ID 函数返回最后一个 INSERT 语句 AUTO_INCREMENT 列的值,如下:

mysql> insert t_test(name) values('name1'),('name2'),('name3');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|                4 |
+------------------+
1 row in set (0.04 sec)

mysql> select * from t_test;
+----+-------+
| id | name  |
+----+-------+
|  4 | name1 |
|  5 | name2 |
|  6 | name3 |
+----+-------+
3 rows in set (0.04 sec)

由运行结果可知,上面 insert 语句一次性向 t_test 表插入3条记录。id 字段分别为 4、5 和 6,而 LAST_INSERT_ID 函数返回的值为 4。表明当前执行的 INSERT 语句不影响 LAST_INSERT_ID() 的值。

(2)如果您使用 INSERT IGNORE 并忽略该行,则 LAST_INSERT_ID() 与当前值保持不变(如果连接尚未执行成功的 INSERT,则返回 0),并且对于非事务性表,AUTO_INCREMENT 计数器不递增。 对于 InnoDB 表,如果 innodb_autoinc_lock_mode 设置为 1 或 2,则 AUTO_INCREMENT 计数器会递增,如以下示例所示:

mysql> USE test;
mysql> SELECT @@innodb_autoinc_lock_mode;
+----------------------------+
| @@innodb_autoinc_lock_mode |
+----------------------------+
|                          1 |
+----------------------------+

mysql> CREATE TABLE `t` (
       `id` INT(11) NOT NULL AUTO_INCREMENT,
       `val` INT(11) DEFAULT NULL,
       PRIMARY KEY (`id`),
       UNIQUE KEY `i1` (`val`)
       ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

# Insert two rows
mysql> INSERT INTO t (val) VALUES (1),(2);

# With auto_increment_offset=1, the inserted rows
# result in an AUTO_INCREMENT value of 3
mysql> SHOW CREATE TABLE t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `val` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `i1` (`val`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

# LAST_INSERT_ID() returns the first automatically generated
# value that is successfully inserted for the AUTO_INCREMENT column
mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|                1 |
+------------------+

# The attempted insertion of duplicate rows fail but errors are ignored
mysql> INSERT IGNORE INTO t (val) VALUES (1),(2);
Query OK, 0 rows affected (0.00 sec)
Records: 2  Duplicates: 2  Warnings: 0

# With innodb_autoinc_lock_mode=1, the AUTO_INCREMENT counter
# is incremented for the ignored rows
mysql> SHOW CREATE TABLE t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `val` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `i1` (`val`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1

# The LAST_INSERT_ID is unchanged because the previous insert was unsuccessful
mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|                1 |
+------------------+

(3)如果 expr 作为 LAST_INSERT_ID() 的参数给出,则参数的值由函数返回,并作为 LAST_INSERT_ID() 返回的下一个值被记住。这可以用来模拟序列:

a、创建一个表来保存序列计数器并对其进行初始化:

mysql> CREATE TABLE sequence (id INT NOT NULL);
mysql> INSERT INTO sequence VALUES (0);

b、使用该表生成序列号,如下所示:

mysql> UPDATE sequence SET id=LAST_INSERT_ID(id+1);
mysql> SELECT LAST_INSERT_ID();

UPDATE 语句递增序列计数器并导致对 LAST_INSERT_ID() 的下一次调用返回更新的值。 SELECT 语句检索该值。 mysql_insert_id() C API 函数也可用于获取值。 请参阅 mysql_insert_id()。

您可以在不调用 LAST_INSERT_ID() 的情况下生成序列,但是以这种方式使用该函数的效用是 ID 值在服务器中作为最后自动生成的值进行维护。 它是多用户安全的,因为多个客户端可以发出 UPDATE 语句并使用 SELECT 语句(或 mysql_insert_id())获取自己的序列值,而不会影响或被其他生成自己的序列值的客户端影响。

请注意,mysql_insert_id() 仅在 INSERT 和 UPDATE 语句之后更新,因此在执行 SELECT 或 SET 等其他 SQL 语句后,您不能使用 C API 函数检索 LAST_INSERT_ID(expr) 的值。

关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号