Update trigger on table fr audit purpose to record column level information

Hi,
I want to create a update trigger which will record data in an audit table.
In audit table I want to have columns like old_value_of_Col and new_value_of_Col.
This is easily achievable. But my main concern is to add three more columns having the information of:
1) Which column was updated?
2) Old value of column that was updated
3) New value of column that was updated
If possible, Also if one updates three columns for example, then in such a case. I would like to have three entries in audit table for the corresponding three columns.
Please help.
Thanks in advance.

A few approaches to consider.
First, if you are on 11g, take a look at Flashback Data Archive: http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28424/adfns_flashback.htm#ADFNS01011
Second, you can use Fine-Grained Auditing to capture DML statements and bind variables. It is not as easy to reconstruct the before/after picture, but it may be sufficient for some purposes.
For trigger-based solutions, I have seen the approach you propose (1 row for each column changed) and it is tedious and prone to maintenance headaches. You have to write code for each column, doing compares (remembering to consider NULLs), dealing with different datatypes, etc. We used that design becasue there was an actual requirement to produce a report that needed such a structure.
An easier trigger-based solution is to create a history table for the table you want to track, with all of the columns from the original, plus whatever else you need for housekeeping. Write an After Insert/Update/Delete trigger on your base table, and populate/insert rows into the history table:
- For inserts, populate and insert row from the :new. values
- For deletes, populate and insert a row from the :old. values
- For updates, popualte and insert a row from the :old. values and another from the :new. values
I would also have a column to designate whether this is an Insert, Delete, Update-Old or Update-New row.
Once you have done one example, the rest are easy. If you were sufficiently motivated (I have not yet been :-) ), you could probably write a script to query DBA_TAB_COLS and generate the code.

Similar Messages

  • Record column level changes of table for audit.

    Hi Experts,
    I need  suggestion on recording column level changes in table for internal aduit purposes, our company policy does not allow us to enable CDC and CT on database levels so I am looking for whar are other the best ways for doing the same ?
    I know we can use triggers and OUTpUT clause to record the changes on tables but Is there any other light weight solution for recording changes on transactions tables which very gaint tables. 
    Seeking your guidnace on solution side ?
    Shivraj Patil.

    OUTPUT should be the best choice for your case as this would be much lighter than Trigger.

  • Update trigger on table

    Hi,
    I have a shadow table that I want to hold exactly the same values as a main table.
    If I use a trigger in the main table to update the shadow table where a column has changed, would it be better to update each column only if it has changed (as a trigger on the column - so 3 columns changed mean 3 updates) or to update the whole table with the new values even though most of them will not have changed?
    thanks in advance
    Michael

    As a rule I would avoid unnecessary database writes. In this case one update is always better than three separate updates.
    rgds, APC
    Have you considered using replication to handle this for you?

  • Update on a Table is Slow After a New Trigger is Applied on Table.

    Hi All,
    Here is the situation:
    1. Table A - we have INSURANCE Records. If we have to cancel Insurance for an agent we have to update cancellation_date &
    INS_Flag sysdate & 'N' respectively. It is having thousands of records.
    - > Every time we update a record with cancellation_date & INS_Flag there are two other tables which are updating using
    trigger.
    - > For each record updated in Table A we have to terminate the existing contract in Table B and set the INS_flag to N in third
    Table C.
    2. Table B - It is the primary table for all the INSURANCE contracts. so we have to terminate existing contracts for those agent who are
    having cancellation_date & INS_Flag in table A.
    3. Table C - It is having all the basic contracts with a flag for INSURANCE Contracts. If a agent is having a flag as Y in this table must have a
    INSURANCE contract in Table B.
    Now We are having a simple after update trigger on TABLE A which will update table B & Table C accordingly for each row.
    We are having Table A so that we can synchronize Oracle Apps Database & Oracle Database for deverlopers.
    Before trigger - Update on table A was fast but after implementing the trigger it takes 4-5 mins. for 10 records update in table A.
    There are several indexes on Table A as well( including several functional indexes ).
    Oracle Database :
    BANNER
    Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi
    PL/SQL Release 10.2.0.3.0 - Production
    CORE 10.2.0.3.0 Production
    TNS for Solaris: Version 10.2.0.3.0 - Production
    NLSRTL Version 10.2.0.3.0 - Production
    Please help me How can I make the update faster.
    If it is required I will post the trigger code as well.
    Many thanks in advance.
    Edited by: Kamal Sharma on Jun 23, 2010 4:10 PM

    Here Table A = TB_CMA063_NETONE_STAGE_DATA
    Table B = TB_30.REAL_ESTATE_AGENT_NUM
    Table C = TB_CMA009_SUPRA_RE_AGNT_CONT
    CREATE OR REPLACE TRIGGER CMADBA.TR_CMA003_ALTERINS_ON_FLG_UPD
    AFTER UPDATE
    ON CMADBA.TB_CMA063_NETONE_STAGE_DATA
    REFERENCING NEW AS New OLD AS Old
    FOR EACH ROW
    DECLARE
        g_exception_id      TB_CMA089_CMA_APPS_EXCEPTION.EXCEPTION_ID%Type;
        v_msg               varchar2(200);
        v_status            varchar2(10);
        l_cmn               NUMBER;
        l_board_cmn         NUMBER;
    BEGIN
        SELECT CUSTOMER_MASTER_NUM INTO l_board_cmn FROM TB_CMA005_CUSTOMER_MASTER 
        WHERE IS_REAL_ESTATE_BOARD_FLG='Y'
        AND UPPER(RE_NETONE_NAM) = UPPER(:new.IR_INSTALL_SITE_NAM);
        IF :new.IR_INSURANCE_FLG = 'N' THEN
            BEGIN
            -- CANCEL Table 30 record for this agent
            UPDATE TB_CMA030_RE_AGENT_INS_PR_BIL TB_30
               SET TB_30.cancellation_dt = SYSDATE,
                   TB_30.RECORD_UPDATE_FLG = 'Y'
             WHERE TB_30.REAL_ESTATE_AGENT_NUM = :new.IR_AGENT_ID_NAM
               AND CANCELLATION_DT IS NULL          
               AND CUSTOMER_MASTER_NUM = l_board_cmn;
                -- UPDATE TABLE 9 RUF to Y so that Contract API would process this agent in next run
                UPDATE TB_CMA009_SUPRA_RE_AGNT_CONT
                SET RECORD_UPDATE_FLG='Y'
                WHERE REAL_ESTATE_AGENT_NUM = :new.IR_AGENT_ID_NAM
                    AND CANCELLATION_DT IS NULL
                    AND CUSTOMER_MASTER_NUM = l_board_cmn;
            END;          
        ELSIF :new.IR_INSURANCE_FLG = 'Y' THEN
            BEGIN
                INSERT INTO TB_CMA030_RE_AGENT_INS_PR_BIL
                    (BILL_FREQ_UNIT_CD,CUSTOMER_MASTER_NUM,SERIAL_NUM,REAL_ESTATE_AGENT_NUM,REAL_ESTATE_BOARD_NUM,
                    PRODUCT_NAM,  SERVICE_TYPE_NAM,  LEASE_TYPE_NAM,  CONT_START_DT, RE_AGENT_CUSTOMER_MASTER_NUM,
                    RE_AGENT_CONT_START_DT, INS_BILL_FREQ_UNIT_CD,
                    CONT_END_DT, RECORD_UPDATE_FLG, INS_AGENT_CONT_START_DT)
                SELECT DISTINCT 'AN', TB009.CUSTOMER_MASTER_NUM, TB009.SERIAL_NUM,
                    TB009.REAL_ESTATE_AGENT_NUM, TB009.REAL_ESTATE_BOARD_NUM, TB009.PRODUCT_NAM, 'INS',
                    TB009.LEASE_TYPE_NAM, TB009.CONT_START_DT, TB009.RE_AGENT_CUSTOMER_MASTER_NUM, TB009.RE_AGENT_CONT_START_DT,
                    TB009.BILL_FREQ_UNIT_CD, TB009.CONT_END_DT, 'Y', TRUNC(SYSDATE)
                FROM TB_CMA009_SUPRA_RE_AGNT_CONT TB009
                WHERE TB009.CUSTOMER_MASTER_NUM = l_board_cmn
                AND TB009.CANCELLATION_DT       IS NULL
                AND TB009.IS_TRAINING_KEY_FLG   = 'N'
                AND TB009.REAL_ESTATE_AGENT_NUM =  :new.IR_AGENT_ID_NAM
                AND TB009.product_nam           IN ('AKY','DKY')
               and not exists
                  (select 'x'
                     from tb_cma030_re_agent_ins_pr_bil tb030
                    where tb030.customer_master_num   = tb009.customer_master_num
                      and tb030.real_estate_agent_num = tb009.real_estate_agent_num
                      and tb030.serial_num            = tb009.serial_num
                      and tb030.ins_bill_freq_unit_cd = tb009.bill_freq_unit_cd
                      and tb030.product_nam           = tb009.product_nam);
                -- UPDATE TABLE 9 RUF to Y so that Contract API would process this agent in next run
                UPDATE TB_CMA009_SUPRA_RE_AGNT_CONT
                SET RECORD_UPDATE_FLG='Y', AGENT_KEY_INS_FLG='Y'
                WHERE REAL_ESTATE_AGENT_NUM = :new.IR_AGENT_ID_NAM
                    AND CANCELLATION_DT IS NULL
                    AND CUSTOMER_MASTER_NUM = l_board_cmn;
            END;
        END IF;   
    EXCEPTION
        WHEN OTHERS THEN          
            pc_cma024_batchjob.sp_cma025_log_err(l_board_cmn
                                                ,'UPDATE_TB63_TRIGGER'
                                                ,'TRIGGER-TR_CMA003_ALTERINS_ON_FLG_UPD'
                                                ,sqlcode
                                                ,sqlerrm,
                                                'An error occurred when Table 63 trigger tried to cancel/create Table 30 record for Agent ' || :new.IR_AGENT_ID_NAM);
    End;
    /

  • Update Trigger and COLUMNS_UPDATED()

    Let me rephrase my question:
    I want to know if there is a faster way (probably using bit operators) to see if COLUMNS_UPDATED() in an update trigger on a table with more than 8 columns contains any column other than 1 specific column I will refer
    to as 'columnToIgnore'. I don't care about which columns are included or if the value has actually changed, only that there are other columns present.
    Original question:
    I have inherited a Sql Server database + schema and corresponding application and I have been tasked with speeding up the batch update of users in the database. After running some profiling I discovered that the reason it was slow is because of an update
    trigger on the users table. I did some “cleaning up” of the trigger and now it runs faster but I still think it could be more efficient. The objective of the trigger is to write to an Audit table any time an update takes place and any column is updated (it
    does not actually matter if the value has changed or not) with the exception of 1 specific column. Now I have the following code in the trigger which I had refactored from something much uglier.
    DECLARE @ColumnsUpdated VARBINARY(100)
    SET @ColumnsUpdated = COLUMNS_UPDATED()
    DECLARE @onlyIgnoreColumnChanged int
    SET @onlyIgnoreColumnChanged = 1
    SET @onlyIgnoreColumnChanged = 1
    IF (EXISTS(SELECT 1
    FROM INFORMATION_SCHEMA.COLUMNS Field
    WHERE TABLE_NAME = 'users'
    AND sys.fn_IsBitSetInBitmask(@ColumnsUpdated, COLUMNPROPERTY(OBJECT_ID('dbo.users'), COLUMN_NAME, 'ColumnID')) <> 0
    AND column_name !='columnToIgnore')) BEGIN
    SET @onlyIgnoreColumnChanged = 0
    END
    Ideally I would change the code to disable the trigger and do the insert myself BUT the code is old Classic ASP ( ie. VBScript with ODBC database connections) so I am not going to change any of the calling code because that would cost too much time and probably
    create more problems than I am solving.
    My proposed fix which I am unsure of how to code in SQL:
    It seems to me that there should be a way to get the proper id or position for the column and see if the column was used in the COLUMNS_UPDATED().
    If it was not used then we can set @onlynieuwchanged to 0 for the remaining code (not shown)
    If it was used then remove the bit from the COLUMNS_UPDATED() and see if it is empty
    If its empty then then we can set @onlynieuwchanged to 1 for the remaining code (not shown)
    If it’s not empty then then we can set @onlynieuwchanged to 0 for the remaining code (not shown)
    My problem is I am not sure how to go about coding this logic in SQL. I would think it would be possible with BIT operators? It would seem this could be more efficient than what I have now as it would get rid of the select statement which could slow down
    a large batch update.
    Notes
    My table has 30+ some columns so more than 8 which is relevant when working with COLUMNS_UPDATED() from what I have read. It also means that testing each column with UPDATE() would probably be more inefficient than what I have now.
    The update is called from the front end code (VBScript) which currently times out although not as much as it used to with my latest change. I cannot alter the code to run async mode and show progress, it would be easier for me to update the trigger.
    Reason for audit - we only record the user id in an audit table. This is then used to force a sync to another system outside of our source control that is used for various other tasks (mailings, views, etc). Again, I do not want to change how this system
    works, I only want to speed up the existing trigger with minimal effort.
    Sql Server version 2012, database Schema is set to Sql server 2005 compatibility mode.
    Any help would be greatly appreciated. Thank you in advance!
    -Igor

    Thank you again everyone for your input. As I mentioned there is much out of my control. This is an active application worked on by many developers and has been pieced together over the past 10+ years. It is NOT well written, the database schema has MUCH
    to be desired, and the code is the classic definition of 'spaghetti' code and multiple platforms are used to access the data, and there are millions of lines of code. There is nothing I can do about all of this which is why I am ignoring much of the advice.
    Sure, if this was a new application or something that was well written and possible to do some modifications then I would BUT it's not. If I was given 1 year of dedicated time to fix everything... well, I would probably throw it all away and start from scratch.
    Here is what I finally came up with. It is probably not  much faster, if at all, but when I started this I thought there would be a simple solution that I did not see. I will probably leave my code the way it was but will include the following for reference.
    DECLARE @bit int, @field int, @char int, @comparisonField VARBINARY(100)
    -- the following segment recreates the value contained in COLUMNS_UPDATED() if only the field nieuw was altered and stores that value in field @comparisonField
    SET @field = (SELECT COLUMNPROPERTY(OBJECT_ID('dbo.users'), 'columnToIgnore', 'ColumnID')) -- geth the field id for column nieuw
    select @bit = (@field - 1 )% 8 + 1
    select @bit = power(2,@bit - 1)
    select @char = ((@field - 1) / 8) + 1
    -- select @field AS [Field number], @char AS [Char], @bit AS [Bit] -- debug code to check the bits that are tested.
    -- Recreate the binary value of just having the field present in the columns_updated
    SELECT @comparisonField = CONVERT(VARBINARY(100),'0x' + RIGHT('000000000000000000' + RIGHT(CONVERT(VARCHAR(30), CONVERT(varbinary(1), @bit), 1), 2), @char*2), 1)
    -- if the generated value in @comparisonField is the same as COLUMNS_UPDATED() then only nieuw was updated
    IF @comparisonField <> COLUMNS_UPDATED() BEGIN
    -- code to add to the tracking table
    END
    -Igor

  • Table with Audit fields

    Hi,
    I am designing a schema where in all the data tables will have created_by, created_on, modified_by and modified_on columns to find what & when changes made and who made changes to the records of the tables for audit purpose. These audit columns are rarely used in queries.
    Other than the above said audit columns, these tables will have application data columns on an average of 20-25 max.
    Will these additional audit columns impact the DML performance on the data tables?
    If it is, will it be a good idea creating separate audit table with all these audit columns having one-to-one relationship with the actual data tables? Will this separation really reduce the load on actual data tables and improve the DML performance?
    In case of creating separate audit tables, is it advisable creating these audit tables in separate schema? So that, the audit tables and related indexes will be created on a different data file and hence archiving, backup and recovery of actual data tables will be easy.
    To update these audit columns on separate tables, using of insert, update & delete triggers is advisable?
    Please advice.
    Thanks in advance
    Sathish

    user6092922 wrote:
    Hi,
    I am designing a schema where in all the data tables will have created_by, created_on, modified_by and modified_on columns to find what & when changes made and who made changes to the records of the tables for audit purpose. These audit columns are rarely used in queries.
    Other than the above said audit columns, these tables will have application data columns on an average of 20-25 max.
    Will these additional audit columns impact the DML performance on the data tables?
    If it is, will it be a good idea creating separate audit table with all these audit columns having one-to-one relationship with the actual data tables? Will this separation really reduce the load on actual data tables and improve the DML performance?
    In case of creating separate audit tables, is it advisable creating these audit tables in separate schema? So that, the audit tables and related indexes will be created on a different data file and hence archiving, backup and recovery of actual data tables will be easy.
    To update these audit columns on separate tables, using of insert, update & delete triggers is advisable?
    Welcome to OTN forums!
    What's the db version in 4 digits and o/s information?
    Why you are reinventing the wheel by adding such columns in the table? Why not to take advantage of the auditing facility which is already there in the database?
    Aman....

  • Multiple column update trigger

    Hi,
    I want to have a history track of all data that has ever been updated in a table. Say, table1 has 100 columns and to store its data change history, I have made another table track_table1 having columns : updated_column_name, old_data, new_data, mod_time.
    For storing this data history, I need to write a trigger in which I will identify all columns that has value updated and insert column's name and values in the track_table1 table.
    What I currently do is checking each of 100 column's new and old value inside the trigger to find out which columns have been updated. This increases the code and also oracle has to check each 100 column's values to find out whether it is being updated.
    Is there any way where oracle itself can give a list of columns which have values updated by the update statement ? so that there is no need to check old and new values of every column of the table.
    I am using oracle 9i and 10g databases.
    Thanks much in advance,
    Kawa

    kawa alkesh wrote:
    So, what I get from here is : I can't get old and new values when used dynamically in trigger itself, but I can get these values dynamically when I write a script for it. Is it so ??Yes, you cannot dynamically reference the :new.column_name or :old.column_name values in a trigger. You can dynamically generate the trigger as was demonstrated in the other thread.
    Also, as per the scenario explained in my first question of this thread, shall I write a script file, store it on the machine as a file and then write a trigger to call this script ? Correct me if I am wrong... I have never done this kind of thing.. so a bit confusedI don't follow. There is no need to create a script that is stored on any machine. You can write a procedure that generates the trigger, install that procedure in your database, and call the procedure to generate the trigger whenever the structure of the table changes. Whether it really makes sense to do this, rather than just writing the trigger code manually, is something you'll have to determine.
    Justin

  • Cash desk and payment lots documents not updating in DFKKOP table

    Dear Experts,
    I have noticed that my cash desk payments and payment lot documents are not getting updating in the DFKKOP table. I have identified that for this documents, the document class is updated with "1" which means "Document does not have FI-CA items".
    But, the postings where shown in the FPL9 transactions and clearing is happening as per the settings and requirements.
    So please update me, on what basis the system would determine the document class for the document types in contract accounting..
    Your inputs on this issue will be really helpful for me.
    Thanks
    Aditya Viswanath

    Hi Aditya,
    In a simple word, whichever documents are posted with MAIN/SUB creates a new row in DFKKOP with document number in DFKKOP-OPBEL.
    If a payment is posted as an open credit item, it should be there DFKKOP-OPBEL field and BETRW and BETRH will be populated with payment amount.
    However if a payment is posted as a clearing document against a debt, system updates that row of DFKKOP where DFKKOP-OPBEL is the cleared document number (debt, mostly an invoice). Clearing document (DFKKOP-AUGBL) of that row is updated with the payment document number.
    In either of the cases DFKKKO and DFKKOPK are updated and below are the reason.
    DFKKKO - document header level information (as who created, at what time, origin key, reco key etc.)
    DFKKOPK - (GL level information of the document)
    For payment lot and payment document related information you can refer DFKKZK, DFKKZP and DFKKZPT tables.
    Hope it clarifies your query.
    Thanks, Bodhisattwa

  • Can any one please send me an update trigger (pl/sql procedure) for ap tables(ap_suppliers,ap_supplier_site_all,and contacts)

    Please send an query for update trigger for those tables .
         1: ap_suppliers
         2: ap_supplier_sites_all
         3: ap_supplier_contacts.
    Thanks,
    Chaitanya.

    Hi,
    Actually ID and Data are different names in my API. Here I mentioned like that.
    Am using Oracle 10g version.
    Yes I am going to get the multiple values using this Ref Cursor. The same ref cursor is used in another API of my package.
    In this API, the user actually enters Application_id and data. API should insert/update the version_master table with the inputs. See application_id is the foriegn key for app_master table.
    The requirement is whenever they enters application_id and data, if application_id already present in the version_master table, then the data should be updated and we should get a status flag for this using Out variable(i.e. using ref cursor only we are showing the status). if the application_id is not present in the version_master table, new record should be inserted. The sequence will generate the version_id for version_master table.
    But the Merge statement is working fine for update and insert also. Problem is am unable to see the success or failure through ref cursor. I don't know how exactly I can use this.
    If data is NULL the operation should be failed. i.e. I should get Failure status here. But am not getting this.
    Please remove the comments here and then check. If I use the NVL2 function I was able to get the status flag, i.e. S or F.
    OPEN resultset_o FOR
    SELECT NVL2 (data, 'S', 'F')
    FROM version_master
    WHERE application_id = application_id_i;
    1.How the value of data being not null will determine Success of the api and how null failure
    2.If the above select statement goes in to exception when others how I will no the failure
    I have to achieve the above scenarios.
    Please advice me.

  • Unable to insert the record to table using pre-insert & pre-update trigger

    Hi All,
    I have tried to insert and update the backend table using the pre-update and pre-insert triggers. But its not working for me. Please find below the code which i have used in the triggers.
    Pre-insert trigger:
    DECLARE
    v_cust_num_cnt NUMBER;
    BEGIN
              SELECT      COUNT(customer_number)
              INTO      v_cust_num_cnt
              FROM cmw_bc_mobile_number
              WHERE substr(customer_number,1,15)=substr(:BLOCKNAME.CUSTOMER_NUMBER,1,15);
              IF      v_cust_num_cnt = 0 THEN
                        INSERT INTO cmw_bc_mobile_number (CUSTOMER_NUMBER
                                                                                                   ,MOBILE_NUMBER
    ,CREATION_DATE
    VALUES
    (substr(:BLOCKNAME.CUSTOMER_NUMBER,1,15)
    ,:BLOCKNAME.MOBILE_NUMBER
    ,SYSDATE
    COMMIT;                                                                                                    
    END IF;          
    END;
    PRE_UPDATE TRIGGER:
    BEGIN
              IF :SYSTEM.RECORD_STATUS = 'CHANGED' THEN
              UPDATE apps.cmw_bc_mobile_number
         SET mobile_number = :BLOCKNAME.MOBILE_NUMBER,
         creation_date = SYSDATE
              WHERE customer_number=substr(:BLOCKNAME.CUSTOMER_NUMBER,1,15);
              COMMIT;
         END IF;
    EXCEPTION
              WHEN OTHERS THEN
                   NULL;     
    END;
    Please let someone assist in gettting it resolved.
    Regards,
    Raj.

    Just use MESSAGE (we don't know what fnd_message is, that is some custom code):
    message('v_cust_num_cnt='||v_cust_num_cnt);
    IF v_cust_num_cnt = 0 THEN
      message('Now inserting...');
      INSERT INTO cmw_bc_mobile_number (CUSTOMER_NUMBER... 
    else
      message('Nothing to insert');
    end if;

  • How to audit the table when updated. The table has 100 Columns.

    Hi,
    I have a requirement to keep the log information of a table whenever there is an update happening on any of the columns available in a table, which has roughly 100 columns. I know we can use :NEW and :OLD specifier using trigger, but the no of columns are 100 + , here shall we fulfill the requirement. Kindly suggest if you have any better way to handle it.
    Regards
    Suresh

    You can use XMLTYPE for this. See an example below:
    First creating some test data:
    SQL> create table test_06302011
      2  (
      3    a1 number(10)
      4  , a2 number(10)
      5  , a3 number(10)
      6  , a4 number(10)
      7  , a5 number(10)
      8  , a6 number(10)
      9  )
    10  /
    Table created.
    SQL> insert into test_06302011
      2  values(1,1,1,1,1,1)
      3  /
    1 row created.
    SQL> insert into test_06302011
      2  values(2,2,2,2,2,2)
      3  /
    1 row created.
    SQL> insert into test_06302011
      2  values(3,3,3,3,3,3)
      3  /
    1 row created.
    SQL> insert into test_06302011
      2  values(4,4,4,4,4,4)
      3  /
    1 row created.
    SQL> insert into test_06302011
      2  values(5,5,5,5,5,5)
      3  /
    1 row created.
    SQL> insert into test_06302011
      2  values(6,6,6,6,6,6)
      3  /
    1 row created.
    SQL> commit
      2  /
    Commit complete.
    SQL> create table log_06302011
      2  (
      3    log_id     number(10) primary key
      4  , log_date   date
      5  , log_action varchar2(1)
      6  , log_old    xmltype
      7  , log_new    xmltype
      8  )
      9  /
    Table created.Then creating the logging trigger:
    SQL> CREATE OR REPLACE TRIGGER trigger_log_06302011
      2     AFTER UPDATE OR INSERT OR DELETE ON test_06302011
      3     FOR EACH ROW
      4  DECLARE
      5      vlog_event VARCHAR2(1);
      6      v_log_new  XMLTYPE;
      7      v_log_old  XMLTYPE;
      8  BEGIN
      9
    10    IF INSERTING THEN
    11        vlog_event := 'I';
    12        select XMLELEMENT("test_06302011",
    13                        XMLFOREST(
    14                            :new.a1 as "a1"
    15                          , :new.a2 as "a2"
    16                          , :new.a3 as "a3"
    17                          , :new.a4 as "a4"
    18                          , :new.a5 as "a5"
    19                          , :new.a6 as "a6")
    20                   )
    21           into v_log_new
    22           from dual;
    23
    24         v_log_old := NULL;
    25
    26    END IF;
    27
    28    IF UPDATING THEN
    29        vlog_event := 'U';
    30        select XMLELEMENT("test_06302011",
    31                        XMLFOREST(
    32                            :new.a1 as "a1"
    33                          , :new.a2 as "a2"
    34                          , :new.a3 as "a3"
    35                          , :new.a4 as "a4"
    36                          , :new.a5 as "a5"
    37                          , :new.a6 as "a6")
    38                   )
    39           into v_log_new
    40           from dual;
    41
    42         select XMLELEMENT("test_06302011",
    43                        XMLFOREST(
    44                                                           :old.a1 as "a1"
    45                                                         , :old.a2 as "a2"
    46                                                         , :old.a3 as "a3"
    47                                                         , :old.a4 as "a4"
    48                                                         , :old.a5 as "a5"
    49                                                         , :old.a6 as "a6")
    50                   )
    51          into v_log_old
    52          from dual;
    53
    54    END IF;
    55
    56    IF DELETING THEN
    57        vlog_event := 'D';
    58        v_log_new := NULL;
    59
    60         select XMLELEMENT("test_06302011",
    61                        XMLFOREST(
    62                                                           :old.a1 as "a1"
    63                                                         , :old.a2 as "a2"
    64                                                         , :old.a3 as "a3"
    65                                                         , :old.a4 as "a4"
    66                                                         , :old.a5 as "a5"
    67                                                         , :old.a6 as "a6")
    68                   )
    69          into v_log_old
    70          from dual;
    71
    72    END IF;
    73
    74     INSERT INTO log_06302011 (log_id, log_date, log_action, log_old, log_new
    75     VALUES ((SELECT NVL(MAX(log_06302011.log_id), 0) + 1 FROM log_06302011)
    76          , SYSDATE
    77          , vlog_event
    78          , v_log_old
    79          , v_log_new
    80           );
    81
    82  END trigger_log_06302011;
    83  /
    Trigger created.Now test:
    SQL> update test_06302011
      2     set a2 = 99
      3   where a1 = 1
      4  /
    1 row updated.
    SQL> commit
      2  /
    Commit complete.
    SQL> column log_old format a30
    SQL> column log_new format a30
    SQL> set linesize 300
    SQL> select log_old, log_new from log_06302011;
    LOG_OLD                        LOG_NEW
    <test_06302011><a1>1</a1><a2>1 <test_06302011><a1>1</a1><a2>9
    </a2><a3>1</a3><a4>1</a4><a5>1 9</a2><a3>1</a3><a4>1</a4><a5>
    </a5><a6>1</a6></tes           1</a5><a6>1</a6></te
    SQL>

  • How can I access the same table within AFTER UPDATE trigger?  mutating

    I'm trying to have processing done within a before or after update trigger.
    reduced what I want to do down to a select within the trigger.
    create or replace TRIGGER "AU_TABLE_A"
    after update on "TABLE_A"
    for each row
    declare
    l_res_status NUMBER:=0;
    l_count NUMBER:=0;
    begin
    SELECT COUNT(*) INTO l_count
    FROM TABLE_A
    WHERE ID = :NEW.ID AND STATUS_ID = 9;
    end;
    Error in mru internal routine: ORA-20001: Error in MRU: row= 1,
         ORA-04091: table TABLE_A is mutating, trigger/function may not see it
         ORA-06512: at "BHRS.BU_MR_RES_APPR", line 13
         ORA-04088: error during execution of trigger 'BHRS.BU_MR_RES_APPR',
         update TABLE_A set "ID" = :b1, "STATUS_ID" = :b2, "COMMENTS" = :b3
         where "ID" = :p_pk_col
    IS THERE A WAY AROUND IT?
    Thank you, Bill

    The very common approach to workaround mutating table problem is passing the table state through the package variables. I.e. - you create a package with one global variable ( say l_count). Then you need
    1) a trigger before update for statement - here you counts your rows and set the package variable accordingly.
    2) a trigger after update for each row - here you read the rowcount from a package variable and do your actual work
    3) a trigger after update for statement - here you reset your package variable.
    This approach uses the fact, that mutating table problem occurs only with triggers for each row and the order of execution of different trigger types.
    However, counting records in every update statement (whereas not so worse although as doing this for each row within an update) may consume a lot of resources, so, possibly , you can rethink your business needs/approach.
    Best regards
    Maxim

  • Table Update Trigger

    Hi,
    please help me in writing a trigger after update on a table?
    Scenario:
    if any update is done on Location_Lat or Location_Lon or both fields then the Geom_Loc should be updated through a trigger. and if there is any update of the lat and lon values inside the sdo_geometry type field SDO_GEOM then respectively the Location_Lat and Location_Lon should be updated
    We have 3 important fields in a table.
    1) Location_Lat -------------- decimal number ( Geographic Location GIS)
    2) Location_Lon------------- decimal number (Geographic Location GIS)
    3) Geom_Loc----------------SDO_Geometry (Oracle Type for the oracle Spatial (your database should be Spatial Enabled)
    Example of SDO_Geometry:-
    (here latitude=23 and longitude=25)
    SDO_GEOMETRY(3001,8307,null,sdo_elem_info_array(1,1,1,3,1,0),sdo_ordinate_array(23,25,0,0,0,0))
    Thanking you in Advance,
    Rashed

    Hi,
    I am very much new to Oracle and PL/SQL, and i dont have any programming experience, i am facing difficulty in writing a logic for these kind of triggers. I wrote this piece of code but it is giving an error
    CREATE OR REPLACE TRIGGER TRG_UPD_TBL_SITE
    BEFORE UPDATE
    OF Location_Lat,Location_Lon,Geom_Loc
    ON TBL_SITE
    REFERENCING NEW AS NEW OLD AS OLD
    FOR EACH ROW
    DECLARE
    pLocation_Lat NUMBER;
    pLocation_Lon NUMBER;
    BEGIN
    pLocation_Lat :=:new.Location_Lat;
    pLocation_Lon :=:new.Location_Lon;
    -- Example of SDO_Geometry update---------UPDATE TBL_SITE SET TBL_SITE.Geom_Loc=SDO_GEOMETRY(3001,8307,null,sdo_elem_info_array(1,1,1,3,1,0),sdo_ordinate_array(99.99,77.77,0,0,0,0)),TBL_SITE.Location_Lat=99.99,TBL_SITE.Location_Lon=77.77 where TBL_SITE.SITE_ID=21;
    UPDATE TBL_SITE
    SET TBL_SITE.Geom_Loc=SDO_GEOMETRY(3001,8307,null,sdo_elem_info_array(1,1,1,3,1,0),sdo_ordinate_array(pLocation_Lat,pLocation_Lon,0,0,0,0)),TBL_SITE.Location_Lat=new.Location_Lat,TBL_SITE.Location_Lon=new.Location_Lon
    where TBL_SITE.SITE_ID=old.SITE_ID
    END TRG_UPD_TBL_SITE;
    Thanks

  • Update trigger - Updating only updated columns

    Hi All,
    I need a trigger to be created on table VNDR(with so many columns, only a few are shown in below script) after insert or update.
    It will insert or update the same data in a table having similar structure in another DB.
    For update part, updating each column value even if a single column is updated would not be the better way.
    please suggest any better optimized way.
    CREATE OR REPLACE TRIGGER TRG_VNDR
       AFTER INSERT OR UPDATE
       ON VNDR
       REFERENCING NEW AS NEW OLD AS OLD
       FOR EACH ROW
    BEGIN
          IF INSERTING
          THEN
             INSERT INTO VNDR@MMMM (VNDR_ID,
                                         VEND_TYP,
                                         CURRENCY_CD,
                                         VNDR_AREA_CD,
                                         DA_FLG,
                                         CREATE_DATE,
                                         UPDATE_DATE,
                                         DELETE_FLG,
                                         DELETE_DATE,
                                         UPDATE_DSCR,
                                         VNDR_CNAME,
                                         VNDR_CABB,
                                         VNDR_CADDRS,
                                         VNDR_EADDRS2,
                                         VNDR_EADDRS5,
                                         VNDR_EADDRS3,
                                         VNDR_STATE,
                                         VNDR_EADDRS4,
                                         VNDR_CNTR,
                                         VNDR_HNDL,
                                         VNDR_TEL)
                  VALUES (:NEW.VNDR_ID,
                          :NEW.VEND_TYP,
                          :NEW.CURRENCY_CD,
                          :NEW.VNDR_AREA_CD,
                          :NEW.DA_FLG,
                          SYSDATE,
                          :NEW.UPDATE_DATE,
                          :NEW.DELETE_FLG,
                          :NEW.DELETE_DATE,
                          :NEW.UPDATE_DSCR,
                          :NEW.VNDR_CNAME,
                          :NEW.VNDR_CABB,
                          :NEW.VNDR_CADDRS,
                          :NEW.VNDR_EADDRS2,
                          :NEW.VNDR_EADDRS5,
                          :NEW.VNDR_EADDRS3,
                          :NEW.VNDR_STATE,
                          :NEW.VNDR_EADDRS4,
                          :NEW.VNDR_CNTR,
                          :NEW.VNDR_HNDL,
                          :NEW.VNDR_TEL);
          ELSIF UPDATING
          THEN
             UPDATE SMGR.VNDR@MMSL
                SET VEND_TYP = :NEW.VEND_TYP,
                    CURRENCY_CD = :NEW.CURRENCY_CD,
                    VNDR_AREA_CD = :NEW.VNDR_AREA_CD,
                    DA_FLG = :NEW.DA_FLG,
                    CREATE_DATE = :NEW.CREATE_DATE,
                    UPDATE_DATE = SYSDATE,
                    DELETE_FLG = :NEW.DELETE_FLG,
                    DELETE_DATE = :NEW.DELETE_DATE,
                    UPDATE_DSCR = :NEW.UPDATE_DSCR,
                    VNDR_CNAME = :NEW.VNDR_CNAME,
                    VNDR_CABB = :NEW.VNDR_CABB,
                    VNDR_CADDRS = :NEW.VNDR_CADDRS,
                    VNDR_EADDRS2 = :NEW.VNDR_EADDRS2,
                    VNDR_EADDRS5 = :NEW.VNDR_EADDRS5,
                    VNDR_EADDRS3 = :NEW.VNDR_EADDRS3,
                    VNDR_STATE = :NEW.VNDR_STATE,
                    VNDR_EADDRS4 = :NEW.VNDR_EADDRS4,
                    VNDR_CNTR = :NEW.VNDR_CNTR,
                    VNDR_HNDL = :NEW.VNDR_HNDL,
                    VNDR_TEL = :NEW.VNDR_TEL
              WHERE VNDR_ID = :NEW.VNDR_ID;
          END IF;
    END;

    Assuming, you want only a particular column value to get updated below scenarios which could occur.
    1) You will insert null values for all other columns.
    2) You will update already existing row with the new value.
    If you go with Option 1, there remains no way to audit and identify which record actually got changed.
    If you go with Option 2, there is no use of Trigger itself.
    On a second thought, you are using trigger to update some other table. Why would you require that? Triggers are normally meant for auditing purposes and to retain history, but if you are updating another similar table with similar values, why can't you update this table directly without the trigger?
    I don't see any reason to believe that only one particular columns should get updated. IMHO, triggers should ideally be used only for INSERTING.
    Ishan

  • Query based on main table and audit table

    Hi,
    I had created auditing on some table. Values might not change and if they changed, it should be stored in audit table.
    I want to get the values in the table a on real time basis, like dimentions in datawarehouse.
    Trying to write a query based on table a and aud_a to get point-in-time or values at anytime in the past.
    Something like
    SELECT *
    FROM a (table_name)
    WHERE effective_from >= $DATE_TO_QUERY
    AND effective_to < $DATE_TO_QUERY
    How to get this kind of query .
    Please help. ( Table structure for table a and audit table aud_a and trigger aud_tg_a given below)
    Giving code as follows.
    main table a
    create table a
    ( val1 number,
    val2 number,
    update_by varchar2(30),
    date_updated date);
    creare auidt table aud_a
    create table aud_a
    ( "AUDIT_SEQ" NUMBER,
    "AUDIT_TRAN_ID" NUMBER,
    "AUDIT_PROG_ID" VARCHAR2(30 BYTE),
    "AUDIT_TERMINAL" VARCHAR2(16 BYTE),
    "AUDIT_REASON" VARCHAR2(30 BYTE),
    "AUDIT_ACTION" CHAR(1 BYTE),
    "AUDIT_ACTION_BY" VARCHAR2(20 BYTE),
    "AUDIT_ACTION_DT" DATE,
    val1 number,
    val2 number,
    updated_by varchar2(30),
    date_updated date);
    trigger on  table a to populate aud_a
    CREATE OR REPLACE TRIGGER aud_tg_a AFTER
    INSERT OR
    DELETE OR
    update on a
    for each row
    declare
    v_time_now DATE;
    v_terminal VARCHAR2(16);
    v_tran_id NUMBER;
    v_prog_id VARCHAR2(30);
    V_reason VARCHAR2(30);
    BEGIN
    v_time_now := sysdate;
    v_terminal := userenv('TERMINAL');
    v_tran_id := 1;
    v_prog_id := 'test';
    v_reason := 'AUDIT';
    IF inserting THEN
    INSERT
    INTO a
    audit_seq,
    AUDIT_tran_id,
    AUDIT_prog_id,
    AUDIT_reason,
    AUDIT_terminal,
    AUDIT_action_by,
    AUDIT_action_dt,
    AUDIT_action ,
    val1,
    val2,
    updated_by,
    date_updated
    VALUES
    s_audit_no.nextval,
    v_tran_id,
    v_prog_id,
    v_reason,
    v_terminal,
    USER,
    v_time_now,
    'I' ,
    :new.val1,
    :new.val2,
    :new.updated_by,
    :new.date_updated
    elsif deleting THEN
    INSERT
    INTO a
    audit_seq,
    AUDIT_tran_id,
    AUDIT_prog_id,
    AUDIT_reason,
    AUDIT_terminal,
    AUDIT_action_by,
    AUDIT_action_dt,
    AUDIT_action ,
    us_agy_backed_id,
    industry_subgroup,
    comments,
    updated_by,
    date_updated
    VALUES
    s_audit_no.nextval,
    v_tran_id,
    v_prog_id,
    v_reason,
    v_terminal,
    USER,
    v_time_now,
    'D' ,
    :old.val1,
    :old.val2,
    :old.comments,
    :old.updated_by,
    :old.date_updated
    elsif updating THEN
    INSERT
    INTO a
    audit_seq,
    AUDIT_tran_id,
    AUDIT_prog_id,
    AUDIT_reason,
    AUDIT_terminal,
    AUDIT_action_by,
    AUDIT_action_dt,
    AUDIT_action ,
    us_agy_backed_id,
    industry_subgroup,
    comments,
    updated_by,
    date_updated
    VALUES
    s_audit_no.nextval,
    v_tran_id,
    v_prog_id,
    v_reason,
    v_terminal,
    USER,
    v_time_now,
    'U' ,
    :new.val1,
    :new.val2,
    :new.updated_by,
    :new.date_updated
    END IF;
    END;
    -------------------------

    Hi hoek,
    I am not able to use Oracle's audit functionality becuase I need to trap some changes in particular tables and then rebuild query if required.
    Thanks for your suggestion though.
    Regards,
    Milind

Maybe you are looking for