<?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-6654852660557505391</id><updated>2012-02-16T05:58:00.635-08:00</updated><category term='SQL'/><title type='text'>Dmitry Nikiforov's blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-3197477414482998101</id><published>2009-05-26T22:55:00.000-07:00</published><updated>2009-05-26T23:24:48.256-07:00</updated><title type='text'>Triggers and constraints: diagnostic mechanism, part II</title><content type='html'>The following question we can ask - what the column DATA represents in the index&lt;br /&gt;structure ? Of course it's not rowid.&lt;br /&gt;The most reasonable suggestion is what Oracle keeps in this index-like structure&lt;br /&gt;the pairs "key - count" for the constraint. Each pair reflects the number of times the constraint voilation&lt;br /&gt;has been detected for particular key value. Each DML operation executed in the current transaction&lt;br /&gt;can change this information.&lt;br /&gt;OK, let's check this suggestion.&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2   execute immediate 'set constraint all deferred';&lt;br /&gt;  3   for i in 1..257 loop&lt;br /&gt;  4   insert into child_t values(i, mod(i,4));&lt;br /&gt;  5   end loop;&lt;br /&gt;  6  end;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;The dump shows:&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 0, len=9, data:(4):  41 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;Really:&lt;br /&gt;&lt;br /&gt;SQL&gt; select y, to_char(count(*),'XX') hex&lt;br /&gt;  2  from child_t group by y&lt;br /&gt;  3  order by 1&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;         Y HEX&lt;br /&gt;---------- ---&lt;br /&gt;         0  40&lt;br /&gt;         1  41&lt;br /&gt;         2  40&lt;br /&gt;         3  40&lt;br /&gt;&lt;br /&gt;Looks truth, right ?&lt;br /&gt;&lt;br /&gt;Ok, let's remove some rows with y = 1:&lt;br /&gt;&lt;br /&gt;SQL&gt; delete from child_t where y = 1 and x &lt;= 127;&lt;br /&gt;&lt;br /&gt;32 rows deleted.&lt;br /&gt;&lt;br /&gt;SQL&gt; select y, to_char(count(*),'XX') hex&lt;br /&gt;  2  from child_t group by y&lt;br /&gt;  3  order by 1&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;         Y HEX&lt;br /&gt;---------- ---&lt;br /&gt;         0  40&lt;br /&gt;         1  21&lt;br /&gt;         2  40&lt;br /&gt;         3  40&lt;br /&gt;&lt;br /&gt;Dump is:&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  21 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;Now remove all rows with Y=1:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ---D--, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;As we can see Oracle doesn't set count to zero when it has to - instead,&lt;br /&gt;it marks the index record as deleted.&lt;br /&gt;&lt;br /&gt;If after that we input new record with y = 1 the key is actual again:&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;If we input parent row for key value = 2:&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into parent_t values(2);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;when Oracle marks index row as deleted but doesn't change DATA value:&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;If now we remove all rows with Y = 2&lt;br /&gt;&lt;br /&gt;SQL&gt; delete from child_t where y = 2;&lt;br /&gt;&lt;br /&gt;64 rows deleted.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Oracle does nothing with index&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;The removement of the parent record doesn't affect the index too:&lt;br /&gt;&lt;br /&gt;SQL&gt; delete from parent_t where x = 2;&lt;br /&gt;&lt;br /&gt;1 row deleted.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;br /&gt;&lt;br /&gt;But if now we insert several child records with this key, Oracle&lt;br /&gt;marks the index record as actual and resets DATA value:&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into child_t select rownum, 2 from dual&lt;br /&gt;  2  connect by level &lt;= 10;&lt;br /&gt;&lt;br /&gt;10 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; select y, to_char(count(*),'XX') hex&lt;br /&gt;  2  from child_t group by y&lt;br /&gt;  3  order by 1&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;         Y HEX&lt;br /&gt;---------- ---&lt;br /&gt;         0  40&lt;br /&gt;         1   1&lt;br /&gt;         2   A&lt;br /&gt;         3  40&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;row#0[8024] flag: ------, lock: 0, len=8, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ------, lock: 2, len=9, data:(4):  0a 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 0, len=9, data:(4):  40 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-3197477414482998101?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/3197477414482998101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=3197477414482998101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/3197477414482998101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/3197477414482998101'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/05/triggers-and-constraints-diagnostic_26.html' title='Triggers and constraints: diagnostic mechanism, part II'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-3464322990786295151</id><published>2009-05-26T01:53:00.000-07:00</published><updated>2009-05-26T05:53:53.026-07:00</updated><title type='text'>Triggers and constraints: diagnostic mechanism, part I</title><content type='html'>Throughout the prevoius topic the essential question is - what is diagnostic area, where it is located and what mechanism Oracle uses to handle it.&lt;br /&gt;Let's check what is temp segment usage in our case. Maybe we'll see something interested.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="font-family:arial;color:#000099;"&gt;&lt;pre&gt;SQL&gt; create table parent_t (x int);&lt;/pre&gt;&lt;pre&gt;Table created.&lt;/pre&gt;&lt;pre&gt;SQL&gt; create table child_t (x int, y int);&lt;/pre&gt;&lt;pre&gt;Table created.&lt;/pre&gt;&lt;pre&gt;SQL&gt; alter table parent_t add constraint parent_t_pk primary key (x);&lt;/pre&gt;&lt;pre&gt;Table altered.&lt;/pre&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;pre&gt;&lt;span style="color:#000099;"&gt;&lt;em&gt;&lt;strong&gt;SQL&gt; alter table child_t add constraint child_t_fk&lt;/strong&gt;&lt;/em&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="color:#000099;"&gt;&lt;em&gt;&lt;strong&gt;2 foreign key (y) references parent_t (x) deferrable;&lt;/strong&gt;&lt;/em&gt;&lt;/span&gt;&lt;/pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="font-family:arial;color:#000099;"&gt;&lt;pre&gt;&lt;br /&gt;Table altered.&lt;/pre&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;At the beginning we will execute INSERT statement which should not generate any constraint violations for not-deferred constraint mode and check temp segment usage.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; insert into child_t select rownum, null from dual connect by level &lt;= 10000;  10000 rows created.  SQL&gt; select sid from v$mystat where rownum = 1;&lt;br /&gt;&lt;br /&gt;       SID&lt;br /&gt;----------&lt;br /&gt;       127&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;SQL&gt; select x.segtype, x.extents, x.blocks, x.segfile#, x.segblk#&lt;br /&gt;  2  from v$tempseg_usage x, v$session y&lt;br /&gt;  3  where x.session_addr = y.saddr and y.sid = 127&lt;br /&gt;  4  /&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;no rows selected&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well, nothing unexpected.&lt;br /&gt;&lt;br /&gt;Now let's set DEFERRED MODE and insert next 10000 rows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; set constraint all deferred;&lt;br /&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;Constraint set.&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into child_t select rownum, null from dual connect by level &lt;= 10000;  10000 rows created.  SQL&gt; select x.segtype, x.extents, x.blocks, x.segfile#, x.segblk#&lt;br /&gt;  2  from v$tempseg_usage x, v$session y&lt;br /&gt;  3  where x.session_addr = y.saddr and y.sid = 127&lt;br /&gt;  4  /&lt;br /&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;br /&gt;no rows selected&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And finally let's insert rows what force constraint violation - Y column value&lt;br /&gt;will contain values from 0 to 3 inclusively and no one has parent key:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; insert into child_t select rownum, mod(rownum,4) from dual connect by level &lt;= 10000;  &lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;10000 rows created.  &lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt; &lt;/pre&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; select x.segtype, x.extents, x.blocks, x.segfile#, x.segblk#, x.segrfno#&lt;br /&gt;  2  from v$tempseg_usage x, v$session y&lt;br /&gt;  3  where x.session_addr = y.saddr and y.sid = 127&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;SEGTYPE      EXTENTS     BLOCKS   SEGFILE#    SEGBLK#   SEGRFNO#&lt;br /&gt;--------- ---------- ---------- ---------- ---------- ----------&lt;br /&gt;INDEX              1        128        202     255753          2&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;Now we see temporary segment usage and it's type is INDEX.&lt;/pre&gt;&lt;pre&gt;Let's dump sereval blocks from this structure.&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; alter system dump tempfile 2 block min 255753 block max 255760;&lt;br /&gt;&lt;br /&gt;System altered.&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We will see segment header and index leaf block:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;Start dump data blocks tsn: 3 file#: 2 minblk 255753 maxblk 255880&lt;br /&gt;buffer tsn: 3 rdba: 0x0083e709 (2/255753)&lt;br /&gt;scn: 0x0875.7bd4b765 seq: 0x00 flg: 0x08 tail: 0xb7651000&lt;br /&gt;frmt: 0x02 chkval: 0x0000 type: 0x10=DATA SEGMENT HEADER - UNLIMITED&lt;br /&gt;  Extent Control Header&lt;br /&gt;  -----------------------------------------------------------------&lt;br /&gt;  Extent Header:: spare1: 0      spare2: 0      #extents: 1      #blocks: 127&lt;br /&gt;                  last map  0x00000000  #maps: 0      offset: 4128&lt;br /&gt;      Highwater::  0x0083e70b  ext#: 0      blk#: 1      ext size: 127&lt;br /&gt;  #blocks in seg. hdr's freelists: 0&lt;br /&gt;  #blocks below: 1&lt;br /&gt;  mapblk  0x00000000  offset: 0&lt;br /&gt;                   Unlocked&lt;br /&gt;     Map Header:: next  0x00000000  #extents: 1    obj#: 8644361 flag: 0x40000000&lt;br /&gt;  Extent Map&lt;br /&gt;  -----------------------------------------------------------------&lt;br /&gt;   0x0083e70a  length: 127&lt;br /&gt;&lt;br /&gt;  nfl = 1, nfb = 1 typ = 2 nxf = 0 ccnt = 0&lt;br /&gt;  SEG LST:: flg: UNUSED lhd: 0x00000000 ltl: 0x00000000 &lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;buffer tsn: 3 rdba: 0x0083e70a (2/255754)&lt;br /&gt;scn: 0x0875.7bd4b7d3 seq: 0x00 flg: 0x08 tail: 0xb7d30600&lt;br /&gt;frmt: 0x02 chkval: 0x0000 type: 0x06=trans data&lt;br /&gt;Block header dump:  0x0083e70a&lt;br /&gt; Object id on Block? Y&lt;br /&gt;&lt;b&gt;&lt;span style="color:green;"&gt; seg/obj: 0x83e709  csc: 0x875.7bd4b764  itc: 2  flg: -  typ: 2 - INDEX&lt;br /&gt;     fsl: 0  fnx: 0x0 ver: 0x01&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt; Itl           Xid                  Uba         Flag  Lck        Scn/Fsc&lt;br /&gt;0x01   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000&lt;br /&gt;0x02   0x0006.02b.000050aa  0x00873a96.165c.02  ----    4  fsc 0x0000.00000000&lt;br /&gt;&lt;br /&gt;Leaf block dump&lt;br /&gt;===============&lt;br /&gt;header address 242391644=0xe729a5c&lt;br /&gt;kdxcolev 0&lt;br /&gt;KDXCOLEV Flags = - - -&lt;br /&gt;kdxcolok 0&lt;br /&gt;kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y&lt;br /&gt;kdxconco 1&lt;br /&gt;kdxcosdc 0&lt;br /&gt;kdxconro 4&lt;br /&gt;kdxcofbo 44=0x2c&lt;br /&gt;kdxcofeo 7997=0x1f3d&lt;br /&gt;kdxcoavs 7953&lt;br /&gt;kdxlespl 0&lt;br /&gt;kdxlende 0&lt;br /&gt;kdxlenxt 0=0x0&lt;br /&gt;kdxleprv 0=0x0&lt;br /&gt;kdxledsz 4&lt;br /&gt;kdxlebksz 8032&lt;br /&gt;&lt;b&gt;&lt;span style="color:green;"&gt;row#0[8024] flag: ------, lock: 2, len=8, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;/span&gt;&lt;/em&gt;&lt;/pre&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Let's now delete from the CHILD_T table all rows with y = 2 and check&lt;br /&gt;if there will be any changes in this segment:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; delete from child_t where y = 1;&lt;br /&gt;&lt;br /&gt;2500 rows deleted.&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;b&gt;&lt;span style="color:green;"&gt;row#0[8024] flag: ------, lock: 2, len=8, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;/span&gt;&lt;br /&gt;&lt;span style="color:red;"&gt;row#1[8015] flag: ---D--, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;/span&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;row#2[8006] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;/span&gt;&lt;/b&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OK, the record for key value = 1 has been marked as deleted.&lt;br /&gt;&lt;br /&gt;Now go forward and create parent row for Y=2:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;strong&gt;&lt;em&gt;&lt;span style="color:blue;"&gt;SQL&gt; insert into parent_t values(2);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;/span&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As we can see, now the row for key value = 2 has been marked as DELETED too:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;b&gt;&lt;span style="color:green;"&gt;row#0[8024] flag: ------, lock: 2, len=8, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ---D--, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;/span&gt;&lt;br /&gt;&lt;span style="color:red;"&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;/span&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;row#3[7997] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;/span&gt;&lt;/b&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And finally we can create parent row for Y=0 in AUTONOMOUS transaction:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; declare&lt;br /&gt;  2   pragma autonomous_transaction;&lt;br /&gt;  3  begin&lt;br /&gt;  4   insert into parent_t values(0);&lt;br /&gt;  5   commit;&lt;br /&gt;  6  end;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;b&gt;&lt;span style="color:green;"&gt;row#0[8024] flag: ------, lock: 2, len=8, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ---D--, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;/span&gt;&lt;/b&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;No change - as we could reasonably expect.&lt;br /&gt;&lt;br /&gt;Now touch rows in the table with Y=0, but don't touch Y value itself:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;strong&gt;&lt;span style="color:blue;"&gt;SQL&gt; update child_t set x = x where y = 0;&lt;br /&gt;&lt;br /&gt;2500 rows updated.&lt;/span&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;No changes in the leaf block:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;b&gt;&lt;span style="color:green;"&gt;row#0[8024] flag: ------, lock: 2, len=8, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;br /&gt;row#1[8015] flag: ---D--, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;/span&gt;&lt;/b&gt;&lt;/em&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But if we touch Y column in UPDATE statement&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;strong&gt;&lt;em&gt;&lt;span style="color:blue;"&gt;SQL&gt; update child_t set y = y where y = 0;&lt;br /&gt;&lt;br /&gt;2500 rows updated.&lt;/span&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;we can see what the row with key value = 0 is marked as deleted - because&lt;br /&gt;now the parent key exists:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;em&gt;&lt;b&gt;&lt;span style="color:red;"&gt;row#0[8024] flag: ---D--, lock: 2, len=8, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 1; (1):  80&lt;/span&gt;&lt;br /&gt;&lt;span style="color:green;"&gt;row#1[8015] flag: ---D--, lock: 2, len=9, data:(4):  01 00 00 00&lt;br /&gt;col 0; len 2; (2):  c1 02&lt;br /&gt;row#2[8006] flag: ---D--, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 03&lt;br /&gt;row#3[7997] flag: ------, lock: 2, len=9, data:(4):  c4 09 00 00&lt;br /&gt;col 0; len 2; (2):  c1 04&lt;/span&gt;&lt;/b&gt;&lt;/em&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-3464322990786295151?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/3464322990786295151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=3464322990786295151' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/3464322990786295151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/3464322990786295151'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/05/triggers-and-constraints-diagnostic.html' title='Triggers and constraints: diagnostic mechanism, part I'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-3988828661579750345</id><published>2009-03-19T11:10:00.001-07:00</published><updated>2009-03-19T11:58:12.670-07:00</updated><title type='text'>Triggers and constraints</title><content type='html'>Oracle documentation specifies the Trigger Execution Model which explains how triggers counteract with integrity constraints. One interesting question concerns to the intergity conception and Oracle internal mechanisms of constraint checking.&lt;br /&gt;&lt;br /&gt;Let's do the following:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;SQL&gt; create table t (x int primary key);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table t_r(x int references t(x), y int)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace trigger aifer_usual&lt;br /&gt;  2  after insert on t_r&lt;br /&gt;  3  for each row&lt;br /&gt;  4  begin&lt;br /&gt;  5   dbms_output.put_line(:new.y);&lt;br /&gt;  6   if :new.y = 6 then&lt;br /&gt;  7    insert into t values(:new.x);&lt;br /&gt;  8   end if;&lt;br /&gt;  9  end;&lt;br /&gt; 10  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace trigger aifer_usual&lt;br /&gt;  2  after insert on t_r&lt;br /&gt;  3  for each row&lt;br /&gt;  4  begin&lt;br /&gt;  5   dbms_output.put_line(:new.y);&lt;br /&gt;  6   if :new.y = 6 then&lt;br /&gt;  7    insert into t values(:new.x);&lt;br /&gt;  8   end if;&lt;br /&gt;  9  end;&lt;br /&gt; 10  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL&gt; set serveroutput on&lt;br /&gt;SQL&gt; insert into t_r select 1, rownum from dual connect by level &lt;= 8;&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;5&lt;br /&gt;6&lt;br /&gt;7&lt;br /&gt;8&lt;br /&gt;&lt;br /&gt;8 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t;&lt;br /&gt;&lt;br /&gt;         X&lt;br /&gt;----------&lt;br /&gt;         1&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;No surprices - everything works as it's described. Really, Oracle claims what the integrity has to be guaranteed at the end of SQL operator execution (for non-deferrable constraints):&lt;br /&gt;&lt;br /&gt;"...If any of the results of a DML statement execution violate an integrity constraint, then Oracle rolls back the statement and returns an error...&lt;br /&gt;...Even though a constraint is defined to verify that each mgr value matches &lt;br /&gt;an empno value, this statement is legal because Oracle effectively performs &lt;br /&gt;its constraint checking after the statement completes. Figure 21-11 shows that Oracle performs the actions of the entire SQL statement before any constraints are checked..."&lt;br /&gt;&lt;br /&gt;But if we cover trigger code in an autonomous transaction, we will find than the result is differ:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;SQL&gt; roll;&lt;br /&gt;&lt;br /&gt;Rollback complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace trigger aifer_usual&lt;br /&gt;  2  after insert on t_r&lt;br /&gt;  3  for each row&lt;br /&gt;  4  declare&lt;br /&gt;  5   pragma autonomous_transaction;&lt;br /&gt;  6  begin&lt;br /&gt;  7   dbms_output.put_line(:new.y);&lt;br /&gt;  8   if :new.y = 6 then&lt;br /&gt;  9    insert into t values(:new.x);&lt;br /&gt; 10   end if;&lt;br /&gt; 11   commit;&lt;br /&gt; 12  end;&lt;br /&gt; 13  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t_r select 1, rownum from dual connect by level &lt;= 8;&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;5&lt;br /&gt;6&lt;br /&gt;7&lt;br /&gt;8&lt;br /&gt;insert into t_r select 1, rownum from dual connect by level &lt;= 8&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02291: integrity constraint (SCOTT.SYS_C0026536) violated - parent key not&lt;br /&gt;found&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t;&lt;br /&gt;&lt;br /&gt;         X&lt;br /&gt;----------&lt;br /&gt;         1&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Why ? After all at the end of INSERT statement all rows in the table &lt;em&gt;satisfy&lt;/em&gt; intergity constraint.&lt;br /&gt;&lt;br /&gt;Of course we can think that the statement checks the constraint using a single point-in-time snapshot at SCN of the statement begin. But in the case of non-autonomous trigger the parent record did not exist too at the start point of operator - and INSERT was in success.&lt;br /&gt;&lt;br /&gt;Probably the internal mechanism of Oracle is based on the transaction diagnostic area stack processing. The following example shows the basis for that conclusion.&lt;br /&gt;&lt;br /&gt;Let's create the table with deferred constrain:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;SQL&gt; create table t_r(x int references t(x) deferrable initially deferred, y int);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace trigger aifer_usual&lt;br /&gt;  2  after insert on t_r&lt;br /&gt;  3  for each row&lt;br /&gt;  4  declare&lt;br /&gt;  5   pragma autonomous_transaction;&lt;br /&gt;  6  begin&lt;br /&gt;  7   dbms_output.put_line(:new.y);&lt;br /&gt;  8   if :new.y = 6 then&lt;br /&gt;  9    insert into t values(:new.x);&lt;br /&gt; 10   end if;&lt;br /&gt; 11   commit;&lt;br /&gt; 12  end;&lt;br /&gt; 13  /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t_r select 1, rownum from dual connect by level &lt;= 8;&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;5&lt;br /&gt;6&lt;br /&gt;7&lt;br /&gt;8&lt;br /&gt;&lt;br /&gt;8 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;commit&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02091: transaction rolled back&lt;br /&gt;ORA-02291: integrity constraint (SCOTT.SYS_C0026537) violated - parent key not&lt;br /&gt;found&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Notice what now at the end of transaction all data satisfy the intergity constraint too but the exception is raised like as in the example above.&lt;br /&gt;&lt;br /&gt;But it's easy to correct.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;SQL&gt; delete from t;&lt;br /&gt;&lt;br /&gt;1 row deleted.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t_r;&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t_r select 1, rownum from dual connect by level &lt;= 8;&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;5&lt;br /&gt;6&lt;br /&gt;7&lt;br /&gt;8&lt;br /&gt;&lt;br /&gt;8 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; delete from t_r where y &lt;=6;&lt;br /&gt;&lt;br /&gt;6 rows deleted.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;But if we leave in the table any row what was inserted before insertion of the parent row, we will have our usual exception: &lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;SQL&gt; delete from t_r;&lt;br /&gt;&lt;br /&gt;2 rows deleted.&lt;br /&gt;&lt;br /&gt;SQL&gt; delete from t;&lt;br /&gt;&lt;br /&gt;1 row deleted.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t_r select 1, rownum from dual connect by level &lt;= 8;&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;3&lt;br /&gt;4&lt;br /&gt;5&lt;br /&gt;6&lt;br /&gt;7&lt;br /&gt;8&lt;br /&gt;&lt;br /&gt;8 rows created.&lt;br /&gt;&lt;br /&gt;SQL&gt; delete from t_r where y &lt; 6;&lt;br /&gt;&lt;br /&gt;5 rows deleted.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;commit&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02091: transaction rolled back&lt;br /&gt;ORA-02291: integrity constraint (SCOTT.SYS_C0026537) violated - parent key not&lt;br /&gt;found&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;So we can imagine the internal meachinsm of constraint checking as follows (the follwing text is no more that the speculation about &lt;em&gt;possible&lt;/em&gt; internal Oracle mechanism):&lt;br /&gt;&lt;br /&gt;1) &lt;br /&gt;&lt;br /&gt;For each new row the integrity constraint is checked, based on the current single point-in-time snapshot.&lt;br /&gt;&lt;br /&gt;2) &lt;br /&gt;&lt;br /&gt;If the constraint is violated the exception information is placed in the diagnostic area stack.&lt;br /&gt;&lt;br /&gt;3) &lt;br /&gt;&lt;br /&gt;Than the statement / transaction ends Oracle checks the content of the diagnostic area stack and generates the exception if it is not empty.&lt;br /&gt;&lt;br /&gt;4) &lt;br /&gt;&lt;br /&gt;Any DML operator in the same transaction touches the error stack and can clear it&lt;br /&gt;(if it's action corrects the prevoius errors). For this reason INSERT statement in the non-autonomous trigger "removes" errors generated by rows with y = 1...6 from the error stack. At the end of the basic INSERT statement the error stack is clear.&lt;br /&gt;&lt;br /&gt;Autonomous DML operator can't touche the local error stack and can't clear it - because of this the basic INSERT failed dispite the presence of the parent row. When we use deferred constraint the diagnostic area error stack is not clear at the end of statement but is doesn't raise the exception - Oracle waits for the transaction end to check the error stack. When we use DELETE statement we remove the rows what are the cause of errors in the stack - so we clear the stack too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-3988828661579750345?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/3988828661579750345/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=3988828661579750345' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/3988828661579750345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/3988828661579750345'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/03/triggers-and-constraints.html' title='Triggers and constraints'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-5686179225487563844</id><published>2009-03-04T06:05:00.000-08:00</published><updated>2009-03-04T06:53:51.269-08:00</updated><title type='text'>ROWNUM and ORDER SIBLINGS BY</title><content type='html'>&lt;div align="left"&gt;Oracle ROWNUM pseudocolumn is a tricky thing and the many people put into the following trap:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; SELECT ename, rownum FROM emp WHERE rownum &lt;= 5 ORDER BY ename;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;pre&gt;ENAME  ROWNUM&lt;br /&gt;------ ------&lt;br /&gt;ALLEN       2&lt;br /&gt;JONES       4&lt;br /&gt;MARTIN      5&lt;br /&gt;SMITH       1&lt;br /&gt;WARD        3&lt;/pre&gt;&lt;/strong&gt;&lt;br /&gt;We have a mess of rownums what is natural behaviour of Oracle (considering when rownum&lt;br /&gt;is assigned to the row and when ORDER BY is executed).&lt;br /&gt;&lt;br /&gt;But for ORDER SIBLINGS BY the behavoiur seems differ:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; select ename, rownum from emp &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;2 start with ename = 'KING' &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;3 connect by prior empno = mgr &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;4 /&lt;br /&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;pre&gt;&lt;br /&gt;ENAME  ROWNUM&lt;br /&gt;------ ------&lt;br /&gt;KING        1&lt;br /&gt;JONES       2&lt;br /&gt;SCOTT       3&lt;br /&gt;ADAMS       4&lt;br /&gt;FORD        5&lt;br /&gt;SMITH       6&lt;br /&gt;BLAKE       7&lt;br /&gt;ALLEN       8&lt;br /&gt;WARD        9&lt;br /&gt;MARTIN     10&lt;br /&gt;TURNER     11&lt;br /&gt;JAMES      12&lt;br /&gt;CLARK      13&lt;br /&gt;MILLER     14&lt;/pre&gt;&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/strong&gt;Now we have natural result - hierarchy order corresponds to rownum sequence.&lt;br /&gt;But if we include ORDER SIBLINGS BY ename, we will not get a mess in rownum as&lt;br /&gt;we could expect:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; select ename, rownum from emp &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;2 start with ename = 'KING' &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;3 connect by prior empno = mgr &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;4 order siblings by ename;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;pre&gt;&lt;br /&gt;ENAME    ROWNUM&lt;br /&gt;-------  ------&lt;br /&gt;KING          1&lt;br /&gt;BLAKE         2&lt;br /&gt;ALLEN         3&lt;br /&gt;JAMES         4&lt;br /&gt;MARTIN        5&lt;br /&gt;TURNER        6&lt;br /&gt;WARD          7&lt;br /&gt;CLARK         8&lt;br /&gt;MILLER        9&lt;br /&gt;JONES        10&lt;br /&gt;FORD         11&lt;br /&gt;SMITH        12&lt;br /&gt;SCOTT        13&lt;br /&gt;ADAMS        14&lt;/pre&gt;&lt;/strong&gt;&lt;br /&gt;&lt;pre&gt;  &lt;/pre&gt;&lt;br /&gt;It looks like if Oracle retrieves child rows for each parent node, sorts them in accordance&lt;br /&gt;with ORDER SIBLINGS BY clause and &lt;strong&gt;&lt;em&gt;after that&lt;/em&gt;&lt;/strong&gt; &lt;span style="font-size:0;"&gt;assignes rownums&lt;/span&gt;.&lt;br /&gt;Unfortunately I couldn't find the details of this behaviour in Oracle documentation and Metalink.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-5686179225487563844?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/5686179225487563844/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=5686179225487563844' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/5686179225487563844'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/5686179225487563844'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/03/rownum-and-order-siblings-by.html' title='ROWNUM and ORDER SIBLINGS BY'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-6322092220341754921</id><published>2009-03-04T04:10:00.000-08:00</published><updated>2009-03-04T04:24:50.441-08:00</updated><title type='text'>SKIP LOCKED and the locking moment</title><content type='html'>SELECT ... FOR UPDATE statement locks data at the moment of a cursor opening.&lt;br /&gt;It's a conceptual thing because SELECT ... FOR UPDATE has to return locked (serialized) consistent snapshot. Really, let's open FOR UPDATE cursor in transaction A:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; REM Transaction A&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; var rc refcursor &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; exec open :rc for select ename from emp where ename in ('KING','ALLEN') for update;&lt;br /&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;PL/SQL procedure successfully completed.&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Then in transaction B we can't lock 'KING' row until commit or rollback in transaction A:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt;REM We wait for A completion&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; select ename from emp where ename = 'KING' for update; &lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;FOR UPDATE SKIP LOCKED voilates the concept of serialized snapshot - and now we&lt;br /&gt;don't need to try to lock rows before fetch:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt;REM Transaction A&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; exec open :rc for select ename from emp where ename in ('KING','ALLEN') for update skip locked;&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/strong&gt;&lt;strong&gt;&lt;em&gt;PL/SQL procedure successfully completed.&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;We can lock 'KING' in another transaction (no lock before fetch):&lt;br /&gt;&lt;strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt;REM Transaction B&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; select ename from emp where ename = 'KING' for update;&lt;br /&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;ENAME&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;----------&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;KING&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Fetching of SKIP LOCKED cursor ignores 'KING'...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt;REM Transaction A&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; print rc&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;ENAME&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;----------&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;ALLEN&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span&gt;...and locks 'ALLEN' row at the moment of fetching (we are waiting for commit/rollback in A):&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; REM Transaction B - we are waitnig for A completion&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;strong&gt;&lt;em&gt;SQL&gt; select ename from emp where ename = 'ALLEN' for update;&lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-6322092220341754921?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/6322092220341754921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=6322092220341754921' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/6322092220341754921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/6322092220341754921'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/03/skip-locked-and-locking-moment.html' title='SKIP LOCKED and the locking moment'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-6963767060466820718</id><published>2009-02-25T21:46:00.000-08:00</published><updated>2009-02-25T21:59:34.567-08:00</updated><title type='text'>Read consistency and flashback query</title><content type='html'>&lt;div align="left"&gt;In one of Oracle professional forums the question has been asked: how can I see the history of changes made by my transaction ? One of the answers was: use a flashback query. This is a wrong answer. &lt;/div&gt;&lt;div align="left"&gt;&lt;br /&gt;When we query data Oracle allowes us to view only changes committed before SCN of query start or changes made by our transaction before the beginning of query. Oracle uses the undo information to reconstruct the required versionsof blocks. they are known as CR (consistent read) versions of blocks. In fact it has to rollback all transactions in blocks what have not been committed before the query beginning. At the same time Oracle can have to perform "partial rollback" for our own transaction.The example below illustrates it:&lt;br /&gt;&lt;br /&gt;SQL&gt; var rc refcursor&lt;br /&gt;SQL&gt; select * from t;&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t values(1); -- Changes before cursor open&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; exec open :rc for select * from t; -- Cursor open, SCN assigned&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; -- Change data after the query beginning.&lt;br /&gt;SQL&gt; -- Our transaction changes the same block using the same ITL slot&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; insert into t values(2);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt;-- Oracle has to perform partial rollback&lt;br /&gt;SQL&gt;-- in our transaction to recover consistent version of block.&lt;br /&gt;&lt;br /&gt;SQL&gt;-- It has to rollback "insert into t values(2)"&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; print rc&lt;br /&gt;&lt;br /&gt;X&lt;br /&gt;-------------------------&lt;br /&gt;1&lt;br /&gt;&lt;br /&gt;SQL&gt; exec open :rc for select * from t; -- Now we have new query SCN &gt; SCN of changes&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; print rc&lt;br /&gt;&lt;br /&gt;X&lt;br /&gt;-------------------------&lt;br /&gt;1&lt;br /&gt;2&lt;br /&gt;&lt;br /&gt;Everything works as it stated in concepts. But it's easy to rollback the transaction in the block entirely - owing to RBA information in the block ITL slot and ability to definethe transaction status using the transaction table in undo header. But what to do if we need to rollback our own transaction partially - up to the last change before the query beginning ? It's not a trivial question.The undo records don't contain the information about SCN, andITL slot contains only Commit SCN, and the block header contains the information about SCN of last changes.&lt;br /&gt;Probably Oracle uses the same mechanism to provide the consistent read and statement-level rollback - statement-level system savepoints. In this case Oracle knows to what point it should rollback the data in the block.&lt;br /&gt;In that sense flashback query works differ than a usual select query when we talk about seeing the changes made by our transaction. Because there is not any relation between SCN in "AS OF SCN ()" clause and the system-level savepoint of the query, Oracle is not able to define the boundary for the partial rollback. Moreover, even if we had the relation between SCN and undo entry, we still would not able to provide the correct partial rollback. Savepoints separate one SQL operator from another (because the transaction performs operators in series). But not SCNs do. So if we rollbacked our transaction to the certain SCN we would have a risk to get to the "middle" of DML operator - and get to the inconsistent data snapshot.&lt;br /&gt;In flashback queries Oracle doesn't have any alternative as to rollback ALL changes what have not been committed before specified SCN, including the changes of our transaction. So it should be taken into account when we try to use flashback queries.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t values(1);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t;&lt;br /&gt;&lt;br /&gt;X&lt;br /&gt;-------------------------&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;&lt;br /&gt;SQL&gt; select dbms_flashback.get_system_change_number scn from dual;&lt;br /&gt;&lt;br /&gt;SCN&lt;br /&gt;-------------------&lt;br /&gt;9300655466989&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t as of scn (9300655466989);&lt;br /&gt;&lt;br /&gt;no rows selected&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-6963767060466820718?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/6963767060466820718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=6963767060466820718' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/6963767060466820718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/6963767060466820718'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/02/read-consistency-and-flashback-query.html' title='Read consistency and flashback query'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-254029168108619346</id><published>2009-02-22T11:27:00.000-08:00</published><updated>2009-02-22T12:26:17.187-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>NOT IN and NULL in multi-column case</title><content type='html'>When I interview people I usually ask the question like this:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;Suppose I have table T ( ID INT, NAME VARCHAR2(10)) and three row in that:&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;ID NAME&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;---- ---------&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;1 ALLEN&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;2 SCOTT&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;3 WARD&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;What is the result of the query&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;SELECT * FROM T WHERE (ID, NAME) NOT IN ((3, 'ALLEN'), (2,NULL)) ?&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;I am quite surpised how many people answer "no one row" on this question.&lt;br /&gt;Probably it is the result of the confusion with the fact what the query&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;SELECT * FROM T WHERE (NAME) NOT IN ('ALLEN', NULL)&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;&lt;em&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/em&gt;&lt;br /&gt;returns no rows.&lt;br /&gt;&lt;br /&gt;This is the example how the common sense can play a bad joke.&lt;br /&gt;&lt;br /&gt;In reality the expression&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(ID, NAME) NOT IN ((3 'ALLEN'), (2,NULL))&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;is the equivalent of&lt;br /&gt;&lt;br /&gt;(ID, NAME) != (3, 'ALLEN') AND (ID, NAME) != (2, NULL)&lt;br /&gt;&lt;br /&gt;We can decompose this expression deeper so we will have&lt;br /&gt;&lt;br /&gt;(ID != 3 OR NAME != 'ALLEN') AND (ID != 2 OR NAME != NULL)&lt;br /&gt;&lt;br /&gt;So now it's clear what the result of the estimation for the record&lt;br /&gt;&lt;br /&gt;1, ALLEN&lt;br /&gt;&lt;br /&gt;is:&lt;br /&gt;&lt;br /&gt;(TRUE OR FALSE) AND (TRUE OR NULL) == TRUE AND TRUE == TRUE&lt;br /&gt;&lt;br /&gt;The estimation for (2, SCOTT) is&lt;br /&gt;&lt;br /&gt;(TRUE OR TRUE) AND (FALSE OR NULL) == TRUE AND NULL == NULL&lt;br /&gt;&lt;br /&gt;and (3, WARD) estimated as&lt;br /&gt;&lt;br /&gt;(FALSE OR TRUE) AND (TRUE OR NULL) == TRUE AND TRUE == TRUE&lt;br /&gt;&lt;br /&gt;So we have the final result&lt;br /&gt;&lt;br /&gt;1, ALLEN&lt;br /&gt;3, WARD&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;You will have "no rows returned" result only in case&lt;br /&gt;&lt;br /&gt;NOT IN ((...), (...), &lt;strong&gt;&lt;em&gt;(NULL,NULL,...,NULL)&lt;/em&gt;&lt;/strong&gt;) where (NULL,NULL,...,NULL) is the cartige&lt;br /&gt;contained only NULL values.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-254029168108619346?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/254029168108619346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=254029168108619346' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/254029168108619346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/254029168108619346'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2009/02/not-in-and-null-in-multi-column-case.html' title='NOT IN and NULL in multi-column case'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-1700479076224305152</id><published>2008-01-30T03:58:00.000-08:00</published><updated>2008-01-30T04:03:25.750-08:00</updated><title type='text'>NEXTVAL and CURRVAL in Oracle 11G</title><content type='html'>Oracle 11G documentation states what the direct access to NEXTVAL and CURRVAL improves the performance impact. But seems this change is a feature to minimize coder's work, no more.&lt;br /&gt;The trace results are below:&lt;br /&gt;&lt;br /&gt;declare&lt;br /&gt; x int;&lt;br /&gt;begin&lt;br /&gt; for i in 1..100000 loop&lt;br /&gt;  x := sq_test.nextval;&lt;br /&gt; end loop;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.00          0          0          0           0&lt;br /&gt;Execute      1     12.76      13.69          0          0          0           1&lt;br /&gt;Fetch        0      0.00       0.00          0          0          0           0&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total        2     12.76      13.69          0          0          0           1&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 0&lt;br /&gt;Optimizer mode: ALL_ROWS&lt;br /&gt;Parsing user id: 20 &lt;br /&gt;&lt;br /&gt;Elapsed times include waiting on following events:&lt;br /&gt;  Event waited on                             Times   Max. Wait  Total Waited&lt;br /&gt;  ----------------------------------------   Waited  ----------  ------------&lt;br /&gt;  SQL*Net message to client                       1        0.00          0.00&lt;br /&gt;  SQL*Net message from client                     1        0.00          0.00&lt;br /&gt;********************************************************************************&lt;br /&gt;&lt;br /&gt;SQL ID : 3uvw0xca08wut&lt;br /&gt;Select SQ_TEST.NEXTVAL&lt;br /&gt;from&lt;br /&gt; dual&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;call     count       cpu    elapsed       disk      query    current        rows&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;Parse        1      0.00       0.08          0          0          0           0&lt;br /&gt;Execute  99999      3.64       3.59          0          0          0           0&lt;br /&gt;Fetch    99999      2.51       2.61          0          0          0       99999&lt;br /&gt;------- ------  -------- ---------- ---------- ---------- ----------  ----------&lt;br /&gt;total   199999      6.17       6.29          0          0          0       99999&lt;br /&gt;&lt;br /&gt;Misses in library cache during parse: 1&lt;br /&gt;Optimizer mode: ALL_ROWSParsing user id: 20     (recursive depth: 1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It looks like  x := sq_test.nextval transforms implicitly into select sq_test.nextval into x from dual.   :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-1700479076224305152?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/1700479076224305152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=1700479076224305152' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/1700479076224305152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/1700479076224305152'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2008/01/nextval-and-currval-in-oracle-11g.html' title='NEXTVAL and CURRVAL in Oracle 11G'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-7328843446466012455</id><published>2007-06-15T00:17:00.000-07:00</published><updated>2007-06-15T00:22:24.303-07:00</updated><title type='text'>Cursor FOR LOOP vs. OPEN-CLOSE in relation with exceptions</title><content type='html'>One of the advantage of cursor FOR-LOOP cycle over ordinar OPEN-FETCH-CLOSE method&lt;br /&gt;is the automatic close the cursor even it's scope is global. In other words FOR-LOOP&lt;br /&gt;automatically closes the cursor in case of exit from the cycle over the exception:&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace package my_pkg&lt;br /&gt;2 is&lt;br /&gt;3 cursor s is select * from emp;&lt;br /&gt;4 end;&lt;br /&gt;5 /&lt;br /&gt;&lt;br /&gt;Package created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace function f1&lt;br /&gt;2 return number&lt;br /&gt;3 is&lt;br /&gt;4 begin&lt;br /&gt;5 for v in my_pkg.s loop&lt;br /&gt;6 dbms_output.put_line(v.ename);&lt;br /&gt;7 raise no_data_found;&lt;br /&gt;8 end loop;&lt;br /&gt;9 return 1;&lt;br /&gt;10 end;&lt;br /&gt;11 /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;2 c int;&lt;br /&gt;3 begin&lt;br /&gt;4 c := f1;&lt;br /&gt;5 exception&lt;br /&gt;6 when others then&lt;br /&gt;7 if my_pkg.s%isopen then&lt;br /&gt;8 dbms_output.put_line('Open');&lt;br /&gt;9 elsif not my_pkg.s%isopen then&lt;br /&gt;10 dbms_output.put_line('Closed');&lt;br /&gt;11 end if;&lt;br /&gt;12 end;&lt;br /&gt;13 /&lt;br /&gt;SMITH&lt;br /&gt;Closed&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace function f1&lt;br /&gt;2 return number&lt;br /&gt;3 is&lt;br /&gt;4 v my_pkg.s%ROWTYPE;&lt;br /&gt;5 begin&lt;br /&gt;6 open my_pkg.s;&lt;br /&gt;7 fetch my_pkg.s into v;&lt;br /&gt;8 dbms_output.put_line(v.ename);&lt;br /&gt;9 raise no_data_found;&lt;br /&gt;10 close my_pkg.s;&lt;br /&gt;11 return 1;&lt;br /&gt;12 end;&lt;br /&gt;13 /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;2 c int;&lt;br /&gt;3 begin&lt;br /&gt;4 c := f1;&lt;br /&gt;5 exception&lt;br /&gt;6 when others then&lt;br /&gt;7 if my_pkg.s%isopen then&lt;br /&gt;8 dbms_output.put_line('Open');&lt;br /&gt;9 elsif not my_pkg.s%isopen then&lt;br /&gt;10 dbms_output.put_line('Closed');&lt;br /&gt;11 end if;&lt;br /&gt;12 end;&lt;br /&gt;13 /&lt;br /&gt;SMITH&lt;br /&gt;Open&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-7328843446466012455?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/7328843446466012455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=7328843446466012455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/7328843446466012455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/7328843446466012455'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2007/06/cursor-for-loop-vs-open-close-in.html' title='Cursor FOR LOOP vs. OPEN-CLOSE in relation with exceptions'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-251406364645224164</id><published>2007-05-31T23:27:00.000-07:00</published><updated>2009-02-21T12:05:30.265-08:00</updated><title type='text'>UPDATE option of MERGE and ordinar MERGE</title><content type='html'>Q: Can I use MERGE with UPDATE option instead ordinar UPDATE.&lt;br /&gt;&lt;br /&gt;Yes, you can but in a single-user environment. MERGE doesn't work like ordinar UPDATE in multi-user system.&lt;br /&gt;&lt;br /&gt;Let's create two tables:&lt;br /&gt;&lt;br /&gt;SQL&gt; create table t_s (on_column number(2), name varchar2(10))&lt;br /&gt;2 /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table t_d (id number, on_column number(2), name varchar2(10))&lt;br /&gt;2 /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t_s values(10,'table 1')&lt;br /&gt;2 /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into t_s values(20,'table 1')&lt;br /&gt;2 /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;2 for i in 1..4 loop&lt;br /&gt;3 insert into t_d values(i,20,null);&lt;br /&gt;4 end loop;&lt;br /&gt;5 commit;&lt;br /&gt;6 end;&lt;br /&gt;7 /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table t_s add constraint t_s_pk primary key (on_column)&lt;br /&gt;2 /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now we open the new Oracle session and change ON_COLUMN value from 20 to 50 of T_D table in one of the row:&lt;br /&gt;&lt;br /&gt;SQL&gt; update t_d set on_column = 50 where id = 3;&lt;br /&gt;&lt;br /&gt;1 row updated.&lt;br /&gt;&lt;br /&gt;Now in the first session we update NAME column of T_D the using corresponding&lt;br /&gt;value of T_S (ordinar UPDATE):&lt;br /&gt;&lt;br /&gt;SQL&gt; update t_d&lt;br /&gt;2 set name =&lt;br /&gt;3 (select name from t_s where t_s.on_column = t_d.on_column)&lt;br /&gt;4 where exists&lt;br /&gt;5 (select null from t_s where t_s.on_column = t_d.on_column)&lt;br /&gt;6 /&lt;br /&gt;&lt;br /&gt;Naturally it hangs until COMMIT in the second session:&lt;br /&gt;&lt;br /&gt;SQL&gt; update t_d set on_column = 50 where id = 3;&lt;br /&gt;&lt;br /&gt;1 row updated.&lt;br /&gt;&lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;Now we have the results of UPDATE:&lt;br /&gt;&lt;br /&gt;SQL&gt; update t_d&lt;br /&gt;2 set name =&lt;br /&gt;3 (select name from t_s where t_s.on_column = t_d.on_column)&lt;br /&gt;4 where exists&lt;br /&gt;5 (select null from t_s where t_s.on_column = t_d.on_column)&lt;br /&gt;6 /&lt;br /&gt;&lt;br /&gt;3 rows updated.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t_d;&lt;br /&gt;&lt;br /&gt;ID ON_COLUMN NAME&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;1 20 table 1&lt;br /&gt;2 20 table 1&lt;br /&gt;3 50&lt;br /&gt;4 20 table 1&lt;br /&gt;&lt;br /&gt;This is exactly what Oracle does using the principle of "serialized snapshot" for&lt;br /&gt;DML statements. The same results we have for updatable in-line wiew:&lt;br /&gt;&lt;br /&gt;SQL&gt; update&lt;br /&gt;2 (select t_s.name sname, t_d.name dname&lt;br /&gt;3 from t_s, t_d where t_s.on_column = t_d.on_column)&lt;br /&gt;4 set dname = sname&lt;br /&gt;5 /&lt;br /&gt;&lt;br /&gt;3 rows updated.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t_d;&lt;br /&gt;&lt;br /&gt;ID ON_COLUMN NAME&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;1 20 table 1&lt;br /&gt;2 20 table 1&lt;br /&gt;3 50&lt;br /&gt;4 20 table 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;MERGE works differ. Compare the results after the same actions:&lt;br /&gt;&lt;br /&gt;SQL&gt; merge into t_d&lt;br /&gt;2 using t_s&lt;br /&gt;3 on (t_s.on_column = t_d.on_column)&lt;br /&gt;4 when matched then&lt;br /&gt;5 update set t_d.name = t_s.name&lt;br /&gt;6 /&lt;br /&gt;&lt;br /&gt;4 rows merged.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t_d;&lt;br /&gt;&lt;br /&gt;ID ON_COLUMN NAME&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;1 20 table 1&lt;br /&gt;2 20 table 1&lt;br /&gt;3 50 table 1&lt;br /&gt;4 20 table 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Constituing on Tom Kyte's explanation what MERGE statement emulates the code&lt;br /&gt;&lt;br /&gt;for v in (select .... ) loop&lt;br /&gt;update ....;&lt;br /&gt;if sql%rowcount = 0 then&lt;br /&gt;insert ....;&lt;br /&gt;end if;&lt;br /&gt;end loop;&lt;br /&gt;&lt;br /&gt;we can think what MERGE operations work like WHERE CURRENT OF construction.&lt;br /&gt;Oracle doesn't specify the details of the MERGE realization but&lt;br /&gt;in favor of this theory two facts say. The first one is what&lt;br /&gt;UPDATE clause of MERGE can't update joined columns of a destination table&lt;br /&gt;- that is not prohibited in an ordinar UPDATE. The second fact is&lt;br /&gt;we can force MERGE to work like an ordinar UPDATE using row-level triggers:&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace trigger force_like_update&lt;br /&gt;2 before update on t_d&lt;br /&gt;3 for each row&lt;br /&gt;4 declare&lt;br /&gt;5 on_column t_d.on_column%type;&lt;br /&gt;6 begin&lt;br /&gt;7 on_column := :old.on_column;&lt;br /&gt;8 end;&lt;br /&gt;9 /&lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL&gt; merge into t_d&lt;br /&gt;2 using t_s&lt;br /&gt;3 on (t_s.on_column = t_d.on_column)&lt;br /&gt;4 when matched then&lt;br /&gt;5 update set t_d.name = t_s.name&lt;br /&gt;6 /&lt;br /&gt;&lt;br /&gt;3 rows merged.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from t_d;&lt;br /&gt;&lt;br /&gt;ID ON_COLUMN NAME&lt;br /&gt;---------- ---------- ----------&lt;br /&gt;1 20 table 1&lt;br /&gt;2 20 table 1&lt;br /&gt;3 50&lt;br /&gt;4 20 table 1&lt;br /&gt;&lt;br /&gt;This trigger causes the restart of MERGE because it uses ON_COLUMN value&lt;br /&gt;and there are differ versions of the consistent read block and the current read&lt;br /&gt;block (like it happens in an ordinar UPDATE).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-251406364645224164?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/251406364645224164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=251406364645224164' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/251406364645224164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/251406364645224164'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2007/05/update-option-of-merge-and-ordinar.html' title='UPDATE option of MERGE and ordinar MERGE'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6654852660557505391.post-1068245018481430386</id><published>2007-02-08T23:44:00.000-08:00</published><updated>2007-02-01T04:37:05.322-08:00</updated><title type='text'>CARDINALITY hint, collection and join</title><content type='html'>CARDINALITY hint has an unexpected affect on the join results in Oracle 9.2.0.6:&lt;br /&gt;&lt;br /&gt;The very strange behaviour has been detected for the following case:&lt;br /&gt;&lt;br /&gt;SQL&gt; desc arrayofnumbers&lt;br /&gt;arrayofnumbers TABLE OF NUMBER(20)&lt;br /&gt;&lt;br /&gt;SQL&gt; desc tmp_log&lt;br /&gt;Name Null? Type&lt;br /&gt;----------------------------------------- -------- ----------------------------&lt;br /&gt;LOG VARCHAR2(4000)&lt;br /&gt;&lt;br /&gt;SQL&gt; CREATE OR REPLACE&lt;br /&gt;2 PROCEDURE tsp&lt;br /&gt;3 (&lt;br /&gt;4 root in VARCHAR2,&lt;br /&gt;5 to_processes in arrayofnumbers,&lt;br /&gt;6 targetProcesses out arrayofnumbers&lt;br /&gt;7 )&lt;br /&gt;8 AS&lt;br /&gt;9 str varchar2(4000);&lt;br /&gt;10 BEGIN&lt;br /&gt;11&lt;br /&gt;12 targetProcesses := arrayofnumbers();&lt;br /&gt;13&lt;br /&gt;14 str := ' r0='to_processes.COUNT;&lt;br /&gt;15&lt;br /&gt;16 select column_value BULK COLLECT INTO targetProcesses from TABLE(to_processes) p;&lt;br /&gt;17&lt;br /&gt;18 str := str ' r1='targetProcesses.COUNT;&lt;br /&gt;19&lt;br /&gt;20 SELECT /*+ cardinality(p 1 ) */ process_id BULK COLLECT INTO targetProcesses&lt;br /&gt;21 FROM glob_data i, TABLE(to_processes) p&lt;br /&gt;22 WHERE waiting=1 AND root = tsp.root AND i.process_id=p.column_value;&lt;br /&gt;23&lt;br /&gt;24 str := str ' r2='targetProcesses.COUNT;&lt;br /&gt;25&lt;br /&gt;26 INSERT INTO tmp_log(log)&lt;br /&gt;27 VALUES ( str);&lt;br /&gt;28&lt;br /&gt;29&lt;br /&gt;30 END;&lt;br /&gt;31 /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;2 targetProcesses arrayofnumbers := arrayofnumbers();&lt;br /&gt;3 tP arrayofnumbers;&lt;br /&gt;4 l_root varchar2(100) := 'ASF_MM_ActionCompleted';&lt;br /&gt;5 begin&lt;br /&gt;6&lt;br /&gt;7 select process_id bulk collect into tP from glob_data&lt;br /&gt;8 where waiting=1 AND root = l_root;&lt;br /&gt;9&lt;br /&gt;10 for i in 1..10 loop&lt;br /&gt;11 tsp ( l_root, tP, targetProcesses);&lt;br /&gt;12 commit;&lt;br /&gt;13 end loop;&lt;br /&gt;14&lt;br /&gt;15 end;&lt;br /&gt;16 /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; select * from (select * from tmp_log order by 1 desc) where rownum &lt;=10;&lt;br /&gt;LOG --------------------------------------------------------------------------------&lt;br /&gt;r0=1260 r1=1260 r2=1260&lt;br /&gt;r0=1260 r1=1260 r2=1260&lt;br /&gt;r0=1260 r1=1260 r2=1260&lt;br /&gt;r0=1260 r1=1260 r2=1260&lt;br /&gt;r0=1260 r1=1260 r2=1260&lt;br /&gt;r0=1260 r1=1260 r2=&lt;strong&gt;1236&lt;/strong&gt;&lt;br /&gt;r0=1260 r1=1260 r2=&lt;strong&gt;1236&lt;/strong&gt;&lt;br /&gt;r0=1260 r1=1260 r2=&lt;strong&gt;1236&lt;/strong&gt;&lt;br /&gt;r0=1260 r1=1260 r2=&lt;strong&gt;1236&lt;br /&gt;&lt;/strong&gt;r0=1260 r1=1260 r2=&lt;strong&gt;1236 &lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;br /&gt;The only one user session existed in Oracle at that moment.&lt;br /&gt;This effect has been revealed then new connection is opening in Oracle.&lt;br /&gt;After the first execution of the block the effect can't be revealed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6654852660557505391-1068245018481430386?l=dnikiforov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dnikiforov.blogspot.com/feeds/1068245018481430386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6654852660557505391&amp;postID=1068245018481430386' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/1068245018481430386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6654852660557505391/posts/default/1068245018481430386'/><link rel='alternate' type='text/html' href='http://dnikiforov.blogspot.com/2007/02/cardinality-hint-collection-and-join.html' title='CARDINALITY hint, collection and join'/><author><name>Dmitry Nikiforov</name><uri>http://www.blogger.com/profile/06361853402739371388</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>0</thr:total></entry></feed>
