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_ID | seekdb 会话上的 LAST_INSERT_ID | 差异说明 |
|---|---|---|---|---|
| INSERT | 主键冲突 | 维持不变 | 维持不变 | 一致 |
| 主键不冲突(不指定自增列) | 第一行 INSERT 的自增列的值 | 第一行 INSERT 的自增列的值 | 一致 | |
| 主键不冲突(手动指定自增列) | 维持不变 | 第一行 INSERT 的自增列的值 | seekdb 会更新值 | |
| REPLACE | 第一行 INSERT 或覆盖写入的自增列的值 | 第一行 INSERT 或覆盖写入的自增列的值 | 一致 | |
| INSERT ... ON DUPLICATE KEY | 全部无冲突 | 第一行 INSERT 的自增列的值 | 第一行 INSERT 的自增列的值 | 一致 |
| 部分冲突 | 第一行 INSERT 的自增列的值 | 第一行 INSERT 的自增列的值 | 一致 | |
| 全部冲突 | 维持不变 | 维持不变 | 一致 |