(Oracle SQL) 如何在插入后使用唯一键更新表?

Posted

技术标签:

【中文标题】(Oracle SQL) 如何在插入后使用唯一键更新表?【英文标题】:(Oracle SQL) How do I update a table with a unique key after an insert? 【发布时间】:2020-01-10 14:17:28 【问题描述】:

我有这张桌子:

CREATE TABLE STATO_VERS_METODO(
    METODO INT NOT NULL,
    PROGETTO INT NOT NULL,
    VERS NUMBER NOT NULL,
    STATO VARCHAR2(20) DEFAULT 'Nuovo' NOT NULL,
    NOTA_VERSM VARCHAR2(500),
    CONSTRAINT UK_STATO_METODO UNIQUE(METODO, PROGETTO, VERS)
);

如果我插入具有相同唯一键(metodo、progetto、vers)的行,我会收到违反唯一性的错误。此时我想修改已经存在的行并更新“Notes”字段,将 :NEW.NOTE 的值添加到现有的值。 我写了这个触发器:

create or replace TRIGGER AGGIORNA_NOTA_METODO
BEFORE INSERT ON STATO_VERS_METODO
FOR EACH ROW
DECLARE
    n_righe INT;
    nota VARCHAR2(500);

BEGIN
    SELECT COUNT(*) INTO n_righe FROM STATO_VERS_METODO WHERE METODO = :NEW.METODO AND PROGETTO = :NEW.PROGETTO AND VERS = :NEW.VERS;
    IF(n_righe > 0)
    THEN
        IF(:NEW.STATO = 'Modificato')
        THEN
            SELECT NOTA_VERSM INTO nota FROM STATO_VERS_METODO WHERE METODO = :NEW.METODO AND PROGETTO = :NEW.PROGETTO AND VERS = :NEW.VERS;
            UPDATE STATO_VERS_METODO
            SET NOTA_VERSM = nota||CHR(10)||:NEW.NOTA_VERSM WHERE METODO = :NEW.METODO AND PROGETTO = :NEW.PROGETTO AND VERS = :NEW.VERS;
        END IF;
    END IF;



END;

它给了我违反唯一性的错误并且不更新。 如何修改现有行?

【问题讨论】:

前触发器不会停止插入。因此,触发此触发器后,仍会尝试插入。这就是为什么您仍然会收到错误消息。 请使用标签下方的edit 按钮编辑您的问题,并包含您遇到的确切错误。谢谢。 你刚才说:If I insert a row with the same unique key (method, project, vers)....。也许您想在更新行时将STATO 设置为“Modificato”? @dmak2709 让我用一个例子来解释一下:假设我们在表 stato_vers_metodo (1, 1, 10, 'Modificato', 'Source code changed') 中有以下行。我在 stato_vers_metodo (1, 1, 10, 'Modificato', 'Minor fix') 中插入了一个新行,这会导致违反键的唯一约束(metodo、progetto、vers)。所以我无法插入该行,但我想通过将 NOTA_VERSM 字段连接到旧字段来修改现有行,如下所示:(1, 1, 10, 'Modificato', 'Source code changed Minor fix') 【参考方案1】:

没有必要使用触发器。您需要一个 Merge 语句 -

MERGE INTO STATO_VERS_METODO SVM
USING (SELECT 'new_metodo' METODO, 'new_progetto' PROGETTO, 'new_vers' VERS FROM DUAL) D
ON (SVM.METODO = D.METODO)
WHEN MATCHED THEN UPDATE SET NOTA_VERSM = NOTA_VERSM || CHR(10) || D.NOTA_VERSM
                         WHERE SVM.METODO = D.METODO
                              ,SVM.PROGETTO = D.PROGETTO
                              ,SVM.VERS = D.VERS
WHEN NOT MATCHED THEN INSERT (METODO,
                              PROGETTO,
                              VERS,
                              NOTA_VERSM)
                      VALUES ('new_metodo',
                              'new_progetto',
                              'new_vers',
                              'new_versm');

【讨论】:

非常感谢。由于我不熟悉合并,我应该在哪里输入这个命令?在插入命令之后还是代替它? 代替它。如果它找到一条记录,它会进行更新,否则插入。【参考方案2】:

正如 Ankit 指出的那样,您不需要使用触发器,但如果您是初学者,我将使用触发器发布答案。

创建表后,使用以下语句插入第一行。

INSERT INTO STATO_VERS_METODO(METODO, VERS, PROGETTO, NOTA_VERSM) 
       VALUES (1, 1, 10, 'Source code changed')

现在我们的表格只有一行,如下所示:

METODO | VERS | PROGETTO |   STATO  |  NOTA_VERSM
-----------------------------------------------------------
  1    |  10  |    1     |   Nuovo  |  Source code changed

现在,由于您声明了带有约束的表,因此您无法插入具有相同(METODO、PROGETTO、VERS)的另一行,因此类似于:

INSERT INTO STATO_VERS_METODO(METODO, VERS, PROGETTO, NOTA_VERSM) 
       VALUES (1, 1, 10, 'New String')

会抛出异常。

ORA-00001: unique constraint (****.UK_STATO_METODO) violated ORA-06512

因此,如果您想更改值,则需要执行UPDATE。但正如您在评论部分所说,您需要连接旧字符串和新字符串。一种解决方案可能是按如下方式实现触发器。

create or replace TRIGGER AGGIORNA_NOTA_METODO
BEFORE UPDATE ON STATO_VERS_METODO
FOR EACH ROW
BEGIN
    :NEW.STATO := 'Modificato';
    :NEW.NOTA_VERSM := :OLD.NOTA_VERSM || ' ' || :NEW.NOTA_VERSM; 
END;

更新行:

UPDATE STATO_VERS_METODO SET NOTA_VERSM = 'Minor fix' 
       WHERE METODO = 1 AND PROGETTO = 10 AND VERS = 1;

最后我们的表格是这样的:

METODO | VERS | PROGETTO |      STATO    |         NOTA_VERSM
--------------------------------------------------------------------
  1    |  10  |    1     |   Modificato  |  Source code changed Minor fix

您编写的触发器也会生成变异表异常learn more。

【讨论】:

感谢您提供带触发器的替代方案。因此,要在我插入行并获取唯一键的异常时自动更新,我应该按照 Ankit 的建议使用合并吗? 您不会在 INSERT 上自动更新,只有在 UPDATE 语句发生时才会自动更新。因为带有 STATO 'Nuovo' 的行还不存在。请阅读答案并检查所有代码。 我阅读了答案,谢谢,我只是想知道是否有办法“自动”更新表而不是得到唯一性违规错误 @Gibser,最好的替代方法是使用 MERGE 命令。 1.您可以避免在您的数据库中有一个新对象。 2. 触发器在 RDBMS 中根本不受欢迎。因为你曾经执行过它们,后来忘记维护了。

以上是关于(Oracle SQL) 如何在插入后使用唯一键更新表?的主要内容,如果未能解决你的问题,请参考以下文章

SQL插入的唯一约束-ORACLE

如何在 SQL 中对触发器进行语法化,以便在插入后从同一个表中更新列(oracle 数据库)

oracle数据库中怎么能避免相同的数据插入数据库多遍?sql语句怎么处理呢?

在Oracle中插入后返回标识列的值

如何得到JDBC Insert 语句执行后插入Oracle 数据库记录的主键

Oracle删除重复记录只保留一条数据的几种方法