Skip to content

SQL Server 批量操作参数过载处理 ​

💡 问题背景

在进行大数据量的批量插入或更新操作时,如果使用 MyBatis 的 <foreach> 标签拼接 SQL,可能会遇到 SQL Server 的参数数量限制异常。

🔴 异常现象

执行 SQL 时抛出以下异常:

text
com.microsoft.sqlserver.jdbc.SQLServerException: 传入的请求具有过多的参数。该服务器支持最多 2100 个参数。请减少参数的数目,然后重新发送该请求。

🔍 原因分析

数据库对单条语句的限制如下:

  • SQL Server:对语句的条数和参数的数量都有限制,分别是 1000 条 和 2100 个参数。
  • MySQL:对语句的长度有限制,默认是 4M。
  • MyBatis:动态语句本身没有数量上的限制,但最终生成的 SQL 受限于数据库驱动。

当批量插入的对象字段较多时,对象数 * 字段数 极易超过 2100 个参数的限制。

🛠️ 解决方案

将原本的一句 SQL 执行 N 次插入,拆分为多条 SQL 执行,或者通过代码逻辑控制批次大小。

1. 场景一:MyBatis 批量插入(报错示例) ​

使用 <foreach> 拼接长 SQL 容易触发限制:

xml
<!-- 批量插入问题件 -->
<sql id="sqlInsertQuestions">
    <foreach collection="questionList" item="question">
        insert into cmd_Question (
            qu_clm_id, qu_data_id, qu_code, qu_des, qu_fieldcode, qu_data_type
        ) values (
            #{clm_id}, #{question.qu_data_id}, #{question.qu_code}, #{question.qu_des}, #{question.qu_fieldcode}, #{question.qu_data_type}
        )
    </foreach>
</sql>

2. 场景二:代码循环处理(推荐方案) ​

将循环逻辑移至代码层,利用 ExecutorType.BATCH 模式进行提交:

java
// 在 Service 或 Mapper 调用处进行循环
for (Question question : claim.getQuestionList()) {
    mapper.insertQuestion(question);
}

对应的 Mapper XML 改为单条插入:

xml
<insert id="insertQuestion" parameterType="question">
    insert into cmd_Question (
        qu_clm_id, qu_data_id, qu_code, qu_des, qu_fieldcode, qu_data_type
    ) values (
        #{qu_clm_id}, #{qu_data_id}, #{qu_code}, #{qu_des}, #{qu_fieldcode}, #{qu_data_type}
    )
</insert>

✅ 优化建议

  • 开启批处理模式:如果使用 MyBatis,建议配置 SqlSession 为 ExecutorType.BATCH。
  • 分片处理:如果必须使用 <foreach>,请在 Java 代码中将大 List 拆分为每 100 或 200 个一组的小 List 分批调用。

🔗 参考资料

方舟 · Ark