如何编写唯一约束作为触发器?

Posted

技术标签:

【中文标题】如何编写唯一约束作为触发器?【英文标题】:How do I write a unique constraint as a trigger? 【发布时间】:2016-02-24 16:27:24 【问题描述】:

我正在 Oracle 数据库中创建一个新表,并且正在尝试学习如何创建触发器。首先,我想我会尝试创建一个基本触发器,在插入或更新之前检查现有值。以后,这将作为一个约束来处理,但我还需要一些触发器来比较其他现有行和列之间的值,所以我想知道如何将其编写为触发器。

我想要实现的目标:创建一个触发器,仅当表中没有其他行包含尝试在新行中输入的相同 TEMPLATE_ID 和 PRE_TEMPLATE_ID 值时才允许创建行。

我的代码:

CREATE TRIGGER check_redundancy
BEFORE INSERT OR UPDATE
ON SCHEMA.TABLE
FOR EACH ROW
BEGIN
    IF NEW.TEMPLATE_ID NOT IN
        (SELECT
            A.TEMPLATE_ID
        FROM
            SCHEMA.TABLE A
        WHERE
            NEW.PRE_TEMPLATE_ID = A.PRE_TEMPLATE_ID)
    THEN
        INSERT INTO SCHEMA.TABLE (ID, TEMPLATE_ID, PRE_TEMPLATE_ID, DATE_CREATED, CREATED_BY)
        VALUES (NEW.ID, NEW.TEMPLATE_ID, NEW.PRE_TEMPLATE_ID, NEW.DATE_CREATED, NEW.CREATED_BY);
    END IF;
END;

当我运行它时,我收到以下错误:

“ORA-06502: PL/SQL: 数值或数值错误: 字符串缓冲区太小” “ORA-06512:第 14 行”

(值得注意 - 它总是在第 14 行告诉我错误...即使我删除了 BEGIN 和 END 之间的所有内容,它仍然在第 14 行显示错误)。

所以我假设我应该在某处声明一个(或多个)变量,但我不确定该怎么做。

【问题讨论】:

PL/SQL错误中的行号是从BEGIN开始计算的,不是从CREATE TRIGGER开始计算的。不过,您的 PL/SQL 块没有 14 行。这真的是你正在运行的吗?您没有在 :NEW 前加上冒号,您不应该在触发器中重新插入该行(这只会导致触发器再次触发,直到 Oracle 杀死它),如果您尝试,您将收到一个 mutating table 错误从触发器所针对的同一个表中进行选择。 Oracle recommends 除非必须,否则不要将触发器用于约束。 触发器是一种根本上存在问题的方法来验证同一个表的不同行中的数据之间的关系。您可以使其与包中的 3 个触发器和一个集合(或复合触发器)一起使用,但这通常是解决该问题的一种极其复杂的方法。通常,需要验证行之间的关系意味着您有一个应该解决的数据模型问题。 @AlexPoole - 实际上,我收到以下错误 - ORA-06502: PL/SQL: numeric or value error: string buffer too small ORA-06512: at line 14. 奇怪的是,即使我删除了 BEGIN 和 END 之间的所有内容,它仍然会在第 14 行返回相同的错误。(更新的 OP 包含两个错误) 【参考方案1】:

before insert 触发器插入之前触发,而不是代替它。如果没有引发异常,则插入将在此触发器之后自然发生。如果使用这样的触发器进行验证,代码应该更像这样:

CREATE TRIGGER check_redundancy
BEFORE INSERT OR UPDATE
ON SCHEMA.TABLE
FOR EACH ROW
BEGIN
    IF :NEW.TEMPLATE_ID IN
        (SELECT
            A.TEMPLATE_ID
        FROM
            SCHEMA.TABLE A
        WHERE
            NEW.PRE_TEMPLATE_ID = A.PRE_TEMPLATE_ID)
    THEN
       RAISE_APPLICATION_ERROR (-20001, 'Your error message here');
    END IF;
END;

正如@Alex 在他的评论中所说,这确实不是一个好主意 - 约束更加可靠和安全。使用这样的触发器,当 2 个不同会话中的用户大致同时插入相同的数据时,很容易导致重复。而且您还会遇到“表正在变异”的问题。

【讨论】:

更不用说之前触发器会引发的变异表错误,以查询触发器所在的表......我看到你刚刚将它添加到你的答案中哈哈

以上是关于如何编写唯一约束作为触发器?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL系列 MySQL的约束

oracle数据导入时,提示违反唯一约束性?

如何编写迁移来撤消Laravel中的唯一约束?

Postgresql 触发器约束

如何查找给定列是不是存在唯一键约束

如果插入的值与唯一约束冲突,则 SQL 触发器在插入时更新字段