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

Mad*_*leC 4 sql oracle select triggers exists

我的代码不起作用,Oracle告诉我触发器已经创建了构建错误.显然我无法获得有关构建错误的更准确信息......

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

所以关于代码:

  • "debut"是一个日期属性(意思是开始)
  • "fin"是另一个日期属性(意味着结束)
  • 如果这两行具有相同的"numInfirmier"属性,我想确保NEW行的日期与表中的任何其他行不重叠.
  • 因此,我选择与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;
    /
    
    Run Code Online (Sandbox Code Playgroud)

知道什么是错的吗?

Jus*_*ave 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;
Run Code Online (Sandbox Code Playgroud)

但是,一旦修复了编译错误,就会出现运行时错误.在行级触发器打开时surveillance,您通常无法查询surveillance(如果您所做的INSERT VALUES只是插入单行,则可以执行此操作).如果这样做,您将在运行时遇到变异触发错误.

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

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

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

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