触发器编译成功但插入时触发ORA-00036递归SQL层级超限错误求助
问题原因分析
这个ORA-00036错误的根源非常直接:你的触发器TR1是BEFORE INSERT OR UPDATE ON DOCKING的行级触发器,而在触发器的逻辑里,当满足car <= cap的条件时,你又执行了一次INSERT INTO DOCKING操作。
每次执行这个INSERT语句,都会再次触发TR1本身,形成了无限递归调用:
- 初始的
INSERT INTO DOCKING触发TR1 - TR1里的INSERT又触发TR1
- 这个循环不断重复,直到达到Oracle默认的递归SQL层数上限(50层),抛出错误
另外还有两个小问题需要注意:
- 触发器的
WHEN条件里TO_DATE(NEW.ARRIVAL_DATE)是多余的,因为ARRIVAL_DATE本身就是DATE类型,不需要再次转换 - 你误用了BEFORE触发器的作用:BEFORE触发器的核心是修改
:NEW字段的值,Oracle会自动把修改后的:NEW数据插入到表中,手动插入完全是画蛇添足
解决方法
我们需要重构触发器的逻辑,去掉不必要的INSERT操作,改成验证拦截逻辑(如果不满足停靠条件就抛出错误阻止插入),这才是你这个触发器应该实现的核心需求——只允许船的载货量小于等于码头容量时,才允许插入DOCKING记录。
修改后的触发器代码如下:
CREATE OR REPLACE TRIGGER TR1 BEFORE INSERT OR UPDATE ON DOCKING FOR EACH ROW WHEN (NEW.ARRIVAL_DATE <= NEW.DEPARTURE_DATE) -- 移除多余的TO_DATE转换 DECLARE cap PIERS.CAPACITY%TYPE; car SHIPS.CARGO_WIEGHT%TYPE; BEGIN -- 获取对应码头的容量 SELECT CAPACITY INTO cap FROM PIERS WHERE PIERS.PID = :NEW.PID; -- 获取对应船只的载货量 SELECT CARGO_WIEGHT INTO car FROM SHIPS WHERE SHIPS.SID = :NEW.SID; -- 如果载货量超过码头容量,抛出自定义错误阻止操作 IF car > cap THEN RAISE_APPLICATION_ERROR(-20001, '船只载货量超过码头容量,无法停靠'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- 把静默失败改成抛出错误,方便排查数据问题 RAISE_APPLICATION_ERROR(-20002, '对应的码头或船只不存在'); END; /
关键修改点说明:
- 移除了触发器内部的
INSERT INTO DOCKING语句,BEFORE触发器不需要手动插入数据,Oracle会自动处理:NEW行的插入/更新 - 把原来的正向判断改成反向拦截逻辑:如果载货量超过容量,就抛出自定义错误,直接阻止非法操作
- 优化了
WHEN条件,去掉多余的类型转换 - 改进了异常处理,把原来的静默忽略改成抛出错误,避免隐藏数据问题
现在执行你的插入语句:
INSERT INTO DOCKING VALUES(88, 5, '15-AUG-17', '15-AUG-17');
因为S8的载货量是50000,E码头的容量是60000,满足停靠条件,所以会正常插入,不会再触发递归错误。
内容的提问来源于stack exchange,提问作者Mor Goren
相关产品推荐
相关产品推荐

