SQL Server 2019 不支持 INSERT ... RETURNING,唯一可靠方案是使用 OUTPUT 子句(如 OUTPUT INSERTED.id);MyBatis 的 useGeneratedKeys 对批量插入无效,需改用带 OUTPUT 的手写 SQL 或存储过程封装。

SQL Server 2019 原生不支持 INSERT ... RETURNING
SQL Server 2019(及所有已发布版本)都不支持 INSERT ... RETURNING 语法。如果你直接写 INSERT INTO users (...) VALUES (...) RETURNING id,会报错:syntax error at or near "RETURNING"。这不是驱动或配置问题,是数据库引擎层面不支持——PostgreSQL 和 SQLite 3.35+ 才有这个特性。
OUTPUT INSERTED.* 是批量返回主键的唯一可靠原生方案
SQL Server 提供 OUTPUT 子句,必须和 INSERT 同一条语句执行,不能拆成两步。它能真正实现“插入一批、返回一批”,每行对应一个生成的 id(或任意列)。
-
OUTPUT INSERTED.id返回新记录的主键值(假设主键是id) -
OUTPUT INSERTED.*返回整行,但注意:如果表有计算列、默认约束或触发器,行为可能受干扰 - 必须显式指定列名,不能用
INSERTED.*在某些上下文中(如嵌套子查询)引发解析错误,建议写全字段或只取关键列 - 不能用于
INSERT ... SELECT以外的批量方式(比如BULK INSERT不支持OUTPUT)
示例:
INSERT INTO users (name, email)
OUTPUT INSERTED.id, INSERTED.created_at
VALUES ('Alice', 'a@example.com'), ('Bob', 'b@example.com');MyBatis 中 useGeneratedKeys=true 对批量插入无效
MyBatis 的 useGeneratedKeys="true" 底层依赖 JDBC 的 Statement.getGeneratedKeys()。SQL Server 的 JDBC 驱动(如 mssql-jdbc)在批量执行时,getGeneratedKeys() 只返回第一个插入行的 ID —— 这不是 bug,是 SQL Server 协议限制,MyBatis 无法绕过。
- 即使你用了
<foreach></foreach>拼出多值VALUES (...), (...),useGeneratedKeys仍只填第一个对象的id -
keyProperty和keyColumn配置无济于事,因为驱动根本没提供其余 ID - 想靠
<selectkey order="AFTER">SELECT @@IDENTITY</selectkey>更危险:它只返回最后一条插入的 ID,批量时完全不可用
可行路径只有两条:改 SQL 或换方案
真要批量插入并拿到全部主键,只有两个落地选择:
-
手写带 OUTPUT 的 SQL + 自定义 resultMap:把
INSERT ... OUTPUT当作普通查询来执行,用<select></select>标签封装,返回List<user></user>;缺点是丧失 MyBatis 自动生成 SQL 的便利性 -
用存储过程封装 OUTPUT 逻辑:创建一个接受表值参数(TVP)的 SP,在内部做
INSERT ... OUTPUT,再把结果集 RETURN;调用时用@Select("EXEC insert_users_with_output @users = ?")配合resultMap - 别碰
BULK INSERT:它快但不支持OUTPUT,也无法回填 Java 对象的id字段
最常被忽略的一点:SQL Server 的 OUTPUT 只在当前语句作用域内有效,没法跨事务、跨批获取;如果你用分批提交(比如每 1000 条一个事务),每批都得单独执行一次 INSERT ... OUTPUT 并处理结果集,不能攒到最后统一查。

















