写点什么

YashanDB 知识库|语句级触发器被触发 N 次?executeBatch 背后还有“坑”

作者:数据库砖家
  • 2025-04-23
    广东
  • 本文字数:1062 字

    阅读完需:约 3 分钟

在使用 JDBC 批量插入 YashanDB 时,有用户反馈触发器行为异常,明明是“语句级”触发器,却执行了 N 次。实际测试显示,在 executeBatch() 的场景下,语句级触发器会被重复触发,导致插入性能大幅下降。

本文将详细解析问题成因、验证方法与规避策略。

一、问题现象

某客户使用 JDBC 接口向表 A 插入数据:

表 A 上有一个语句级触发器(用于执行 alter sequence 等操作);

同时还存在一个行级触发器(用于赋值 sequence.nextval 或 current_timestamp)。

在插入 90 万条数据时,整个过程耗时高达 2.5 小时,性能远低于预期。

二、影响版本

该问题在以下版本均存在:

22.2.14.100 及以前版本

23.2.1.100 及以前版本

三、问题原因分析

经过内部排查,发现 YashanDB 在批量执行 SQL 时存在如下实现逻辑:

JDBC 的 executeBatch() 实际上会循环调用内部的 anlExecuteSingle(),导致语句级触发器被重复触发。

也就是说:

虽然你执行的是一条 batch 语句,数据库却当成多条语句分别执行,语句级触发器自然被重复触发了 N 次。

这显然不符合 SQL 标准行为,在 Oracle 中,语句级触发器只会在整批执行前或执行后触发一次。

四、验证方式

可使用如下方式进行验证:

建表并创建触发器:

drop table trig_test;create table trig_test(t1 number, t2 number);drop table flag;create table flag(t number);create or replace trigger trig_01before insert or update on trig_testbegininsert into flag values(1);end;
复制代码

Java 模拟批量插入代码:

public static void main(String[] args) {try (Connection conn = DBUtil.getConn()) {conn.setAutoCommit(false);PreparedStatement ps = conn.prepareStatement("insert into trig_test(t1) values(?)");for (int i = 0; i < 100; i++) {ps.setInt(1. i);ps.addBatch();}ps.executeBatch();conn.commit();} catch (Exception e) {e.printStackTrace();}}
复制代码

验证触发次数:

select count(*) from flag;
复制代码


你会发现 flag 表里有 100 条记录,也就是语句级触发器被触发了 100 次。

五、风险与影响


六、解决方案与规避建议

推荐方式:避免使用语句级触发器与 executeBatch 搭配

将语句级触发器逻辑迁移至业务代码控制;

或使用普通 insert 循环,牺牲一点性能换行为稳定。

临时规避:切换为 PL/SQL 块执行

使用单条 SQL + 触发一次触发器逻辑,例如:

beginfor i in 1..100 loopinsert into trig_test(t1) values(i);end loop;end;/
复制代码

七、官方解决方案

此问题已被官方定位为 驱动/执行器的设计缺陷,后续版本中将修复触发器触发逻辑,使其更符合标准行为。

八、经验总结



用户头像

还未添加个人签名 2025-04-09 加入

还未添加个人简介

评论

发布
暂无评论
YashanDB 知识库|语句级触发器被触发 N 次?executeBatch 背后还有“坑”_数据库·_数据库砖家_InfoQ写作社区