我使用UPDATING(col_name)检查触发器内是否更新了列的值。但是最大的问题是这个命令不会检查:old和:new对象的值。如果col_name存在于 set 部分的查询中,即使使用旧值,UPDATING(col_name)也为true。
我不想分别检查每个列的old.col1<>:new.col1。
如何正确检查更改列值?
我想以通用的方式做到这一点。喜欢:
SELECT col_name bulk collect INTO included_columns FROM trigger_columns where tbl_name ='name';
l_idx := included_columns.first;
while (l_idx is not null)
loop
IF UPDATING(included_columns(l_idx)) THEN
//DO STH
return;
END IF;
l_idx := included_columns.next(l_idx);
end loop;
由于
答案 0 :(得分:2)
你在评论中说:
“我希望以通用方式执行此操作并将其管理得更安全。在表格中放置重要的列,并且不要在触发器中放置许多IF。”
我怀疑那是你想要的。实现这一目标的唯一方法是使用动态SQL来组装和执行PL / SQL块。这是一个复杂的解决方案,没有实质性的好处。
我害怕我在那里使用“更安全”。触发器已经非常糟糕:它们使得更难以推断出数据库中发生的事情并且可能导致无法预料的可伸缩性问题。不要通过在混合中注入动态SQL来使它们变得更糟。动态SQL很难,因为它将编译错误转换为运行时错误。
您对触发器中的列名和IF语句的硬编码有何异议?它更安全,因为触发器已编译。验证触发器逻辑更容易,因为代码就在那里。
如果这只是不想输入,那么您可以从数据字典视图(例如all_tab_cols
)甚至您自己的元数据表生成触发源(如果必须)(即trigger_columns
)。
答案 1 :(得分:0)
您可以定义类似于以下内容的全局函数:
CREATE OR REPLACE FUNCTION NUMBER_HAS_CHANGED(pinVal_1 IN NUMBER,
pinVal_2 IN NUMBER)
RETURN CHAR
IS
BEGIN
IF (pinVal_1 IS NULL AND pinVal_2 IS NOT NULL) OR
(pinVal_1 IS NOT NULL AND pinVal_2 IS NULL) OR
pinVal_1 <> pinVal_2
THEN
RETURN 'Y';
ELSE
RETURN 'N';
END IF;
END NUMBER_HAS_CHANGED;
现在在您的触发器中,您只需编写
IF NUMBER_HAS_CHANGED(:OLD.COL1, :NEW.COL1) = 'Y' THEN
-- whatever
END IF;
请注意,此函数定义为返回CHAR,因此如果需要,也可以从SQL语句调用它 - 例如,在CASE表达式中。请记住,在Oracle中,数据库中没有BOOLEAN类型 - 仅在PL / SQL中。
你可能想要创建这个函数的其他版本来处理VARCHAR2和DATE值,但是因为这是替换数据类型和更改函数名称的问题,我会让你拥有写它们的乐趣。 : - )
祝你好运。