条件 old 不等于 new 的 Oracle 触发器以错误 PLS-00049、PL/SQL: ORA-00933 结束
Posted
技术标签:
【中文标题】条件 old 不等于 new 的 Oracle 触发器以错误 PLS-00049、PL/SQL: ORA-00933 结束【英文标题】:Oracle Trigger with condition old not equal to new ending up with errors PLS-00049, PL/SQL: ORA-00933 【发布时间】:2018-10-24 19:51:58 【问题描述】:假设我在名为 EMPLOYEES 的主表上有数据,示例数据如下:
员工
+--------+----------+
| EMP_ID | EMP_NAME |
+--------+----------+
| 100 | Smith |
| 200 | Clark |
+--------+----------+
现在,如果表中插入了新数据或更新了现有数据;然后数据如下所示: 员工
+--------+----------+
| EMP_ID | EMP_NAME |
+--------+----------+
| 100 | Blake | <---- UPDATE with different value
| 200 | Clark | <---- UPDATE with same value
| 300 | Mary | <---- INSERT
+--------+----------+
其中 EMP_ID 为 100 和 200 的行是更新操作,而 EMP_ID 为 300 的行作为其新记录插入。
我编写了触发器来捕获 EMP_NAME 列中的插入或更新更改,仅当旧 emp_name 不等于相应 EMP_ID 的新 emp_name 时。 (我不想在名为 EMPLOYEE_AUDITS 的审计表中有重复的 EMP_ID)。下面是触发器:
CREATE OR REPLACE TRIGGER employee_audits_tr
INSERT OR UPDATE ON employees
FOR EACH ROW
BEGIN
IF ( inserting OR updating AND :old.emp_name <> :new.emp_name ) THEN
INSERT INTO employee_audits(emp_id, old_emp_name,new_emp_name)
VALUES(emp_id, :old.emp_name, :new.emp_name )
WHERE emp_id = :new.emp_id;
END IF;
END;
根据上面提供的数据,尝试在 EMPLOYEE_AUDITS 上获得如下输出 EMPLOYEE_AUDITS
+----+--------+--------------+--------------+
| ID | EMP_ID | OLD_EMP_NAME | NEW_EMP_NAME |
+----+--------+--------------+--------------+
| 1 | 100 | Smith | Blake |
| 2 | 300 | NULL | Mary |
+----+--------+--------------+--------------+
对于下一次迭代,当 EMPLOYEES 表再次使用新数据集更新时,如下所示: 员工
+--------+----------+
| EMP_ID | EMP_NAME |
+--------+----------+
| 100 | Karla | <---- UPDATE with NEW value
| 200 | Clark | <---- UPDATE with same value
| 300 | James | <---- UPDATE with NEW value
| 400 | Sofia | <---- INSERT
+--------+----------+
EMPLOYEE_AUDITS 表应包含以下值:EMPLOYEE_AUDITS
+----+--------+--------------+--------------+
| ID | EMP_ID | OLD_EMP_NAME | NEW_EMP_NAME |
+----+--------+--------------+--------------+
| 1 | 100 | Blake | Karla |
| 2 | 300 | Mary | James |
| 3 | 400 | NULL | Sofia |
+----+--------+--------------+--------------+
使用触发器,出现如下错误:
错误 PLS-00049、PL/SQL:ORA-00933
在此先感谢大家,并感谢任何帮助。
谢谢, 里查
【问题讨论】:
【参考方案1】:触发码错误:
触发器声明中没有BEFORE
关键字
您已经说过它会在插入或更新之前触发;您也不必将其放入 IF 中
如果你确实把它放在那里,那么要小心:当有OR
s 时,如果你不将运算符括在括号中,你会破坏一切。那将是if (updating or inserting) and (:old.emp_name <> :new.emp_name) then ...
。
注意 NULL 值!插入时,没有 OLD 值
INSERT INTO
中没有 WHERE
子句
您错过了填充AUDIT
表中的ID
列;我选择使用序列
上面所说的组合(尤其是缺少BEGIN
和多余的WHERE
)导致ORA-00933(SQL 命令未正确结束)错误。
这是一个完整的示例:
SQL> create table employees (emp_id number, emp_name varchar2(20));
Table created.
SQL> insert into employees
2 select 100, 'smith' from dual union all
3 select 200, 'clark' from dual;
2 rows created.
SQL> create table employee_audits
2 (id number, emp_id number, old_emp_name varchar2(20), new_emp_name varchar2(20));
Table created.
SQL> create sequence seq_audit;
Sequence created.
SQL>
SQL> create or replace trigger employee_audits_tr
2 before insert or update on employees
3 for each row
4 begin
5 if nvl(:old.emp_name, '-1') <> nvl(:new.emp_name, '-1')
6 then
7 insert into employee_audits
8 (id, emp_id, old_emp_name, new_emp_name)
9 values
10 (seq_audit.nextval, :new.emp_id, :old.emp_name, :new.emp_name);
11 end if;
12 end;
13 /
Trigger created.
测试:
SQL> update employees set emp_name = 'blake' where emp_id = 100;
1 row updated.
SQL> update employees set emp_name = 'clark' where emp_id = 200;
1 row updated.
SQL> insert into employees values (300, 'mary');
1 row created.
SQL> select * From employee_audits;
ID EMP_ID OLD_EMP_NAME NEW_EMP_NAME
---------- ---------- -------------------- --------------------
1 100 smith blake
2 300 mary
SQL>
[编辑:每位员工只保留一行]
在我看来,您不应该这样做 - 如果您丢失了之前的所有修改,这是什么样的审核?无论如何,这是触发代码:
首先更新一行 如果EMP_ID
不存在,UPDATE
不会做任何事情,SQL%ROWCOUNT
将是 0
在这种情况下,INSERT
一行
.
SQL> create or replace trigger employee_audits_tr
2 before insert or update on employees
3 for each row
4 begin
5 update employee_audits set
6 old_emp_name = :old.emp_name,
7 new_emp_name = :new.emp_name
8 where emp_id = :new.emp_id ;
9
10 if sql%rowcount = 0 then
11 insert into employee_audits (id, emp_id, old_emp_name, new_emp_name)
12 values (seq_audit.nextval, :new.emp_id, :old.emp_name, :new.emp_name);
13 end if;
14 end;
15 /
Trigger created.
SQL>
【讨论】:
谢谢@Littlefoot。我非常感谢您的帮助。非常感谢您的详细解释,重写代码并为我节省了大量时间。我用 AFTER insert 语句编写了代码,我想在格式化时我可能已经删除了。我的错。我有一个问题:如果我必须在 EMPLOYEES 表上添加另一列“PAYMENT_HOLD”,其中包含 Y 和 N 值,并且如果我必须在 EMPLOYEE_AUDITS 表上捕获这些更改,我必须只修改 IF 部分对吗?如下:if ((nvl(:old.emp_name, '-1') <> nvl(:new.emp_name, '-1')) or (nvl(:old.payment_hold, '-1') <> nvl(:new.payment_hold, '-1')))
不客气。作为一个新的条件,是的 - 如果你想检查它,使用 OR。
真棒@Littlefoot。再次感谢您的快速回复。非常感谢您的时间和帮助
没问题:)
嗨@Littlefoot,我用不同的数据集尝试了几次迭代并注意到,由于为插入语句指定了条件,我得到了重复的 EMP_ID。如果 emp_id 已经存在,我应该包含更新语句吗?我只需要 EMPLOYEES_AUDIT 表中唯一的 emp_id。我应该将代码修改为BEGIN... IF insert then... IF nvl(:old.emp_name, '-1') <> nvl(:new.emp_name, '-1') then.. insert into...
IF 更新然后IF update then... nvl(:old.emp_name, '-1') <> nvl(:new.emp_name, '-1') then.. update where...
抱歉问了太多问题以上是关于条件 old 不等于 new 的 Oracle 触发器以错误 PLS-00049、PL/SQL: ORA-00933 结束的主要内容,如果未能解决你的问题,请参考以下文章