我的数据库中有3-4个表,我想跟踪它们的变化。
我主要关注更新。
每当发生更新时,我想在审计表中存储先前的条目(值或完整的行)。
我想到的基本栏目如下:
AuditId, TableName, PK1, PK2, PK3, PKVal1, PKVal2, PKVal3, UpdateType, PrevEntryJSON
JSON格式为:Key:Value
我更喜欢随着列不断更改而继续使用它,即使它们没有更改,我也希望保留所有值。
其他选项是删除带有100个列的JSON,这些列的名称与不同的列相同(所有表的累计)。
我想听听别人对此的看法。我怎么能改进它以及我可以面对哪些问题?
通过触发器可能不是更好的方式,但我愿意接受它。
谢谢,
答案 0 :(得分:1)
我已经看到了一个非常有效的实现,如下所示:
TABLE audit_entry (
audit_entry_id INTEGER PRIMARY KEY,
audit_entry_type VARCHAR2(10) NOT NULL,
-- ^^ stores 'INSERT' / 'UPDATE' -- / 'DELETE'
table_name VARCHAR2(30) NOT NULL,
-- ^^ stores the name of the table that is changed
column_name VARCHAR2(30) NOT NULL,
-- ^^ stores the name of the column that is changed
primary_key_id INTEGER NOT NULL,
-- ^^ Primary key ID to identify the row that is changed
-- Below are the actual values that are changed.
-- If the changed column is a foreign key ID then
-- below columns tell you which is new and which is old
old_id INTEGER,
new_id INTEGER,
-- If the changed column is of any other numeric type,
-- store the old and new values here.
-- Modify the precision and scale of NUMBER as per your
-- choice.
old_number NUMBER(18,2),
new_number NUMBER(18,2),
-- If the changed column is of date type, with or without
-- time information, store it here.
old_ts TIMESTAMP,
new_ts TIMESTAMP,
-- If the changed column is of VARCHAR2 type,
-- store it here.
old_varchar VARCHAR2(2000),
new_varchar VARCHAR2(2000),
...
... -- Any other columns to store data of other types,
... -- e.g., blob, xmldata, etc.
...
)
我们创建一个简单的序列,为audit_entry_id
提供新的增量整数值:
CREATE SEQUENCE audit_entry_id_seq;
像audit_entry
这样的表的美妙之处在于,您可以在同一个地方存储有关所有类型的DML的信息 - INSERT
,UPDATE
和DELETE
。
例如,对于插入,请将old_*
列保留为空,并使用您的值填充new_*
。
对于更新,只要更改了old_*
和new_*
列,就会填充这些列。
对于删除,只需填充old_*
列并保持new_*
为空。
当然,为audit_entry_type
输入适当的值。 ; 0)
然后,例如,您有一个如下表:
TABLE emp (
empno INTEGER,
ename VARCHAR2(100) NOT NULL,
date_of_birth DATE,
salary NUMBER(18,2) NOT NULL,
deptno INTEGER -- FOREIGN KEY to, say, department
...
... -- Any other columns that you may fancy.
...
)
只需在此表上创建一个触发器,如下所示:
CREATE OR REPLACE TRIGGER emp_rbiud
-- rbiud means Row level, Before Insert, Update, Delete
BEFORE INSERT OR UPDATE OR DELETE
ON emp
REFERENCING NEW AS NEW OLD AS OLD
DECLARE
-- any variable declarations that deem fit.
BEGIN
WHEN INSERTING THEN
-- Of course, you will insert empno.
-- Let's populate other columns.
-- As emp.ename is a not null column,
-- let's insert the audit entry value directly.
INSERT INTO audit_entry(audit_entry_id,
audit_entry_type,
table_name,
column_name,
primary_key,
new_varchar)
VALUES(audit_entry_id_seq.nextval,
'INSERT',
'EMP',
'ENAME',
:new.empno,
:new.ename);
-- Now, as date_of_birth may contain null, we do:
IF :new.date_of_birth IS NOT NULL THEN
INSERT INTO audit_entry(audit_entry_id,
audit_entry_type,
table_name,
column_name,
primary_key,
new_ts)
VALUES(audit_entry_id_seq.nextval,
'INSERT',
'EMP',
'DATE_OF_BIRTH',
:new.empno,
:new.date_of_birth);
END IF;
-- Similarly, code DML statements for auditing other values
-- as per your requirements.
WHEN UPDATING THEN
-- This is a tricky one.
-- You must check which columns have been updated before you
-- hurry into auditing their information.
IF :old.ename != :new.ename THEN
INSERT INTO audit_entry(audit_entry_id,
audit_entry_type,
table_name,
column_name,
primary_key,
old_varchar,
new_varchar)
VALUES(audit_entry_id_seq.nextval,
'INSERT',
'EMP',
'ENAME',
:new.empno,
:old.ename,
:new.ename);
END IF;
-- Code further DML statements in similar fashion for other
-- columns as per your requirement.
WHEN DELETING THEN
-- By now you must have got the idea about how to go about this.
-- ;0)
END;
/
只需提醒一句:选择要选择审核的表和列,因为无论如何,这个表会有大量的行。此表上的SELECT
语句将比您预期的要慢。
我真的很想看到任何其他类型的实现,因为这将是一个很好的学习经验。希望你的问题得到更多的答案,因为这是我见过的审计表的最佳实现,我仍然在寻找使其更好的方法。