跳到主要内容

LAST_INSERT_ID 行为说明

本文介绍 seekdb 中 LAST_INSERT_ID() 函数的行为,以及与 MySQL 的差异。

LAST_INSERT_ID() 函数用于获取最近一次 INSERT 操作生成的自增 ID 值。在 seekdb 中,该函数的行为与 MySQL 存在一些差异,主要体现在以下两个方面:

  • 会话级别的 LAST_INSERT_ID:通过 LAST_INSERT_ID() 函数读取和修改
  • 协议级别的 LAST_INSERT_ID:MySQL 协议 OK 包中返回的值

MySQL 行为

会话级别的 LAST_INSERT_ID

会话上的 LAST_INSERT_ID 通过 LAST_INSERT_ID(args) 函数读取和修改:

-- 读取会话上的 LAST_INSERT_ID
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 0 |
+------------------+

-- 通过函数修改会话上的 LAST_INSERT_ID
SELECT LAST_INSERT_ID(10);
+--------------------+
| LAST_INSERT_ID(10) |
+--------------------+
| 10 |
+--------------------+

协议级别的 LAST_INSERT_ID

MySQL 协议的 OK 包中的 LAST_INSERT_ID。当 DML 语句会修改会话上的 LAST_INSERT_ID 时,那么 DML 执行之后客户端回的 OK 包中的 LAST_INSERT_ID 会和会话上的 LAST_INSERT_ID 等价,否则回给客户端的 LAST_INSERT_ID 会是 DML 算子写入的最后一行数据对应的 AUTO_INCREMENT 列的值。

seekdb 与 MySQL 的兼容性

完全兼容的行为

seekdb 在以下方面与 MySQL 完全兼容:

  • 会话级别的 LAST_INSERT_ID 读取和修改:通过 LAST_INSERT_ID() 函数读取和修改会话上的值
  • 通过函数表达式设置 LAST_INSERT_ID:使用 LAST_INSERT_ID(expr) 函数设置值
  • 协议级别的 OK 包格式:在给客户端的回包格式上与 MySQL 保持一致
  • REPLACE 语句:返回第一行 INSERT 或覆盖写入的自增列的值
  • INSERT ... ON DUPLICATE KEY UPDATE:全部无冲突时返回第一行 INSERT 的自增列的值,部分冲突时返回第一行 INSERT 的自增列的值,全部冲突时维持不变
  • 多行插入:返回第一行 INSERT 的自增列的值
  • IGNORE 语句:主键冲突导致数据没写入时,LAST_INSERT_ID 不会被修改
信息

会话上 seekdb 和 MySQL 一样也保存了 LAST_INSERT_ID,通过表达式获取和修改会话上的 LAST_INSERT_ID,seekdb 的行为和 MySQL 完全一致。

行为差异

seekdb 与 MySQL 的主要差异体现在以下方面:

提示

seekdb 目前在给客户端的回包格式上和 MySQL 保持一致,即 OK 包中存在 LAST_INSERT_ID 信息,但是该信息目前表现上和 MySQL 不完全一致。通过 DML 语句在写入 AUTO_INCREMENT 列时,LAST_INSERT_ID 的值变化和 MySQL 不完全兼容。

INSERT 语句中手动指定自增列

MySQL 行为:

  • 手动指定自增列:维持 LAST_INSERT_ID 不变

seekdb 行为:

  • 手动指定自增列:返回第一行 INSERT 的自增列的值
提示

这是 seekdb 与 MySQL 在 LAST_INSERT_ID 行为上的主要差异。seekdb 在实现上区分不出当前自增列的值是被手动指定的还是自增列服务获取的递增值,所以在 LAST_INSERT_ID 的表现上都保持了一致。

-- 创建测试表
CREATE TABLE `t1` (
`c1` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`c2` INT DEFAULT NULL,
PRIMARY KEY (`c1`),
UNIQUE KEY `c2` (`c2`)
);

-- 示例 1:通过 LAST_INSERT_ID 函数修改值(MySQL 和 seekdb 行为一致)
UPDATE t2 SET c1 = LAST_INSERT_ID(100) + c1 WHERE c2 = 1;
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 100 |
+------------------+

-- 示例 2:INSERT 语句(MySQL 和 seekdb 行为一致)
INSERT INTO t1(c2) VALUES (1);
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 1 |
+------------------+

-- 示例 3:REPLACE 语句(MySQL 和 seekdb 行为一致)
REPLACE INTO t1(c2) VALUES (2);
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+

-- 示例 4:多行插入(MySQL 和 seekdb 都返回第一行的 ID)
INSERT INTO t1(c2) VALUES (3), (4);
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 3 |
+------------------+

详细对比表

DML 语句类型场景MySQL 会话上的 LAST_INSERT_IDseekdb 会话上的 LAST_INSERT_ID差异说明
INSERT主键冲突维持不变维持不变一致
主键不冲突(不指定自增列)第一行 INSERT 的自增列的值第一行 INSERT 的自增列的值一致
主键不冲突(手动指定自增列)维持不变第一行 INSERT 的自增列的值seekdb 会更新值
REPLACE第一行 INSERT 或覆盖写入的自增列的值第一行 INSERT 或覆盖写入的自增列的值一致
INSERT ... ON DUPLICATE KEY全部无冲突第一行 INSERT 的自增列的值第一行 INSERT 的自增列的值一致
部分冲突第一行 INSERT 的自增列的值第一行 INSERT 的自增列的值一致
全部冲突维持不变维持不变一致