last_insert_id() 返回当前会话最近一次成功insert生成的第一个自增id,仅限本连接、不跨会话、不依赖表名;无自增列时返回0;php中应使用mysqli_insert_id()或pdo::lastinsertid()(需关闭模拟预处理),而非select查询。

LAST_INSERT_ID() 返回的是当前会话最后一次 INSERT 生成的自增 ID
它不依赖于表名,也不受其他会话干扰,只返回本连接(session)中最近一次成功插入的 AUTO_INCREMENT 值。哪怕你 INSERT 多行、用 INSERT ... SELECT 或批量插入,它也只返回第一条生成的 ID(不是最大 ID,也不是最后一条的 ID)。
常见错误是以为它能跨会话取值,或者在执行了 UPDATE 或无自增列的 INSERT 后还能拿到旧值——其实只要没触发自增列插入,LAST_INSERT_ID() 就不会更新。
- 必须在同一个数据库连接里调用,不能换连接后查
- 即使 INSERT 失败(比如主键冲突),只要没真正写入,
LAST_INSERT_ID()也不会变 -
INSERT INTO t1 VALUES (), ()插入两行,LAST_INSERT_ID()返回的是第一行的 ID,不是第二行的 - 如果表没有
AUTO_INCREMENT主键,LAST_INSERT_ID()永远返回 0(除非显式调用LAST_INSERT_ID(expr)设置)
在 PHP 中用 mysqli 或 PDO 获取时,别直接 SELECT LAST_INSERT_ID()
很多新手写 $mysqli->query("SELECT LAST_INSERT_ID()"),这多此一举且容易出错——mysqli_insert_id() 和 PDO::lastInsertId() 才是正确封装,它们底层就是读取协议返回的 “insert id” 字段,比发一条额外查询更可靠、更快。
尤其注意:PDO 默认开启模拟预处理(PDO::ATTR_EMULATE_PREPARES = true),此时 lastInsertId() 在批量 INSERT 后可能返回 0;关掉模拟(设为 false)才能拿到真实值。
- mysqli 推荐用
$mysqli->insert_id(属性)或mysqli_insert_id($mysqli)(函数) - PDO 推荐用
$pdo->lastInsertId(),但确保PDO::ATTR_EMULATE_PREPARES为false - 不要用
SELECT LAST_INSERT_ID()—— 它可能被并发 INSERT 干扰(虽然概率低,但没必要冒这个险)
LAST_INSERT_ID(expr) 可以手动设置返回值,但慎用
LAST_INSERT_ID(123) 会把当前会话的“最后插入 ID”设为 123,并返回该值。这不是为了伪造 ID,而是配合某些特殊场景,比如用 REPLACE INTO 或 INSERT ... ON DUPLICATE KEY UPDATE 时,你想让后续逻辑基于某个确定值做判断。
但它会覆盖上一次真正的自增 ID,之后再调用 LAST_INSERT_ID() 就得不到真实插入值了。除非你明确需要“覆盖行为”,否则不要碰这个用法。
- 只在当前会话生效,不影响其他连接
- 设完之后,下次普通 INSERT 的自增 ID 不受影响,只是
LAST_INSERT_ID()返回值变了 - 典型误用:在事务里设了值,又忘了重置,导致后面逻辑取错 ID
批量 INSERT 后想拿到所有生成的 ID?LAST_INSERT_ID() 不行,得换思路
LAST_INSERT_ID() 只返回第一个 ID,哪怕你插入 100 行。MySQL 本身不提供“返回全部自增 ID 列表”的原生方式。如果业务真需要每个新记录的 ID,有三个现实选择:
- 拆成单条 INSERT 循环,每次拿
LAST_INSERT_ID()(性能差,不推荐) - 用临时表 +
INSERT ... SELECT+ 自增 ID 回填(复杂,适合后台批处理) - 应用层自己生成 UUID 或雪花 ID,绕开自增依赖(最常用,尤其微服务场景)
别指望 LAST_INSERT_ID() + 行数 算出其他 ID——中间可能有 DELETE、REPLACE、事务回滚,ID 不连续是常态。











