【问题标题】:Using IF EXISTS (SELECT ...) in a BEFORE INSERT trigger (Oracle)在 BEFORE INSERT 触发器 (Oracle) 中使用 IF EXISTS (SELECT ...)
【发布时间】:2014-05-08 04:03:21
【问题描述】:

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

我之前真的没有做过很多 SQL,所以我对语法不是很熟悉。我有一种预感,Oracle 不喜欢我的 IF EXISTS (SELECT ...) THEN 语句,我一直在谷歌搜索类似的例子,但我真的找不到任何适合我的情况。

关于代码:

  • “debut”是一个日期属性(表示开始)
  • “fin”是另一个日期属性(表示结束)
  • 如果这两行具有相同的“numInfirmier”属性,我想确保新行的日期不与表中的任何其他行重叠。
  • 所以我选择所有与新行具有相同 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;
    /
    

知道有什么问题吗?

【问题讨论】:

  • 如果您的客户支持,您可以通过show errorsselect * from user_errors where type = 'TRIGGER' and name = 'CHEV_SURV' 获得有关实际错误的更准确信息。这适用于任何存储的 PL/SQL。你是对的,你不能在select 条件之外使用exists。但即使你让它编译,你也会得到一个变异表错误——你不能查询触发器所针对的表。
  • 天哪,知道这是件好事XD
  • "show error" 说我只能在 SQL 指令中使用 "EXISTS".... wut?
  • 您正试图在 PL/SQL 上下文中使用它;它是一种不同的(尽管经常是重叠的)语言。你可以only use it in a subquery,点赞select ... from ... where exists (select ... from ...)。贾斯汀的回答向您展示了在这种情况下您应该如何检查,以及为什么它仍然无法工作 *8-)

标签: sql oracle select triggers exists


【解决方案1】:

首先,如果您使用的是 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 运行,直到第一个会话提交或回滚。这增加了复杂性,降低了可扩展性,并且取决于它的实现方式,可能会成为支持方面的噩梦。

【讨论】:

  • "从数据模型的角度来看,当您发现自己设计的表中特定行的有效数据依赖于存储在同一表的其他行中的数据时,您通常违反了规范化原则并且您通常会更好地修复底层数据模型。”我完全同意。但是,不幸的是,这是针对大学作业的,其中强加了数据模型,并且还强加了使用触发器解决问题......我正在攻读软件工程学士学位,我们自己的老师无法做出像样的数据模型。讲一个很好的例子:P
  • 真的没有办法用一个触发器来完成这个吗?出于某种原因,我真的怀疑老师会期望我们提出一个 3 触发解决方案...
  • @MademoiselleC - 没有一个触发器,没有。您可以创建一个仅在 insert 而不是 update 上触发的触发器,并将您的应用程序限制为仅执行 insert ... values 操作(insert ... select 仍会引发变异触发器错误),然后只需要 1 个触发器。这不是您曾经考虑过对真实系统施加的限制,但也许这就是您的讲师正在寻找的。​​span>
  • 我们得到的情况只是学术性的(即绝对不应该符合实际系统的目的),所以我认为这将是一个可以接受的解决方案。最重要的是,这是我可以很快完成的。感谢您的帮助!
猜你喜欢
  • 2011-01-17
  • 2021-11-23
  • 2012-06-05
  • 1970-01-01
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 2012-10-12
  • 2013-03-29
相关资源
最近更新 更多