"批量"删除-sql

这是一个非常好的问题!我来详细解释为什么这样就是"批量删除",以及它是如何做到的。


1. 从最终生成的 SQL 看本质

当传入的 alarmIds 数组是 [1001, 1002, 1003, 1004] 时,MyBatis 会生成一条完整的 SQL 语句:

sql 复制代码
delete from nec_alarm_log where alarm_id in (1001, 1002, 1003, 1004)

关键点 :虽然业务上叫"批量删除",但在数据库层面,这是一条 SQL 语句,一次执行删除了多条记录。


2. <foreach> 标签的工作原理

xml 复制代码
<delete id="deleteNecAlarmLogByAlarmIds" parameterType="String">
    delete from nec_alarm_log where alarm_id in
    <foreach item="alarmId" collection="array" open="(" separator="," close=")">
        #{alarmId}
    </foreach>
</delete>
属性 作用
item alarmId 循环中的每个元素,在循环体内用 #{alarmId} 引用
collection array 要遍历的集合(因为参数是 Long[] 数组,MyBatis 默认把它当作 array
open ( 循环开始时拼接的字符
separator , 每个元素之间的分隔符
close ) 循环结束时拼接的字符

拼接过程

  1. 遍历 alarmIds 数组
  2. 第一个元素:(1001
  3. 后续元素:, 1002, 1003, 1004
  4. 最后补上:)
  5. 完整拼接成 (1001, 1002, 1003, 1004)

3. 为什么这算"批量"删除?

"批量"的含义有两种理解:

理解一:多次数据库交互(真正的批量)

java 复制代码
// 循环执行多条 delete 语句(不推荐)
for (Long id : alarmIds) {
    mapper.deleteById(id); // 每次一条 SQL
}

→ 4 条记录 = 4 次网络往返 + 4 次 SQL 解析执行

理解二:一次数据库交互删除多条(你的实现)

sql 复制代码
delete from nec_alarm_log where alarm_id in (1,2,3,4)

→ 4 条记录 = 1 次网络往返 + 1 次 SQL 解析执行

你的实现属于"批量删除"的第二种理解,虽然只有一条 SQL,但效果等同于一次删除多条,性能远优于循环删除。


4. 什么是真正的"批量删除"(Batch)?

如果使用 JDBC 的 PreparedStatement.addBatch(),才是真正的批量操作:

java 复制代码
// 使用 BATCH 执行器
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    NecAlarmLogMapper mapper = sqlSession.getMapper(NecAlarmLogMapper.class);
    for (Long id : alarmIds) {
        mapper.deleteById(id); // 每个 delete 被 addBatch() 缓存
    }
    sqlSession.commit(); // 一次性发送所有 delete 语句
}

这会生成多条独立的 delete 语句,但一次网络请求发送到数据库,减少了网络开销。


5. 两种"批量"方式的对比

维度 IN 方式(你的实现) addBatch() 方式
SQL 条数 1 条 N 条
网络往返 1 次 1 次(但 SQL 更长)
数据库解析 1 次 N 次
事务 单条 SQL,自动原子 多条 SQL,需要事务保证原子性
适用场景 删除/查询/更新 插入/更新(每条数据不同)
性能 非常高(尤其数据量大时) 较高
SQL 长度限制 max_allowed_packet 限制 无限制(分批发送)

6. 你的代码注意事项

参数类型写成 parameterType="String"

xml 复制代码
<delete id="deleteNecAlarmLogByAlarmIds" parameterType="String">

这里写 String 是错误的,因为实际传入的是 Long[]。虽然 MyBatis 不强制校验这个类型,但最好修正:

xml 复制代码
<delete id="deleteNecAlarmLogByAlarmIds" parameterType="java.lang.Long[]">
    <!-- 或者直接省略 parameterType -->
</delete>

collection 的取值规则

参数类型 collection
数组 (如 Long[]) array
List (如 List<Long>) list
Map map 中的 key
使用 @Param 注解 注解的值

如果你在 Mapper 方法中加了 @Param

java 复制代码
int deleteNecAlarmLogByAlarmIds(@Param("ids") Long[] alarmIds);

collection 应该改为 ids


7. 适用场景限制

IN 方式虽然高效,但有两个限制:

  1. SQL 长度限制 :MySQL 默认 max_allowed_packet=4M,如果 alarmIds 有几十万个,生成的 SQL 会超长。
  2. IN 子句性能 :当 IN 列表非常大(如数万)时,数据库优化器可能不会走索引,性能下降。

解决方案 :大数据量时,可以分批使用 IN

java 复制代码
// 每 1000 条一批
List<Long> ids = Arrays.asList(alarmIds);
int batchSize = 1000;
for (int i = 0; i < ids.size(); i += batchSize) {
    List<Long> subList = ids.subList(i, Math.min(i + batchSize, ids.size()));
    mapper.deleteNecAlarmLogByAlarmIds(subList.toArray(new Long[0]));
}

8. 总结

你的代码为什么是批量删除?

  • 因为 foreach 标签把多个 alarmId 拼接到 IN 子句中
  • 最终生成一条 delete ... in (id1, id2, id3, ...) 的 SQL
  • 一条 SQL 删除了多条记录,从业务角度看就是"批量删除"
  • 相比循环删除,性能提升巨大(减少 N-1 次网络交互)

本质上 :这不是 JDBC 的批量操作,而是 SQL 层面的批量操作,是 MyBatis 动态 SQL 的经典用法之一。😊

相关推荐
瀚高PG实验室2 小时前
SQL优化案例:使用分区表优化SQL查询性能
linux·服务器·数据库·sql·microsoft·postgresql
SelectDB2 小时前
15 分钟搭建 PostgreSQL + Apache Iceberg + Apache Doris 的湖仓分析平台
数据库
SelectDB2 小时前
科大讯飞可观测性底座:从 ES/Loki 到 Apache Doris 的可观测存储分析升级实践
数据库
SelectDB2 小时前
Cisco WebEx 数据平台统一改造:基于 Apache Doris / SelectDB 替换 Trino、Pinot、Iceberg 及 Kyuubi
数据库
数据技术说2 小时前
MySQL 和 PostgreSQL 的 CDC 方案到底有什么区别?
数据库·架构
SelectDB2 小时前
灵犀科技统一数据服务平台:基于 Apache Doris / SelectDB 实现存储降本 60%、计算提效 10 倍
数据库
让学习成为一种生活方式3 小时前
KMC 3.2.4 安装与使用--生信工具108
数据库
今天AI了吗3 小时前
从聊天到委派:AI Agent 如何推进长期任务
数据库·人工智能·python·sql·rust
—Miss. Z—3 小时前
第11章 故障管理
网络·数据库·oracle