<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1339993448594009052</id><updated>2012-02-15T22:21:55.005-08:00</updated><title type='text'>Triggers Considered Harmful, Considered Harmful</title><subtitle type='html'>Within the Oracle community, there is a general consensus that database triggers are to be considered harmful.

I find this general consensus harmful...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-4211159426408947527</id><published>2012-02-06T11:27:00.000-08:00</published><updated>2012-02-06T22:02:35.092-08:00</updated><title type='text'>Where TK agrees with TK (or: why are triggers harmful)</title><content type='html'>Sofar we've explored playing around with a few triggers to implement a business rule. In this post I'd like to step back a bit and take a birds-eye view at the common use-cases that I see brought up for introducing triggers into the app-development picture.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Vn7fzUSm0w0/TzAXAy3wJ2I/AAAAAAAAAGo/xRYD3G6ab-8/s1600/30_cus1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/-Vn7fzUSm0w0/TzAXAy3wJ2I/AAAAAAAAAGo/xRYD3G6ab-8/s400/30_cus1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The first use-case is: we use triggers &lt;i&gt;to assign or modify column values of mutating rows&lt;/i&gt; (that is, rows currently being inserted, updated, or deleted). Here's a few typical examples of this use case that, I'm sure, must look familiar to you too.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-0We8jFcebUc/TzAYpJSeK5I/AAAAAAAAAGw/0ZKcjDmD7ek/s1600/30_cus2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://4.bp.blogspot.com/-0We8jFcebUc/TzAYpJSeK5I/AAAAAAAAAGw/0ZKcjDmD7ek/s400/30_cus2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;For this use-case we always use a row-level trigger (for each row), as this type of trigger allows us to manipulate :new.[column-name] variables. We can re-assign supplied (by the trigger DML-statement) column values, or initialize ones that were not supplied. So for instance the use enters all column-values but the Created and By_User columns, which are initialized by a row-trigger we wrote.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;The second use-case is: we use triggers &lt;i&gt;to execute insert/update/delete statements&lt;/i&gt;. So we perform some DML against a table. This table has some trigger, in which we perform more DML.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-vk1BsInQ108/TzAbbAIV-hI/AAAAAAAAAG4/5hBINp7mJuQ/s1600/30_cus3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://1.bp.blogspot.com/-vk1BsInQ108/TzAbbAIV-hI/AAAAAAAAAG4/5hBINp7mJuQ/s400/30_cus3.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;Of course this can lead to a cascading effect: the DML that we perform from within our trigger body might be against a table that too has triggers. In which there might be more DML-statements performed, and so on and so forth. Typical examples of why we perform DML-statements inside triggers, are: we want to audit changes against the table, or we want to maintain redundant data stored elsewhere in the database design.&lt;br /&gt;&lt;br /&gt;The third use-case is: we use triggers &lt;i&gt;to execute non-transactional stuff&lt;/i&gt;. And by "non-transactional" I mean stuff that, once executed inside our transaction, does not participate in a rollback, in case we (the code-path) decides to perform one.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-fTUZrArPP-c/TzAdTG-o8jI/AAAAAAAAAHA/c0Dz1InTolk/s1600/30_cus4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://2.bp.blogspot.com/-fTUZrArPP-c/TzAdTG-o8jI/AAAAAAAAAHA/c0Dz1InTolk/s400/30_cus4.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The most beautiful example of the last decade for this is, we call a web-service inside our trigger body. In case this is a web-service that actually 'does something' (changes/transacts information at/behind the endpoint of the service), then obviously when we rollback our triggering DML-statement, whatever was changed by the web-service, remains changed. Other examples of this use-case are: we send email from our trigger body, or perform file I/O. We can also perform an autonomous transaction (one that actually performs some DML which given it's executed inside the autonomous TX, cannot be rolled back by the parent transaction). This is actually a blend of this use-case with the previous one.&lt;br /&gt;&lt;br /&gt;So what's harmful about all these use-cases? Well, they make stuff happen &lt;i&gt;automagically&lt;/i&gt; (copyright the other TK).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-tAgvALgwtMA/TzAfaxcOuTI/AAAAAAAAAHI/0y6A3Of9DDY/s1600/30_cus5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/-tAgvALgwtMA/TzAfaxcOuTI/AAAAAAAAAHI/0y6A3Of9DDY/s400/30_cus5.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;What we mean by this is, that instead of only getting the effect of the triggering DML-statement, you also get the effect of whatever is being performed inside the trigger body. And exactly that is the harmful bit.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-4FuXZdhjLkk/TzAiZDgrbSI/AAAAAAAAAHQ/qq2ytC3Nk5U/s1600/30_cus6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://3.bp.blogspot.com/-4FuXZdhjLkk/TzAiZDgrbSI/AAAAAAAAAHQ/qq2ytC3Nk5U/s400/30_cus6.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;In a SQL DBMS, the insert, update and delete 'operators', constitute three &lt;i&gt;primitive operators&lt;/i&gt; that we can use to manipulate the data held inside the database (tables). Primitive here is used in the sense of &lt;i&gt;primary&lt;/i&gt; or &lt;i&gt;basic&lt;/i&gt;. Sort of like Inc(rement) and Dec(rement) are primitives that can be used in machine language to increase or decrease a register's value. These primitive operators have well understood and universally known &lt;i&gt;change-semantics&lt;/i&gt;. Say you start with an empty EMP table, you then insert a employee row, you will end up with a change of the EMP table: it now has a single row in it. And all other bits and pieces of the database remain unchanged.&lt;br /&gt;&lt;br /&gt;Triggers executing automagic stuff change these universally well-understood change-semantics of our three primitive operators.&lt;br /&gt;&lt;br /&gt;And this is harmful: because nobody (after you've gone) expects insert, update or delete to quietly behave &amp;nbsp;differently. For those of you who have done some machine programming in a distant past: it's like increasing one of your registers, and suddenly (as an automagic side-effect) the top of the execution stack is increased also and has some subroutine's address pushed on it. Not something you'd expect...&lt;br /&gt;&lt;br /&gt;The TCO (total cost of ownership) of an application sits not in initially building the thing. It sits in the years, sometimes decades, the thing lives. And you, but more probably others, maintain it. And more often than not these developers expect their available primitive operators to follow the primitive change-semantics.&lt;br /&gt;&lt;br /&gt;I'll finish this post with the following thought:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-i8g5cQvYzTU/TzAngCYYM1I/AAAAAAAAAHY/mCFuhvfR3zU/s1600/30_cus7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://3.bp.blogspot.com/-i8g5cQvYzTU/TzAngCYYM1I/AAAAAAAAAHY/mCFuhvfR3zU/s400/30_cus7.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;If you agree on the three use-cases discussed being harmful, do you then also agree with me that:&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;The cascade delete option of a foreign key, and&lt;/li&gt;&lt;li&gt;The default clause of a table column, and&lt;/li&gt;&lt;li&gt;The char (fixed-length) datatype&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;are to be considered harmful too.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Aren't they conceptually about the exact same problem? Introducing different change-semantics to our three primitive operators?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'd be interested to hear of any other 'features' that change the way insert, update or delete behave.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-4211159426408947527?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/4211159426408947527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2012/02/where-tk-agrees-with-tk-or-why-are.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/4211159426408947527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/4211159426408947527'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2012/02/where-tk-agrees-with-tk-or-why-are.html' title='Where TK agrees with TK (or: why are triggers harmful)'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-Vn7fzUSm0w0/TzAXAy3wJ2I/AAAAAAAAAGo/xRYD3G6ab-8/s72-c/30_cus1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-5946353638171767752</id><published>2012-01-29T08:13:00.000-08:00</published><updated>2012-01-29T08:13:52.308-08:00</updated><title type='text'>Statement level constraint consistency</title><content type='html'>In the past week I've been investigating how Oracle fires triggers with the Merge and Multi-Table-Insert statements. Also took a look at 'statement-level constraint consistency' with these two types of statements.&amp;nbsp;My findings are here:&amp;nbsp;&lt;a href="http://rulegen.blogspot.com/2012/01/statement-level-constraint-validation.html"&gt;http://rulegen.blogspot.com/2012/01/statement-level-constraint-validation.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Normal transmission on harmful triggers should resume shortly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-5946353638171767752?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/5946353638171767752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2012/01/statement-level-constraint-consistency.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/5946353638171767752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/5946353638171767752'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2012/01/statement-level-constraint-consistency.html' title='Statement level constraint consistency'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-8015290085444912676</id><published>2012-01-04T06:12:00.000-08:00</published><updated>2012-01-04T06:12:42.345-08:00</updated><title type='text'>Workaround 2: don't use the row-trigger</title><content type='html'>So where were we?&lt;br /&gt;&lt;br /&gt;We wanted to implement the following business rule: "Cannot have a manager without a clerk in the same department." At the end of &lt;a href="http://harmfultriggers.blogspot.com/2011/11/some-preliminaries.html"&gt;this post&lt;/a&gt;, we tried implementing that using a straight-forward row-trigger, which introduced us to the mutating table error. And we've explained thereafter (&lt;a href="http://harmfultriggers.blogspot.com/2011/12/mutating-table-error-prevents-non.html"&gt;here&lt;/a&gt; and &lt;a href="http://harmfultriggers.blogspot.com/2011/12/look-mom-mutating-table-error-without.html"&gt;here&lt;/a&gt;) that this error is there for a very good reason.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Yducyg9X_Mk/TwRW66pcXfI/AAAAAAAAAGE/ag-W4KhGHpA/s1600/12_mut1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://4.bp.blogspot.com/-Yducyg9X_Mk/TwRW66pcXfI/AAAAAAAAAGE/ag-W4KhGHpA/s400/12_mut1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;We've also clarified that the two common '&lt;a href="http://harmfultriggers.blogspot.com/2011/11/workarounds-for-ora-04091.html"&gt;workarounds' 1a and 1b&lt;/a&gt;&amp;nbsp;really should not be used.&lt;br /&gt;&lt;br /&gt;So using a row-trigger to check for a clerk whenever a manager is inserted will not work. Remember the reason we wanted to use a row-trigger is because in it we have available &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;:new.deptno&lt;/span&gt; which represents the department into which a manager just got inserted. This enabled us to efficiently check only that department for the necessary clerk.&lt;br /&gt;&lt;br /&gt;So what about a statement trigger then? Obviously it forces us to validate all departments, since within the context of the after insert statement trigger, we do not know anything about the actual insert(s), other than that some insert took place.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-dm7g9Ku6wqI/TwRbhBV7hsI/AAAAAAAAAGQ/YQwCIWD9mH8/s1600/24_workaround2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://3.bp.blogspot.com/-dm7g9Ku6wqI/TwRbhBV7hsI/AAAAAAAAAGQ/YQwCIWD9mH8/s400/24_workaround2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Above is an attempt to implement our rule using the after insert statement trigger. As you can see we validate all departments. This is not a very efficient solution to implement our business rule. Not only do we validate all departments, we also validate them on all types of inserts. Obviously when we insert, for instance, a trainer, there is no need at all to validate this rule (assuming the rule was fine, before this insert), since inserting a trainer cannot violate this rule: such an insert doesn't 'touch' the rule.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Vo5BaCv1YUk/TwRc9ZRvOTI/AAAAAAAAAGc/TKU4M0CxmJE/s1600/24_workaround2_1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://4.bp.blogspot.com/-Vo5BaCv1YUk/TwRc9ZRvOTI/AAAAAAAAAGc/TKU4M0CxmJE/s400/24_workaround2_1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We will explain in detail how to a) workaround the mutating table issue + b) get an efficient (and correct) implementation for our rule using a statement trigger in multiple future posts. But before we do that, we'd like to spend some time on common use-cases of triggers, and why they gave rise to the general consensus out there of triggers being harmful.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-8015290085444912676?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/8015290085444912676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2012/01/workaround-2-dont-use-row-trigger.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/8015290085444912676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/8015290085444912676'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2012/01/workaround-2-dont-use-row-trigger.html' title='Workaround 2: don&apos;t use the row-trigger'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-Yducyg9X_Mk/TwRW66pcXfI/AAAAAAAAAGE/ag-W4KhGHpA/s72-c/12_mut1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-3025998177654337126</id><published>2011-12-30T01:10:00.000-08:00</published><updated>2011-12-30T01:28:23.447-08:00</updated><title type='text'>Look mom: a mutating table error without a trigger!</title><content type='html'>Did you ever wonder why it says 'trigger/function' in the error message of ORA-04091?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;ORA-04091: table ... is mutating, trigger/function may not see it&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;We know (and understand) by now that a row trigger cannot read a mutating table, but what's the /&lt;i&gt;function&lt;/i&gt; all about in above message text? Well there is a completely different scenario that has nothing to do with triggers, where you can run into this error. I thought to spend a short post on that first, so that you really fully understand that ORA-04091 is your friend. And again in this scenario it prevents you from coding logic that might work differently tomorrow than from what it did today.&lt;br /&gt;&lt;br /&gt;Let's quickly show you this scenario. We create the EMP table again and insert some test data into it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;drop table EMP;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;create table EMP&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;(EMPNO &amp;nbsp; &amp;nbsp;number(3,0) &amp;nbsp;not null primary key&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;,ENAME &amp;nbsp; &amp;nbsp;varchar2(20) not null&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;,SAL &amp;nbsp; &amp;nbsp; &amp;nbsp;number(4,0) &amp;nbsp;not null)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;insert into emp(empno,ename,sal)&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;values(100,'Toon',4000);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;insert into emp(empno,ename,sal)&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;values(101,'Izaak',5000);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;insert into emp(empno,ename,sal)&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;values(102,'Marcel',7000);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;insert into emp(empno,ename,sal)&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;values(103,'Rene',8000);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;commit;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Our HR department decided that above salaries should be aligned a bit more in the year 2012. And in their wisdom they decided that the following update should be run at the end of the year:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;update EMP e1 set e1.SAL =&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;e1.SAL + ((select avg(e2.SAL) from EMP e2) - e1.SAL)/2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This update brings everybody's salary a bit more towards the current average salary of all employees. The dba does a testrun of the script in the acceptance instance:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-dsjMfyMAjjg/Tv1-UWNxjTI/AAAAAAAAAF4/CcWJBI53PZc/s1600/xx.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="331" src="http://1.bp.blogspot.com/-dsjMfyMAjjg/Tv1-UWNxjTI/AAAAAAAAAF4/CcWJBI53PZc/s400/xx.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It runs well, HR confirms that it does what it was intended to do, and dba is fine with this in production.&lt;br /&gt;&lt;br /&gt;Here come the QA department though: they need to 'ok' the running of this script in production too. So they inspect the script with all QC guidelines in mind. Result of their review is that they won't allow this. There is distinct 'business logic' in there that needs to be removed from the statement and implemented in a stored function, so that it 'can be reused sometime in the future' (I've seen this happen in real companies...). They mandate that a function be implemented such that the update statement will be of the following form:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;update EMP e set e.SAL = f_new_sal(e.SAL)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So the developers pump out the following function for this:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;create or replace function f_new_sal&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;(p_current_sal in number)&amp;nbsp;&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;return number as&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;--&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;pl_avg_sal number;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;--&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;begin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; --&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; select avg(SAL)&amp;nbsp;&lt;/span&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;into pl_avg_sal&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; from EMP;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; --&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; return p_current_sal + (pl_avg_sal - p_current_sal)/2;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; --&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;end;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Everybody is happy. The dba executes the testrun again:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-cMO0qc6il2Q/Tv1z-yeLJwI/AAAAAAAAAFU/ob94UEPCqQQ/s1600/23_testrun2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="245" src="http://4.bp.blogspot.com/-cMO0qc6il2Q/Tv1z-yeLJwI/AAAAAAAAAFU/ob94UEPCqQQ/s400/23_testrun2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;WTF? Oracle throws a mutating table error? But there's no trigger involved here...&lt;br /&gt;&lt;br /&gt;Why is this happening?&lt;br /&gt;&lt;br /&gt;If you understood the explanation of this error in my previous posts sofar, you should be able to figure this one out. Oracle again detects that we seem to be selecting from a table that's currently mutating. Think about what is happening here:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The update starts identifying the rows that need their SAL column value changed;&lt;/li&gt;&lt;li&gt;It does this in some undetermined order (!);&lt;/li&gt;&lt;li&gt;When the first rows is found, it invokes our function to determine the new SAL column value for it;&lt;/li&gt;&lt;li&gt;Our function queries the EMP table, which is currently mutating: bingo we hit the error.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;When Oracle steps out of the SQL-engine that's currently executing the update statement, and invokes the function, then this function -- just like an after row update trigger would -- sees the intermediate states of EMP as they exist during execution of the update statement. This implies that the return value of our function invocations heavily depend on the order in which the rows happen to be updated.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Wanna see this happening? Let's use our database link "workaround" to prevent the mutating error. We create a loopback database link and change our function accordingly:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;create or replace function f_new_sal&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;(p_current_sal in number) return number as&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;--&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;pl_avg_sal number;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;--&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;begin&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; --&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; select avg(SAL) into pl_avg_sal&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; from EMP@loopback; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-- Here: added db-link.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; --&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; return p_current_sal + (pl_avg_sal - p_current_sal)/2;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; --&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;end;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Dba can now run the update without hitting the error:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-erjDXVs7Rew/Tv17lhLdWnI/AAAAAAAAAFg/qGnsxBeZXR0/s1600/23_testrun3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="372" src="http://2.bp.blogspot.com/-erjDXVs7Rew/Tv17lhLdWnI/AAAAAAAAAFg/qGnsxBeZXR0/s400/23_testrun3.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But look at the SAL values, only Toon's salary is updated correctly. These are not accepted by HR...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And these values will be different if we force a different order of updating the rows, for instance like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;update (select * from EMP order by EMPNO desc) e&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; set e.SAL = f_new_sal(e.SAL)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The proof is in eating the pudding:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-cyOe4KsCYOQ/Tv180NOO_5I/AAAAAAAAAFs/ntjG3m8V-do/s1600/23_testrun4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://4.bp.blogspot.com/-cyOe4KsCYOQ/Tv180NOO_5I/AAAAAAAAAFs/ntjG3m8V-do/s400/23_testrun4.jpg" width="373" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now Rene's salary is the only one being updated correctly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So again, don't forget: ORA-04091 is your friend.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Note that in the original version of the update statement&amp;nbsp;(without the function), Oracle ensures that the repeated 'invocations' of the subquery (select AVG(e2.SAL) etc.) all see the same snapshot of the database: the one that existed at the start of the update statement. So there is no indeterministic behavior in that case.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A fix for the issue demonstrated could be to just add the autonomous transaction pragma to the function. We would then get the same (deterministic) behavior as the original version of the update statement.&lt;/div&gt;&lt;div&gt;A better fix in this case though would be to not have the function at all, and just go with the original update statement.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-3025998177654337126?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/3025998177654337126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2011/12/look-mom-mutating-table-error-without.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/3025998177654337126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/3025998177654337126'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2011/12/look-mom-mutating-table-error-without.html' title='Look mom: a mutating table error without a trigger!'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-dsjMfyMAjjg/Tv1-UWNxjTI/AAAAAAAAAF4/CcWJBI53PZc/s72-c/xx.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-1686168132473594888</id><published>2011-12-23T01:13:00.000-08:00</published><updated>2011-12-23T01:13:41.012-08:00</updated><title type='text'>The mutating table error prevents non-deterministic behavior of your code</title><content type='html'>A short recap of workarounds 1a and 1b from our &lt;a href="http://harmfultriggers.blogspot.com/2011/11/workarounds-for-ora-04091.html"&gt;previous post&lt;/a&gt;.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In workaround 1a we ensured that the query that was selecting from the mutating table, was being executed from within an autonomous transaction.&lt;/li&gt;&lt;li&gt;In workaround 1b we ensured that this query was being executed via a loopback database link.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;We also discussed the major difference between the two workarounds:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;1a: the query sees the mutating table &lt;i&gt;as it existed prior to the start of the main transaction&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;1b: the query sees the mutating table &lt;i&gt;as it is being mutated by the triggering DML statement&lt;/i&gt;. Essentially this workaround behaves as if Oracle did not have a mutating table error at all.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;Please never implement these workarounds. Especially 1b for the reason I will demonstrate in this post. And do you really want the overhead of going through a database link every time the row-trigger fires? And potentially have twice the number of database sessions in your database instance? I hope not.&lt;br /&gt;&lt;br /&gt;Apart from this, you will have planted a booby trap in your application code: your code can now behave non-deterministically.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-FTzlxYKmfv8/Tuez4RaTknI/AAAAAAAAADs/YbhIjHIwgqU/s1600/19_why_friend.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://1.bp.blogspot.com/-FTzlxYKmfv8/Tuez4RaTknI/AAAAAAAAADs/YbhIjHIwgqU/s400/19_why_friend.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To discuss this we will investigate how our row-trigger behaves not in the case of a &lt;i&gt;single row&lt;/i&gt; insert, but a &lt;i&gt;multi-row&lt;/i&gt; insert. Given our business rule, no manager can be without a clerk in the same department, a multi-row insert that inserts both the manager as well as the necessary clerk as the first two employees for a new department in one go, is a valid insert statement that our trigger-based rule-enforcement code should allow.&lt;br /&gt;&lt;br /&gt;Let's look at the behavior of our row-trigger using workaround 1b (loopback db-link) first. Suppose we have a table called EMP_STAGE. This (staging) table is holding employee-rows that are to be inserted into the real EMP table. Table EMP_STAGE currently holds two employees: a manager and a clerk both working in department 42.&lt;br /&gt;&lt;br /&gt;Now see what our row-trigger does.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-mrRelLfHoxc/Tue3m-mHUFI/AAAAAAAAAD0/XFpttn5biMY/s1600/20_flaw1b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://3.bp.blogspot.com/-mrRelLfHoxc/Tue3m-mHUFI/AAAAAAAAAD0/XFpttn5biMY/s400/20_flaw1b.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Our row-trigger does not allow this insert. Why? Well think about it. How is the statement 'insert into EMP select * from EMP_STAGE' executed? Particularly in what order are the selected from EMP_STAGE and then inserted into EMP? Apparently in the case above the manager was the first one that was selected and inserted into EMP. Which would then fire our row-trigger, which would then see the mutating table (via the loopback db-link) and not find the required clerk in department 24. Hence our row-trigger raised the business rule error. And the multi-row insert statement never reaches inserting the second row (the clerk), it is cancelled by the raise_application_error and rolled back.&lt;br /&gt;&lt;br /&gt;But what if the rows happened to be processed in a different order? We can force this by simply adding an order-by clause to the subquery in the insert statement (we re-execute the insert and have added 'order by empno desc'):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-Nonj5ntV_ww/TvQ-CphNpQI/AAAAAAAAAEA/zhxtMkCuvCM/s1600/21_reason1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://4.bp.blogspot.com/-Nonj5ntV_ww/TvQ-CphNpQI/AAAAAAAAAEA/zhxtMkCuvCM/s400/21_reason1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Our two-row insert statement now executes succesfully!&lt;br /&gt;&lt;br /&gt;So what happened in this case? Due to the order by clause we've ensured that employee 111 (the clerk) got inserted first. The row-trigger fires, sees it's not a manager and therefore nothing needs to be done. Next employee 110 got inserted. The row-trigger fires again, sees it's a manager, and executes the business rule check by calling our procedure. This procedure now queries the EMP table (via the loopback db-link) and it actually sees (!) the clerk that just got inserted by the same statement, and therefore executes successfully.&lt;br /&gt;&lt;br /&gt;Now the order in which rows get inserted by a multi-row insert statement is (and rightfully should be) of no concern to you. Think of the subquery instead of being a simple 'select * from emp_stage', being a complex multi-table join: depending on current contents/sizes of the tables involved, statistics might differ which in turn might cause different execution plans for the insert statement, which in turn might change the order in which rows get inserted by a multi-row insert statement. &lt;i&gt;If your code is not immune to this order then in effect your code executes in a non-deterministic fashion.&lt;/i&gt;&amp;nbsp;And this is the essence of why ORA-04091 is your friend. It protects you against developing software that might work today, and not work tomorrow.&lt;br /&gt;&lt;br /&gt;So how does this all work with the autonomous transaction workaround? Here's the same two-row insert for that scenario, executed without and with the forced order (EMP again has no employees in department 42).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-V044Lu0NDQI/TvRBNwu3_wI/AAAAAAAAAEM/jVmNXlAMaDI/s1600/21_reason2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://4.bp.blogspot.com/-V044Lu0NDQI/TvRBNwu3_wI/AAAAAAAAAEM/jVmNXlAMaDI/s400/21_reason2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;As you can see both versions of the insert statements fail with this workaround. The explanation for this is simple. Since the query that checks for the necessary clerk is run from inside an autonomous transaction, all it sees is the EMP table as it existed prior to start of the insert statement: in which there were no employees for department 42. So in this case the 'workaround' is not a workaround at all: yes it prevents the mutating table error, but no it does not allow a valid insert-statement to execute successfully. Note that it even wouldn't allow two subsequent single row-inserts: first insert a clerk, then insert a manager. With this workaround you are forced to commit the clerk first, and only then insert the manager.&lt;br /&gt;&lt;br /&gt;So, in summary:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-xtqfBtuBcWE/TvRDAtvD1uI/AAAAAAAAAEY/RfqNtA2hAVw/s1600/22_summary.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/-xtqfBtuBcWE/TvRDAtvD1uI/AAAAAAAAAEY/RfqNtA2hAVw/s400/22_summary.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Again, forget about these two red herrings: don't use them.&lt;br /&gt;&lt;br /&gt;In the following posts we will start developing how to correctly work around the mutating table error. And as many of you probably know, it involves a combination of row and statement triggers.&lt;br /&gt;&lt;br /&gt;In the meantime happy holidays!&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-1686168132473594888?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/1686168132473594888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2011/12/mutating-table-error-prevents-non.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/1686168132473594888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/1686168132473594888'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2011/12/mutating-table-error-prevents-non.html' title='The mutating table error prevents non-deterministic behavior of your code'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-FTzlxYKmfv8/Tuez4RaTknI/AAAAAAAAADs/YbhIjHIwgqU/s72-c/19_why_friend.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-7227447456330635007</id><published>2011-12-13T22:35:00.000-08:00</published><updated>2011-12-13T12:41:28.722-08:00</updated><title type='text'>"Workarounds" for ORA-04091</title><content type='html'>In the &lt;a href="http://harmfultriggers.blogspot.com/2011/11/some-preliminaries.html"&gt;previous post&lt;/a&gt;, we demonstrated running into the mutating table error. The example involved the well-known EMP table and a business rule that we are trying to implement: all departments with a Manager should also employ a Clerk. We started our implementation for that rule by writing a straight-forward 'after-row-insert' trigger on the EMP table.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ruwCkQkn33M/TtAQPx_ReMI/AAAAAAAAAC0/nD0VhQKEqp0/s1600/13_mut2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="301" src="http://1.bp.blogspot.com/-ruwCkQkn33M/TtAQPx_ReMI/AAAAAAAAAC0/nD0VhQKEqp0/s400/13_mut2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The row trigger detects whenever a Manager is inserted (remember, we can only detect this with a row trigger, not with a statement trigger);&lt;/li&gt;&lt;li&gt;It then calls a stored procedure to which it supplies the department number (deptno-column value) of the inserted Manager;&lt;/li&gt;&lt;li&gt;The procedure then queries the EMP table to see if a Clerk exists in this department;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;If it finds no such Clerk, the procedure raises an application error, which causes the after-insert-row trigger to fail. This in turn prevents the insert of the Manager, since it would violate our business rule (the Manager insert will undergo a statement-level rollback due to the row-trigger failing);&lt;/li&gt;&lt;li&gt;If it finds such a Clerk, then the procedure finishes&amp;nbsp;successfully, which causes the after-insert-row trigger to finish successfully, which causes the Manager insert to execute successfully.&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;div&gt;A straight-forward 'use-case' of using a (row) trigger, right? Of course this setup didn't work, since the cursor in the stored procedure will always hit the mutating table error, thus preventing a Manager insert even if the necessary Clerk would indeed be present.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, what's the workaround for this? Well there are a two I would like to discuss in this post: one of them is very 'popular', the other one is -- I guess -- not so well-known.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I cannot stress enough upfront right now, that both 'workarounds' are red herrings.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Workaround 1a: use the autonomous_transaction pragma inside the stored procedure.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If we add just one line at the top of the stored procedure, we can fix this baby (see red-arrow line below).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-luiOG0G1GYI/Tt_Ju_0I5AI/AAAAAAAAADM/1Sa_VHEM94E/s1600/16_workaround1a.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="296" src="http://3.bp.blogspot.com/-luiOG0G1GYI/Tt_Ju_0I5AI/AAAAAAAAADM/1Sa_VHEM94E/s400/16_workaround1a.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;By ensuring that all code inside the P_CHECK_FOR_CLERK procedure will run as an autonomous transaction, we can prevent the mutating table error. Why? Because it causes Oracle to 'step out' of the current transaction context, in which EMP is currently 'mutating', and open a new transaction in which EMP is not mutating, and therefor can be queried without hitting the mutating table error. So all we need to do is add the PRAGMA line highlighted above. We do not need to add an explicit COMMIT (or ROLLBACK) at the end of the procedure, which is usually required to end an autonomous transaction, since the procedure doesn't really start a transaction at all: is just issues a query.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is the popular workaround. Now for the lesser known workaround.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Workaround 1b: use a loopback db-link to query the mutating table.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;By querying the EMP table to look for a Clerk, via a loopback database link, we can fix this baby too.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-MqwybsV-kMU/Tt_Mw7h2QLI/AAAAAAAAADU/4RSrgvxKf0w/s1600/16_workaround1b.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://3.bp.blogspot.com/-MqwybsV-kMU/Tt_Mw7h2QLI/AAAAAAAAADU/4RSrgvxKf0w/s400/16_workaround1b.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So we create a loopback database link (which we call loopback above) which connects back to our application owner schema. We then add the text '@loopback' to the table name (EMP) inside the FROM-clause of our query that was hitting the mutating table (second red arrow above). Done. We'll demonstrate in a minute that this 'fixes' our issue as well.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But before we do that you really need to understand a fundamental difference between these two workarounds.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-znc0JOzojZk/Tt_OVXuc-iI/AAAAAAAAADc/9qBh6H6bpJc/s1600/17_beware.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://2.bp.blogspot.com/-znc0JOzojZk/Tt_OVXuc-iI/AAAAAAAAADc/9qBh6H6bpJc/s400/17_beware.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the preliminaries that we discussed in the previous post, one of the observations that was made about an autonomous transaction was that all code in such transaction cannot see any (uncommitted) changes made by the other (parent) transaction. So what this means for our example is that the cursor in the stored procedure (the one that tries to find a Clerk) which is executed by an 'after-row' trigger, will see the EMP table without the Manager that was inserted by our triggering insert statement.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Yet.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The cursor that uses the loopback database link, which is executed from within a new session created by the database link, *will* see the mutating table as it exists at that point-in-time in the transaction of the 'original' session (the one that issued the Manager insert). Why? Because Oracle detects that both sessions are now part of a distributed transaction, and the database link session will see the same database snapshot as the other session. Now this is pretty radical, I think. When I discovered this behavior I was stunned for a few moments: here we have a database session (OK, it's one that was created by a database link, but still) a session that is able to see uncommitted changes of a different session!&amp;nbsp;I will show you a demo that proves this behavior in a minute.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now wouldn't that be an in-depth (trick) question for some fancy certification exam:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;"When can a database session see uncommitted changes of another session?"&lt;/div&gt;&lt;div&gt;a) Never.&lt;/div&gt;&lt;div&gt;b) Always, this is default behavior.&lt;/div&gt;&lt;div&gt;c) If it's using an autonomous transaction.&lt;/div&gt;&lt;div&gt;d) If the session was opened by a database link.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;;-)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now let's test both workarounds. Remember we are trying to insert a manager into department 42, in which no other employee works yet.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-B9t80nM8pwk/TuevkIo-n_I/AAAAAAAAADk/xsyNz1iEGQ0/s1600/18_itworks.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="302" src="http://4.bp.blogspot.com/-B9t80nM8pwk/TuevkIo-n_I/AAAAAAAAADk/xsyNz1iEGQ0/s400/18_itworks.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Et voilá: no more mutating table error. Our row-trigger + procedure work, they give us the business rule error.&lt;br /&gt;&lt;br /&gt;So what's wrong with both approaches? Can you guess it? I'll give you a hint: think about multi-row inserts. Will they work given workaround 1a (the autonomous transaction) or workaround 1b (the db-link trick)?&lt;br /&gt;&lt;br /&gt;I'll discuss this in the sequel, which should follow promptly.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-7227447456330635007?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/7227447456330635007/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/workarounds-for-ora-04091.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/7227447456330635007'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/7227447456330635007'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/workarounds-for-ora-04091.html' title='&quot;Workarounds&quot; for ORA-04091'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-ruwCkQkn33M/TtAQPx_ReMI/AAAAAAAAAC0/nD0VhQKEqp0/s72-c/13_mut2.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-4832226421015021797</id><published>2011-11-25T13:13:00.001-08:00</published><updated>2011-11-25T14:16:31.847-08:00</updated><title type='text'>Some preliminaries</title><content type='html'>There are a few remarks to be made on DML event triggers. Just to be sure we are all at the same level on the playing field. The first one is that since these triggers fire as part of the execution of a DML statement, the trigger code is not allowed to execute any DDL (which causes an implicit commit) or (explicit) transactional control statements, such as commit, rollback, savepoint or rollback to savepoint. This restriction is true for both statement level as well as row level DML event triggers.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-jG5pyF2TB_g/TtAE4-TdXoI/AAAAAAAAACU/F6tOg3Rq32k/s1600/09_prop1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://4.bp.blogspot.com/-jG5pyF2TB_g/TtAE4-TdXoI/AAAAAAAAACU/F6tOg3Rq32k/s400/09_prop1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;If you try to do so, Oracle will throw an &lt;a href="http://ora-04092.ora-code.com/"&gt;ORA-04092&lt;/a&gt; at you. A common "workaround" of developers who really think that they need to do this kind of stuff from within trigger code, is to wrap the offending code inside an autonomous transaction, thereby not affecting,&amp;nbsp;transactionally, the main transaction which is currently executing the DML statement (that caused the trigger to fire). There is an important observation to be made at this time: the SQL-code inside such autonomous code block, cannot see the changes that are being made by the DML statement that is executing. Nor can this SQL-code see the effect of any other DML statements that might have been executed earlier inside the main transaction. Oracle treats the autonomous code block as if it were executed inside a different &lt;i&gt;session&lt;/i&gt;. And sessions never see changes made by other sessions that have not yet committed these changes.&lt;br /&gt;&lt;br /&gt;Remember this one. I'll refer back to this observation in the next post when I discuss common "workarounds" for the infamous mutating table error.&lt;br /&gt;&lt;br /&gt;The second remark:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-O2xkb6tAD70/TtALWgQK_MI/AAAAAAAAACc/Fg-YjiHIcPk/s1600/10_prop2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://2.bp.blogspot.com/-O2xkb6tAD70/TtALWgQK_MI/AAAAAAAAACc/Fg-YjiHIcPk/s400/10_prop2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;If a trigger fails, due to an exception being raised (and left unhandled), then the triggering DML statement (and all that might have been done by the trigger code self), will be rolled back. Note that this is a statement level rollback: so any prior changes executed in the transaction, are still left "posted" (not rolled back). You can view this statement level rollback as a 'rollback to savepoint', where the savepoint was set (by Oracle) just prior to starting the DML statement execution.&lt;br /&gt;&lt;br /&gt;Third (an final) remark in this post: row level triggers are not allowed to read what's called the mutating table.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-O3DC2q4c1u0/TtAM6lsNLjI/AAAAAAAAACk/a8Gx1Y8M68E/s1600/11_prop3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://1.bp.blogspot.com/-O3DC2q4c1u0/TtAM6lsNLjI/AAAAAAAAACk/a8Gx1Y8M68E/s400/11_prop3.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;If your row level trigger code queries the table that's being affected by the triggering DML statement, then Oracle will throw an &lt;a href="http://ora-04091.ora-code.com/"&gt;ORA-04091&lt;/a&gt; at you and cause the above mentioned statement level rollback. If you've ever played around with (row) triggers, you must have encountered this issue. I think my first encounter was in 1993. And there is a very clear cause why you will encounter this issue: the kind of stuff you would like to do in a row-trigger, often, no very often, requires you to query the mutating table. I'll come back to this later.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Mg5Ta537-aw/TtAPOW2deKI/AAAAAAAAACs/ah9fjhxCqoI/s1600/12_mut1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://3.bp.blogspot.com/-Mg5Ta537-aw/TtAPOW2deKI/AAAAAAAAACs/ah9fjhxCqoI/s400/12_mut1.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;One of my goals is to explain to you that this error is not your enemy. No, it's your friend. It prevents you to develop software, that might work today, but no longer tomorrow. I'll demonstrate that in the next post. For now we'll close this post by&amp;nbsp;quickly building an example that gives rise to the mutating table.&lt;br /&gt;&lt;br /&gt;Suppose we need to implement the requirement that every employee whose job is Manager, must be accompanied by another employee whose job is Clerk and works in the same department (as the manager does). Well, we can implement that easily, all it requires is a row trigger, right?&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ruwCkQkn33M/TtAQPx_ReMI/AAAAAAAAAC0/nD0VhQKEqp0/s1600/13_mut2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="301" src="http://1.bp.blogspot.com/-ruwCkQkn33M/TtAQPx_ReMI/AAAAAAAAAC0/nD0VhQKEqp0/s400/13_mut2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We build an after insert row trigger on the EMP table, which will verify, whenever a Manager is inserted, that a Clerk exists in the same department. Here's the code for that procedure you see called in above trigger body:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ECQuwS_KDuw/TtAQ66ksfiI/AAAAAAAAAC8/rx28k8TZbY4/s1600/14_mut3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/-ECQuwS_KDuw/TtAQ66ksfiI/AAAAAAAAAC8/rx28k8TZbY4/s400/14_mut3.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And as you can see, this code queries the mutating table. So let's test this trigger. Here's the contents of our EMP table, and an insert statement that attempts to insert an Manager into department 42 (which has no Clerk).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-QNH2rL1EL8A/TtARLySmiZI/AAAAAAAAADE/QNBwPXvxgTA/s1600/15_mut4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://3.bp.blogspot.com/-QNH2rL1EL8A/TtARLySmiZI/AAAAAAAAADE/QNBwPXvxgTA/s400/15_mut4.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And as expected, Oracle throws the mutating table at us.&lt;br /&gt;&lt;br /&gt;In the next installment I'll discuss common "workarounds" for this issue. They really aren't workarounds, but everyone calls them that...&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-4832226421015021797?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/4832226421015021797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/some-preliminaries.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/4832226421015021797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/4832226421015021797'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/some-preliminaries.html' title='Some preliminaries'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-jG5pyF2TB_g/TtAE4-TdXoI/AAAAAAAAACU/F6tOg3Rq32k/s72-c/09_prop1.jpg' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-2533264628308357751</id><published>2011-11-22T07:26:00.001-08:00</published><updated>2011-11-22T09:53:53.929-08:00</updated><title type='text'>So what triggers are we talking about?</title><content type='html'>Here's an overview of the talk.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-8ox98OfWhxk/Tsu_HIflttI/AAAAAAAAABU/ETi2Bbjja5o/s1600/01_contents.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://2.bp.blogspot.com/-8ox98OfWhxk/Tsu_HIflttI/AAAAAAAAABU/ETi2Bbjja5o/s400/01_contents.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;This post will deal with the first bullet: the scope, what triggers are we talking about? And what triggers are we not talking about.&amp;nbsp;Then there will probably be a few posts on 'properties' of the triggers, most notably I will spend some time on explaining the infamous mutating table error.&amp;nbsp;Next we move on to a high level classification of use-cases of triggers. And talk a bit about why some of these might be considered harmful. Finally we will explain, in detail, the one use case where triggers are the perfect means to achieve the end.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-qkw6XNLh7bM/TsvFQYiQJGI/AAAAAAAAABc/vpNfaxkVQ30/s1600/01_whattriggers.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://4.bp.blogspot.com/-qkw6XNLh7bM/TsvFQYiQJGI/AAAAAAAAABc/vpNfaxkVQ30/s400/01_whattriggers.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The most common types of triggers, the ones everybody probably used at some time in their pl/sql programming career, are the "DML event" triggers. As above slide shows, there are twelve of such triggers: four each for every type of DML statement, Insert, Update and Delete. These triggers will be fired by the DBMS before a DML statement, after a DML statement, and before/after each affected row of the triggering DML statement. Stuff you all know right? The big difference between the statement-level and the row-level triggers, is that the latter ones can inspect (and change) the column-values of the current row that is being inserted/deleted/updated.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-8SRV9ke3aT0/TsvGVIbIGtI/AAAAAAAAABk/o8ez3G5njkY/s1600/03_example.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://3.bp.blogspot.com/-8SRV9ke3aT0/TsvGVIbIGtI/AAAAAAAAABk/o8ez3G5njkY/s400/03_example.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;So here's an example. Suppose we have an EMP table that holds employees, and we want to execute an update statement that will increase the salary of all clerks (see update statement above). This will for the given table affect three rows. The before update statement trigger will then fire once. Next for each affected row the before and after row triggers will fire. And finally the after statement trigger will fire.&lt;br /&gt;&lt;br /&gt;So if we create the four update triggers on the EMP table as follows:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-zy4YeepcmAY/TsvelhLVfaI/AAAAAAAAABs/batqpeW1Fvc/s1600/04_triggs.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://2.bp.blogspot.com/-zy4YeepcmAY/TsvelhLVfaI/AAAAAAAAABs/batqpeW1Fvc/s400/04_triggs.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We will get the following output (given we have set serveroutput to on).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ARTzYDBm4wY/Tsve5e4oqrI/AAAAAAAAAB0/10QWMDRFfdI/s1600/05_output.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://4.bp.blogspot.com/-ARTzYDBm4wY/Tsve5e4oqrI/AAAAAAAAAB0/10QWMDRFfdI/s400/05_output.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Nothing new so far, I hope. Before we continue I just wanted to mention that as of Oracle11G we have the compound trigger feature.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-qa8jZjegBuc/TsvfUCs_R0I/AAAAAAAAAB8/d1p2ulshfOQ/s1600/06_compound.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://3.bp.blogspot.com/-qa8jZjegBuc/TsvfUCs_R0I/AAAAAAAAAB8/d1p2ulshfOQ/s400/06_compound.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&amp;nbsp;A compound trigger enables us to create the four update triggers above all in one go as follows:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-kqRXJvgDl_k/TsvfftMQaTI/AAAAAAAAACE/DkQ6q1AnmVw/s1600/07_compound2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/-kqRXJvgDl_k/TsvfftMQaTI/AAAAAAAAACE/DkQ6q1AnmVw/s400/07_compound2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Now, do you know why Oracle introduced compound triggers? I'll talk about that in a later post. What I'll say now is this: compound triggers are the answer of an enhancement request made by you (the pl/sql community) a long time ago. Because you have always hit a certain programming pattern with regards to triggers, when using them for a certain use case. Again I'll explain this in more detail in a future post.&lt;br /&gt;&lt;br /&gt;So these are the triggers that are in-scope of this blog: DML event triggers, be them created individually or four in one go using the compound trigger mechanism.&lt;br /&gt;&lt;br /&gt;Oracle DBMS offers us with many more triggers:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-zJ176KB_ckQ/Tsvg5V10EkI/AAAAAAAAACM/c0GBJKs63JE/s1600/08_outscope.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://1.bp.blogspot.com/-zJ176KB_ckQ/Tsvg5V10EkI/AAAAAAAAACM/c0GBJKs63JE/s400/08_outscope.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;All of which will not be the matter of subject for this blog.&lt;br /&gt;&lt;br /&gt;Stay tuned.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-2533264628308357751?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/2533264628308357751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/so-what-triggers-are-we-talking-about.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/2533264628308357751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/2533264628308357751'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/so-what-triggers-are-we-talking-about.html' title='So what triggers are we talking about?'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-8ox98OfWhxk/Tsu_HIflttI/AAAAAAAAABU/ETi2Bbjja5o/s72-c/01_contents.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1339993448594009052.post-8335162540180419954</id><published>2011-11-22T00:58:00.001-08:00</published><updated>2011-11-22T23:57:38.019-08:00</updated><title type='text'>Starting this blog</title><content type='html'>So I finally was able to find some time to start this blog. I mentioned before that I might do this &lt;a href="http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:3868442800346504246"&gt;in the asktom discussion here&lt;/a&gt;. This blog will be about database triggers. Oracle database triggers that is. I'm a strong advocate of database triggers. Now hold your horses... I have been using database triggers ever since Oracle7 in the early nineties brought them to us. But I've been using them for a very specific reason, and have been presenting about this ever since. I will blog about this in a very similar way as I blogged about &lt;a href="http://thehelsinkideclaration.blogspot.com/2009/03/start-of-this-blog.html"&gt;TheHelsinkiDeclaration&lt;/a&gt;, by documenting the presentation I have frequently given now at various Oracle related seminars and user group meetings.&lt;br /&gt;&lt;br /&gt;Let me present exhibit A, showing that I have been presenting about a particular use-case for using database triggers for quite some time now. Here's the front cover of the 1994 International Oracle User Week (that's how Oracle OpenWorld used to be called in the nineties) conference.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-y8Uuk9gWOgY/TstrQIkldzI/AAAAAAAAABA/ZW6pUz7pa5Q/s1600/hot1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-y8Uuk9gWOgY/TstrQIkldzI/AAAAAAAAABA/ZW6pUz7pa5Q/s320/hot1.jpg" width="226" /&gt;&lt;/a&gt;&lt;/div&gt;And here's a part of the contents.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-vtiZhfwb1kU/Tstr0EEdsEI/AAAAAAAAABI/FSdZjKVgM2s/s1600/hot2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="640" src="http://4.bp.blogspot.com/-vtiZhfwb1kU/Tstr0EEdsEI/AAAAAAAAABI/FSdZjKVgM2s/s640/hot2.jpg" width="452" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Paper #10, "Business Rules: Classification and Implementation (...)". And as you can see I was employed by Oracle back then. Note that Steve (Feuerstein) was already presenting in those days too. Now some of you can already guess what my talk was about. The use-case for triggers that I'm advocating, that is. For those of you who don't, stay tuned, I will explain the use-case and my arguments for it, in the posts that will follow.&lt;br /&gt;&lt;br /&gt;So that was (almost) two centuries ago, business rules presentations by T(oon) K(oppelaars)...&lt;br /&gt;&lt;br /&gt;Fast forward to this millenium. Here's another TK on triggers in the database:&lt;br /&gt;&lt;br /&gt;From&amp;nbsp;&lt;a href="http://tkyte.blogspot.com/2006/09/classic-example-of-why-i-despise.html"&gt;http://tkyte.blogspot.com/2006/09/classic-example-of-why-i-despise.html &lt;/a&gt;&lt;br /&gt;"Triggers are so abused and so used inappropriately, I'd rather live without them."&lt;br /&gt;"There are no times triggers cannot be avoided. They are purely a convenience that is overused, abused, and improperly used."&lt;br /&gt;&lt;br /&gt;And from:&amp;nbsp;&lt;a href="http://tkyte.blogspot.com/2007/03/dreaded-others-then-null-strikes-again.html"&gt;http://tkyte.blogspot.com/2007/03/dreaded-others-then-null-strikes-again.html&lt;/a&gt;&lt;br /&gt;[on why he doesn't like triggers] "Because I hate being surprised or tricked. And triggers are all about trickery and surprises."&lt;br /&gt;&lt;br /&gt;The other "TK" of course is Tom Kyte from&amp;nbsp;&lt;a href="http://asktom.oracle.com/"&gt;asktom.oracle.com&lt;/a&gt;. Since you are reading this blog, I"m sure Tom Kyte needs no further introduction. He makes no secret of the fact that he has strong reservations about the database feature called "triggers". And this opinion is wholeheartedly embraced by the PL/SQL community at large. The default response whenever a question on database triggers pops up on &amp;nbsp;&lt;a href="https://forums.oracle.com/forums/forum.jspa?forumID=75&amp;amp;start=0"&gt;PL/SQL OTN forum&lt;/a&gt;&amp;nbsp;is:&lt;br /&gt;&lt;br /&gt;"Triggers should be avoided as much as possible."&lt;br /&gt;"Don't use them, they are bad."&lt;br /&gt;"Triggers are considered harmful."&lt;br /&gt;&lt;br /&gt;Well excuse me, but I find this general consensus harmful.&lt;br /&gt;&lt;br /&gt;At this time I should disclose that I run a company called &lt;a href="http://www.rulegen.com/"&gt;RuleGen&lt;/a&gt;, which is also the name of a product that generates Oracle trigger code to implement business rules.&lt;br /&gt;&lt;br /&gt;A year ago I decided to present on why I find this general consensus harmful. And this resulted in a presentation at &lt;a href="http://www.hotsos.com/sym11/sym_speakers_koppelaars.html"&gt;Hotsos 2011&lt;/a&gt;. The title of this presentation is: "Triggers considered harmful, considered harmful". And that's not a typo, no. I'm using the "considered harmful" concept which was introduced by Edsger Dijkstra when he published a paper called "Goto statement considered harmful". And as you can read &lt;a href="http://en.wikipedia.org/wiki/Considered_harmful"&gt;here&lt;/a&gt;, it gave cause to quite some 'follow up' papers with the same "typo" in their titles.&lt;br /&gt;&lt;br /&gt;Recently I also delivered this presentation at the &lt;a href="http://www.bgoug.org/"&gt;BGOUG&lt;/a&gt;. And it has been submitted for presentation at the upcoming &lt;a href="http://kscope12.com/"&gt;ODTUG&lt;/a&gt;. I'll let you know if it got accepted, so you can attend ODTUG and hear a live version of this blog.&lt;br /&gt;&lt;br /&gt;So much for the introduction. Future posts we will go into the details of why I find triggers not harmful at all given they are used for a very specific use-case.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1339993448594009052-8335162540180419954?l=harmfultriggers.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://harmfultriggers.blogspot.com/feeds/8335162540180419954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/triggers-considered-harmful-considered.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/8335162540180419954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1339993448594009052/posts/default/8335162540180419954'/><link rel='alternate' type='text/html' href='http://harmfultriggers.blogspot.com/2011/11/triggers-considered-harmful-considered.html' title='Starting this blog'/><author><name>Toon Koppelaars</name><uri>http://www.blogger.com/profile/08153913435188725112</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-y8Uuk9gWOgY/TstrQIkldzI/AAAAAAAAABA/ZW6pUz7pa5Q/s72-c/hot1.jpg' height='72' width='72'/><thr:total>11</thr:total></entry></feed>
