在BEFORE INSERT触发器(Oracle)中使用IF EXISTS(SELECT ...)

时间:2014-03-28 15:34:47

标签: sql oracle select triggers exists

我所拥有的代码不起作用,Oracle告诉我,触发器是在构建错误的情况下创建的。显然,我无法获得有关构建错误的更准确信息......

我之前没有做过很多SQL,所以我对语法并不熟悉。我有预感它是我的IF EXISTS(SELECT ...)THEN声明,Oracle不喜欢,我一直在谷歌搜索类似的例子但我无法找到任何东西在我的情况下工作。

关于代码:

  • "登场"是一个日期属性(它意味着开始)
  • "鳍"是另一个日期属性(它意味着结束)
  • 我想确保NEW行的日期与表格中的任何其他行重叠,如果这两行具有相同的" numInfirmier"属性。
  • 所以我选择所有与NEW行和重叠日期具有相同numInfirmier的行。
  • 如果选择中存在任何内容,我会引发错误。

    CREATE OR REPLACE TRIGGER chev_surv
    BEFORE INSERT OR UPDATE ON surveillance
    FOR EACH ROW
    BEGIN
        IF EXISTS (
            SELECT * FROM surveillance
            WHERE surveillance.numInfirmier = :NEW.numInfirmier
            AND ((surveillance.debut > :NEW.debut AND surveillance.debut < :NEW.fin)
            OR (surveillance.fin > :NEW.debut AND surveillance.fin < :NEW.fin))
        ) THEN
            RAISE_APPLICATION_ERROR(-20001,
            'Il ne doit pas y avoir de chevauchement entre deux périodes surveillance pour un surveillant.');
        END IF;
    END;
    /
    

知道什么是错的吗?

1 个答案:

答案 0 :(得分:7)

首先,如果你正在使用SQL * Plus,当你创建一个对象并被告知有编译错误时,命令show errors会显示错误。

如果您运行show errors,系统会告诉您IF EXISTS语法无效。你可以做点什么

SELECT COUNT(*)
  INTO l_cnt
  FROM <<rest of query>>

IF( l_cnt > 0 )
THEN
  RAISE_APPLICATION_ERROR ...
END IF;

但是,一旦修复了编译错误,您最终会遇到运行时错误。在surveillance上的行级触发器中,您通常无法查询surveillance(如果您所做的只是INSERT VALUES,则可以保证只插入一行)。如果这样做,您将在运行时遇到变异触发错误。

从数据模型的角度来看,当您发现自己设计的表中某个特定行的有效数据依赖于存储在同一个表的其他行中的数据时,您通常会违反规范化原则并且通常会更好服务于修复基础数据模型。

如果你真的决定保留数据模型,我宁愿创建一个在提交时刷新的物化视图,它只包含违反你标准的行的数据。然后,您可以在物化视图上设置约束,在违反条件时在提交时抛出错误。这将需要您桌面上的物化视图日志。

如果您真的想要保留数据模型并且想要使用触发器强制执行逻辑,那么您需要经典的三触发解决方案(如果您使用11.2或更高版本,则需要具有三个部分的复合触发器)。您将创建一个包含主键值集合的包。 before语句触发器将初始化集合。行级触发器将插入已插入和/或更新到此集合中的行的主键。然后一个after语句触发器将遍历此集合并实现您想要的任何检查。然而,这是很多动人的作品,这就是为什么我通常会反对它。

另外,即使您将所有这些部分都运行起来,您的逻辑也无法在多用户环境中保护您。当您有多个用户同时访问系统时,一个用户完全可能插入一行,第二个用户将插入另一个具有重叠范围的行,然后每个会话都将提交。在这种情况下,两组触发器都允许更改,但您仍然会在表格中留下违反要求的数据。物化视图,因为它在提交时而不是在插入时强制执行,将在多用户环境中正常工作。如果您希望触发器在多用户环境中工作,则必须通过添加额外的逻辑来进一步使它们复杂化,这些逻辑强制执行序列化,阻止第二个会话的insert运行,直到第一个会话提交或滚动背部。这增加了复杂性,降低了可扩展性,并且取决于它的实现方式,可能会造成支持噩梦。

相关问题