sql >> Database >  >> RDS >> Oracle

Auditgeschiedenis van meerdere tabellen in de database

Ik heb een zeer effectieve implementatie hiervan gezien die als volgt gaat:

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.
    ...
)

En we maken een eenvoudige reeks om ons een nieuwe incrementele integerwaarde te geven voor audit_entry_id :

CREATE SEQUENCE audit_entry_id_seq;

De schoonheid van een tabel als audit_entry is dat u informatie over alle soorten DML's kunt opslaan- INSERT , UPDATE en DELETE op dezelfde plaats.

Bewaar voor bijvoorbeeld invoegen de old_* kolommen null en vul de new_* met jouw waarden.

Vul voor updates zowel old_* en new_* kolommen wanneer ze worden gewijzigd.

Voor verwijderen, vul gewoon de old_* kolommen en bewaar de new_* null.

En voer natuurlijk de juiste waarde in voor audit_entry_type .;0)

Dan heb je bijvoorbeeld een tabel als volgt:

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.
    ...
)

Maak als volgt een trigger op deze tafel:

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;
/

Slechts één woord van waarschuwing:wees selectief met welke tabellen en kolommen u wilt controleren, want hoe dan ook, deze tabel zal een enorm aantal rijen hebben. SELECT uitspraken in deze tabel zullen langzamer zijn dan je zou verwachten.

Ik zou heel graag een ander soort implementatie hier zien, omdat het een goede leerervaring zou zijn. Ik hoop dat je vraag meer antwoorden krijgt, want dit is de beste implementatie van een controletabel die ik heb gezien en ik ben nog steeds op zoek naar manieren om het te verbeteren.




  1. Een SQL Server Monitoring Tool kiezen die aan uw behoeften voldoet

  2. JPA Eclipselink-databasewijzigingsmelding maakt cache-invoer niet ongeldig

  3. Een speciaal netwerk configureren voor communicatie met beschikbaarheidsgroepen

  4. MySQL verwijdert alle rijen waar id groter is dan het opgegeven getal