<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
		>
<channel>
	<title>Comments on: Monitoring Changes to Table Data</title>
	<atom:link href="http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/feed/" rel="self" type="application/rss+xml" />
	<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/</link>
	<description>Miscellaneous Random Oracle Topics: Stop, Think, ... Understand</description>
	<lastBuildDate>Mon, 13 May 2013 14:10:06 +0000</lastBuildDate>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
	<item>
		<title>By: damirvadas</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4656</link>
		<dc:creator><![CDATA[damirvadas]]></dc:creator>
		<pubDate>Tue, 24 Apr 2012 15:55:58 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4656</guid>
		<description><![CDATA[Charles,

Thank you ... will take a look
Rg,
Damir]]></description>
		<content:encoded><![CDATA[<p>Charles,</p>
<p>Thank you &#8230; will take a look<br />
Rg,<br />
Damir</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4655</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Tue, 24 Apr 2012 15:08:34 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4655</guid>
		<description><![CDATA[Damir,

I am not sure that I understand your question.  My VBS script and EXE referenced above allow the user to select which columns of a table to log, and which columns that undergo changes will trigger the logging.  The existing and the new values may be captured in the same logging table row.  If you capture the existing and the new values, you can determine how the value has changed, and you can determine how the row changed with the assistance of the LOG_TRANSACTION_TYPE column in the logging table.]]></description>
		<content:encoded><![CDATA[<p>Damir,</p>
<p>I am not sure that I understand your question.  My VBS script and EXE referenced above allow the user to select which columns of a table to log, and which columns that undergo changes will trigger the logging.  The existing and the new values may be captured in the same logging table row.  If you capture the existing and the new values, you can determine how the value has changed, and you can determine how the row changed with the assistance of the LOG_TRANSACTION_TYPE column in the logging table.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4654</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Tue, 24 Apr 2012 15:02:04 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4654</guid>
		<description><![CDATA[Damir,

A Windows VBS/Internet Explorer version:
http://hoopercharles.wordpress.com/2010/08/25/oracle-logging-trigger-creator/

An executable version that is part of a much larger project:
http://hoopercharles.wordpress.com/2012/03/15/hyper-extended-oracle-performance-monitor-6-0-beta/]]></description>
		<content:encoded><![CDATA[<p>Damir,</p>
<p>A Windows VBS/Internet Explorer version:<br />
<a href="http://hoopercharles.wordpress.com/2010/08/25/oracle-logging-trigger-creator/" rel="nofollow">http://hoopercharles.wordpress.com/2010/08/25/oracle-logging-trigger-creator/</a></p>
<p>An executable version that is part of a much larger project:<br />
<a href="http://hoopercharles.wordpress.com/2012/03/15/hyper-extended-oracle-performance-monitor-6-0-beta/" rel="nofollow">http://hoopercharles.wordpress.com/2012/03/15/hyper-extended-oracle-performance-monitor-6-0-beta/</a></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: damirvadas</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4653</link>
		<dc:creator><![CDATA[damirvadas]]></dc:creator>
		<pubDate>Tue, 24 Apr 2012 14:45:27 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4653</guid>
		<description><![CDATA[Hi!

I look in your example and I do not see how easy to see only changes? Not whole rows?
Did I missed something?
Rg
Damir]]></description>
		<content:encoded><![CDATA[<p>Hi!</p>
<p>I look in your example and I do not see how easy to see only changes? Not whole rows?<br />
Did I missed something?<br />
Rg<br />
Damir</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: damirvadas</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4652</link>
		<dc:creator><![CDATA[damirvadas]]></dc:creator>
		<pubDate>Tue, 24 Apr 2012 14:42:37 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4652</guid>
		<description><![CDATA[Charles,

This program, that you mention, is this public or private stuff ...
Any demo/download?

Rg
Damir]]></description>
		<content:encoded><![CDATA[<p>Charles,</p>
<p>This program, that you mention, is this public or private stuff &#8230;<br />
Any demo/download?</p>
<p>Rg<br />
Damir</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: hourim</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4587</link>
		<dc:creator><![CDATA[hourim]]></dc:creator>
		<pubDate>Sat, 24 Mar 2012 12:13:01 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4587</guid>
		<description><![CDATA[Charles,

What I have implemented in several real life applications is a little bit different from the example I mentioned in my blog. In these applications the table to be audited (emp here) contains the columns dml_usr, dml_dat, dml_pgm so that the historical table (emp_history here) doesn&#039;t contain any trigger. As such there is no trigger at all in the historical table.

Yes, your suggestion to use DEFAULT values could be envisaged and might give the same results. 

As per regards to performance, I can assure you this audit history has never caused us any noticeable performance problem. In addition I can tell you that what was initially implemented as an audit task (to maintain a change history), you could not imagine, how it reveals itself as a wonderful tool for debugging and understanding some bugs in PRODUCTION, just by looking at those dml_usr, dml_dat, dml_pgm (which in reality have been implemented as user_ins, dat_ins, pgm_ins and user_upd, dat_upd, pgm_upd)]]></description>
		<content:encoded><![CDATA[<p>Charles,</p>
<p>What I have implemented in several real life applications is a little bit different from the example I mentioned in my blog. In these applications the table to be audited (emp here) contains the columns dml_usr, dml_dat, dml_pgm so that the historical table (emp_history here) doesn&#8217;t contain any trigger. As such there is no trigger at all in the historical table.</p>
<p>Yes, your suggestion to use DEFAULT values could be envisaged and might give the same results. </p>
<p>As per regards to performance, I can assure you this audit history has never caused us any noticeable performance problem. In addition I can tell you that what was initially implemented as an audit task (to maintain a change history), you could not imagine, how it reveals itself as a wonderful tool for debugging and understanding some bugs in PRODUCTION, just by looking at those dml_usr, dml_dat, dml_pgm (which in reality have been implemented as user_ins, dat_ins, pgm_ins and user_upd, dat_upd, pgm_upd)</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4586</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sat, 24 Mar 2012 01:06:48 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4586</guid>
		<description><![CDATA[You are correct that the OP was using Microsoft SQL Server - he was previously running Oracle Database but switched to SQL Server to save money.

My comment was intended to state that while I probably could not write a trigger in SQL Server (or Sybase) I could understand what your trigger was accomplishing (even though the trigger syntax appears a bit different than that used by Oracle).  I used to be able to say the same about Fortran, COBOL, ARREX and a couple of other programming languages, but those languages are just distant now memories.]]></description>
		<content:encoded><![CDATA[<p>You are correct that the OP was using Microsoft SQL Server &#8211; he was previously running Oracle Database but switched to SQL Server to save money.</p>
<p>My comment was intended to state that while I probably could not write a trigger in SQL Server (or Sybase) I could understand what your trigger was accomplishing (even though the trigger syntax appears a bit different than that used by Oracle).  I used to be able to say the same about Fortran, COBOL, ARREX and a couple of other programming languages, but those languages are just distant now memories.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4585</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sat, 24 Mar 2012 00:54:33 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4585</guid>
		<description><![CDATA[Marcus,

Nice ideas.  &quot;Continuous Query Notification&quot; is a feature that I was unaware of - probably a sign that I need to stop reading/reviewing books, and instead read the official Oracle Database documentation instead.  :-)

&quot;Continuous Query Notification&quot;:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25518/adfns_cqn.htm]]></description>
		<content:encoded><![CDATA[<p>Marcus,</p>
<p>Nice ideas.  &#8220;Continuous Query Notification&#8221; is a feature that I was unaware of &#8211; probably a sign that I need to stop reading/reviewing books, and instead read the official Oracle Database documentation instead.  <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>&#8220;Continuous Query Notification&#8221;:<br />
<a href="http://docs.oracle.com/cd/E11882_01/appdev.112/e25518/adfns_cqn.htm" rel="nofollow">http://docs.oracle.com/cd/E11882_01/appdev.112/e25518/adfns_cqn.htm</a></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Charles Hooper</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4584</link>
		<dc:creator><![CDATA[Charles Hooper]]></dc:creator>
		<pubDate>Sat, 24 Mar 2012 00:46:44 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4584</guid>
		<description><![CDATA[Mohamed,

I just read your article - nice, and you covered the topic a week before it was mentioned here.  

For your emp_history table, would you grant to PUBLIC just INSERT and not SELECT, UPDATE, and DELETE to minimize the chances of the problems mentioned by Pete.  Or possibly call a definer&#039;s rights (http://docs.oracle.com/cd/E11882_01/timesten.112/e21639/accesscntl.htm#BABDDCHC ) procedure within the trigger code to perform the insert into your _HISTORY tables?  

I see that your method makes use of two table triggers - does that present more of a performance overhead than a single trigger?

Two of your _HISTORY columns are defined as follows:
&lt;pre&gt;
dml_usr varchar2(48),
dml_dat date 
&lt;/pre&gt;

I would probably create those two columns with the following definitions so that I would not need to populate those columns in a trigger (that is not to say that this method is better than your method):
&lt;pre&gt;
dml_usr varchar2(30) DEFAULT USER,
dml_dat date DEFAULT SYSDATE
&lt;/pre&gt;
I have not experimented yet, but could a DEFAULT value also be declared for your lv_module column?]]></description>
		<content:encoded><![CDATA[<p>Mohamed,</p>
<p>I just read your article &#8211; nice, and you covered the topic a week before it was mentioned here.  </p>
<p>For your emp_history table, would you grant to PUBLIC just INSERT and not SELECT, UPDATE, and DELETE to minimize the chances of the problems mentioned by Pete.  Or possibly call a definer&#8217;s rights (<a href="http://docs.oracle.com/cd/E11882_01/timesten.112/e21639/accesscntl.htm#BABDDCHC" rel="nofollow">http://docs.oracle.com/cd/E11882_01/timesten.112/e21639/accesscntl.htm#BABDDCHC</a> ) procedure within the trigger code to perform the insert into your _HISTORY tables?  </p>
<p>I see that your method makes use of two table triggers &#8211; does that present more of a performance overhead than a single trigger?</p>
<p>Two of your _HISTORY columns are defined as follows:</p>
<pre>
dml_usr varchar2(48),
dml_dat date 
</pre>
<p>I would probably create those two columns with the following definitions so that I would not need to populate those columns in a trigger (that is not to say that this method is better than your method):</p>
<pre>
dml_usr varchar2(30) DEFAULT USER,
dml_dat date DEFAULT SYSDATE
</pre>
<p>I have not experimented yet, but could a DEFAULT value also be declared for your lv_module column?</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: talebzadeh</title>
		<link>http://hoopercharles.wordpress.com/2012/03/22/monitoring-changes-to-table-data/#comment-4583</link>
		<dc:creator><![CDATA[talebzadeh]]></dc:creator>
		<pubDate>Fri, 23 Mar 2012 22:42:38 +0000</pubDate>
		<guid isPermaLink="false">http://hoopercharles.wordpress.com/?p=6198#comment-4583</guid>
		<description><![CDATA[The same code in Oracle
&lt;pre&gt;
drop sequence identity;
CREATE SEQUENCE identity
    MINVALUE 1
    MAXVALUE 999999999999999999999999999
    START WITH 1
    INCREMENT BY 1
    CACHE  1000;
drop table source;
create table source
(
           col1 number not null
          ,col2 varchar(30) not null
);
alter table source add constraint source_pk primary key (col1);
desc source
drop table source_audit;
create table source_audit
(
           col1 number not null
          ,col2 varchar(30) not null
          ,who  varchar(30) not null
          ,application varchar(30) null
          ,hostname varchar(30) null
          ,when_changed  date not null
          ,action char(1) not null
);
desc source_audit
create or replace trigger source_b_ud_tr
before update or delete
on source
for each row
declare
  v_who varchar(30) := USER;
  v_application varchar(30);
  v_hostname varchar(30);
  v_when_changed date := sysdate;
  v_action char(1);
begin
  SELECT
            substr(sys_context(&#039;USERENV&#039;,&#039;HOST&#039;),1,30)
          , substr(sys_context(&#039;USERENV&#039;,&#039;MODULE&#039;),1,30)
  INTO    v_hostname
         ,v_application
FROM
        dual;
  if updating then
    v_action := &#039;U&#039;;
  elsif deleting then
    v_action := &#039;D&#039;;
  end if;
  insert into source_audit
  (
          col1
         ,col2
         ,who
         ,application
         ,hostname
         ,when_changed
         ,action
  )
  values
  (
          :old.col1
         ,:old.col2
         ,v_who
         ,v_application
         ,v_hostname
         ,v_when_changed
         ,v_action
  );
end source_b_ud_tr;
/
show error
create or replace trigger source_a_iu_tr
after insert or update
on source
for each row
declare
  v_who varchar(30) := USER;
  v_application varchar(30);
  v_hostname varchar(30);
  v_when_changed date := sysdate;
  v_action char(1);
begin
  SELECT
            substr(sys_context(&#039;USERENV&#039;,&#039;HOST&#039;),1,30)
          , substr(sys_context(&#039;USERENV&#039;,&#039;MODULE&#039;),1,30)
  INTO    v_hostname
         ,v_application
FROM
        dual;
  if inserting then
    v_action := &#039;I&#039;;
  elsif updating then
    v_action := &#039;U&#039;;
  end if;
  insert into source_audit
  (
          col1
         ,col2
         ,who
         ,application
         ,hostname
         ,when_changed
         ,action
  )
  values
  (
          :new.col1
         ,:new.col2
         ,v_who
         ,v_application
         ,v_hostname
         ,v_when_changed
         ,v_action
  );
end source_a_iud_tr;
/
show error
exit
&lt;/pre&gt;

Well it works. Let us try it
&lt;pre&gt;
insert into source values (identity.nextval,&#039;LOndon&#039;);

1 row created.

update source set col2 = &#039;London&#039;;

1 row updated.

insert into source values (identity.nextval,&#039;NY&#039;);

1 row created.

insert into source values (identity.nextval,&#039;LA&#039;);

1 row created.

delete from source where col2 = &#039;LA&#039;;

1 row deleted.

select * from source_audit;

      COL1 COL2                           WHO                            APPLICATION
---------- ------------------------------ ------------------------------ ------------------------------
HOSTNAME                       WHEN_CHANGED        A
------------------------------ ------------------- -
         2 LOndon                         SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:52:26 I

         2 LOndon                         SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:52:50 U

         2 London                         SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:52:50 U

         3 NY                             SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:53:30 I

         4 LA                             SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:53:39 I

         4 LA                             SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:54:24 D


6 rows selected.
&lt;/pre&gt;
I am sure you guys can write better code than myself so please suggest where it can be bettered.

Mich]]></description>
		<content:encoded><![CDATA[<p>The same code in Oracle</p>
<pre>
drop sequence identity;
CREATE SEQUENCE identity
    MINVALUE 1
    MAXVALUE 999999999999999999999999999
    START WITH 1
    INCREMENT BY 1
    CACHE  1000;
drop table source;
create table source
(
           col1 number not null
          ,col2 varchar(30) not null
);
alter table source add constraint source_pk primary key (col1);
desc source
drop table source_audit;
create table source_audit
(
           col1 number not null
          ,col2 varchar(30) not null
          ,who  varchar(30) not null
          ,application varchar(30) null
          ,hostname varchar(30) null
          ,when_changed  date not null
          ,action char(1) not null
);
desc source_audit
create or replace trigger source_b_ud_tr
before update or delete
on source
for each row
declare
  v_who varchar(30) := USER;
  v_application varchar(30);
  v_hostname varchar(30);
  v_when_changed date := sysdate;
  v_action char(1);
begin
  SELECT
            substr(sys_context('USERENV','HOST'),1,30)
          , substr(sys_context('USERENV','MODULE'),1,30)
  INTO    v_hostname
         ,v_application
FROM
        dual;
  if updating then
    v_action := 'U';
  elsif deleting then
    v_action := 'D';
  end if;
  insert into source_audit
  (
          col1
         ,col2
         ,who
         ,application
         ,hostname
         ,when_changed
         ,action
  )
  values
  (
          <img src='http://s1.wp.com/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> ld.col1
         ,:old.col2
         ,v_who
         ,v_application
         ,v_hostname
         ,v_when_changed
         ,v_action
  );
end source_b_ud_tr;
/
show error
create or replace trigger source_a_iu_tr
after insert or update
on source
for each row
declare
  v_who varchar(30) := USER;
  v_application varchar(30);
  v_hostname varchar(30);
  v_when_changed date := sysdate;
  v_action char(1);
begin
  SELECT
            substr(sys_context('USERENV','HOST'),1,30)
          , substr(sys_context('USERENV','MODULE'),1,30)
  INTO    v_hostname
         ,v_application
FROM
        dual;
  if inserting then
    v_action := 'I';
  elsif updating then
    v_action := 'U';
  end if;
  insert into source_audit
  (
          col1
         ,col2
         ,who
         ,application
         ,hostname
         ,when_changed
         ,action
  )
  values
  (
          :new.col1
         ,:new.col2
         ,v_who
         ,v_application
         ,v_hostname
         ,v_when_changed
         ,v_action
  );
end source_a_iud_tr;
/
show error
exit
</pre>
<p>Well it works. Let us try it</p>
<pre>
insert into source values (identity.nextval,'LOndon');

1 row created.

update source set col2 = 'London';

1 row updated.

insert into source values (identity.nextval,'NY');

1 row created.

insert into source values (identity.nextval,'LA');

1 row created.

delete from source where col2 = 'LA';

1 row deleted.

select * from source_audit;

      COL1 COL2                           WHO                            APPLICATION
---------- ------------------------------ ------------------------------ ------------------------------
HOSTNAME                       WHEN_CHANGED        A
------------------------------ ------------------- -
         2 LOndon                         SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:52:26 I

         2 LOndon                         SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:52:50 U

         2 London                         SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:52:50 U

         3 NY                             SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:53:30 I

         4 LA                             SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:53:39 I

         4 LA                             SCRATCHPAD                     SQL*Plus
rhes564                        23/03/2012 22:54:24 D


6 rows selected.
</pre>
<p>I am sure you guys can write better code than myself so please suggest where it can be bettered.</p>
<p>Mich</p>
]]></content:encoded>
	</item>
</channel>
</rss>
