【发布时间】:2019-10-30 10:23:29
【问题描述】:
我正在编写一个通用的 oracle 触发器。假设有许多主表,如 PERSON_INFO、EMPLOYEE_INFO 等,以及它们对应的审计表,如 PERSON_INFO_AUDIT、EMPLOYEE_INFO_AUDIT。结构如下。
PERSON_INFO 有列:-
------------------------------------------------
| PERSON_INFO |
------------------------------------------------
| PERSON_ID | FIRST_NAME | LAST_NAME |
| (NUMBER) | (VARCHAR2) | (VARCHAR2)|
------------------------------------------------
| 1 | Andrew | Jack |
------------------------------------------------
PERSON_INFO_AUDIT 包含 PERSON_INFO 的所有列以及另外两个列 OPERATIONS 和 AUDIT_DATE。
要求是,如果任何主表被更新或主表中的任何行被删除,则主表的旧条目应插入其相应的审计表中。
然后我写了如下更新:-
更新 PERSON_INFO SET FIRST_NAME= 'John';
那么 PERSON_INFO 的旧值应插入到 PERSON_INFO_AUDIT 表中,如下所示:-
PERSON_INFO_AUDIT 现在应该包含:-
-------------------------------------------------------------------------
| PERSON_INFO_AUDIT |
-------------------------------------------------------------------------
| PERSON_ID | FIRST_NAME | LAST_NAME | AUDIT_DATE | OPERATIONS|
| (NUMBER) | (VARCHAR2) | (VARCHAR2)| (TIMESTAMP) | (CHAR) |
-------------------------------------------------------------------------
| 1 | Andrew | Jack | 30-08-2019 | U |
-------------------------------------------------------------------------
这里的 audit_date 是今天的日期,操作指定主表中的行是被删除(D)还是更新(U)。为了方便上述场景,我编写了以下触发函数。
CREATE OR replace TRIGGER trig_PERSON_INFO_deleteupdate
after UPDATE OR DELETE
ON PERSON_INFO
FOR EACH ROW
DECLARE
base_table_name clob;
audit_table_name clob;
base_table_cols_in_string clob;
audit_table_cols_in_string clob;
operation char;
final_query clob;
BEGIN
base_table_name:= 'PERSON_INFO';
audit_table_name := base_table_name || '_AUDIT';
IF UPDATING THEN
operation:= 'U';
ELSE
operation:= 'D';
END IF;
SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY column_id)
INTO base_table_cols_in_string
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME= 'PERSON_INFO';
audit_table_cols_in_string:= base_table_cols_in_string || ',AUDIT_DATE,OPERATIONS';
final_query:= 'INSERT INTO ' || audit_table_name || '(' || audit_table_cols_in_string || ') VALUES(' || ':OLD.PERSON_ID,:OLD.FIRST_NAME,:OLD.LAST_NAME,' || SYSDATE || ',''' || operation || ''');';
dbms_output.put_line(final_query);
EXECUTE IMMEDIATE final_query;
END;
形成的查询是:
INSERT INTO PERSON_INFO_AUDIT(PERSON_ID,FIRST_NAME,LAST_NAME,AUDIT_DATE,OPERATIONS) VALUES(:OLD.PERSON_ID,:OLD.FIRST_NAME,:OLD.LAST_NAME,30-10-19,'U');
但是,当我尝试使用 EXECUTE IMMEDIATE final_query 执行查询时,我遇到了错误
【问题讨论】:
-
只去掉最后一个单引号前的分号。
-
@BarbarosÖzhan 要求只保留审计表中的旧值。新值无论如何都会出现在主表中
-
您尝试做的事情是不可能的。没有动态方法可以访问
:old和:new中的字段。通常做的是读取DBA_TAB_COLUMNS并动态创建整个触发器。 -
@Abhishek 我想这取决于您所说的“通用”是什么意思。我在您的 OP 中注意到构建您的
final_query的 PL/SQL 代码按名称引用字段。您需要为每个表重写触发器的那一部分。这是您的理解/期望吗? -
让甲骨文承担压力。使用 Flashback Data Archive - 适用于 Oracle 11.2.0.4 及更高版本。
标签: oracle plsql database-trigger