<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>OraNA :: Oracle News Aggregator</title><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/orana" /><language>en</language><managingEditor>noemail@noemail.org (OraNA.info)</managingEditor><lastBuildDate>Thu, 11 Mar 2010 05:25:13 PST</lastBuildDate><generator>Google Reader http://www.google.com/reader</generator><gr:continuation xmlns:gr="http://www.google.com/schemas/reader/atom/">CKWZx7rUr6AC</gr:continuation><feedburner:info uri="orana" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><description>One place to monitor and read Oracle related weblogs and news sources.</description><feedburner:emailServiceId>orana</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:browserFriendly>OraNA is an RSS and Atom feed aggregator that allows readers to monitor Oracle related weblogs and news sources, all in one place -- Eddie Awad :: http://awads.net/wp/</feedburner:browserFriendly><item><title>Surveying MySQL’s Popular Storage Engines</title><link>http://feedproxy.google.com/~r/orana/~3/aYkbQGmBNlw/</link><category>databasejournal</category><category>mysql</category><category>storage engines</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Sean Hull</dc:creator><pubDate>Thu, 11 Mar 2010 05:00:35 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/deb4e4658e9cc94b</guid><description>&lt;p&gt;In this month’s Database Journal piece we look at the spectrum of MySQL storage engines available, and examine what some of their strengths and weaknesses are.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.databasejournal.com/features/mysql/article.php/3867841/article.htm"&gt;View the article here:  Survey of MySQL Storage Engines&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/xeJj9JcVluNjNlfCocLbEcEWN2Y/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xeJj9JcVluNjNlfCocLbEcEWN2Y/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/xeJj9JcVluNjNlfCocLbEcEWN2Y/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xeJj9JcVluNjNlfCocLbEcEWN2Y/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/aYkbQGmBNlw" height="1" width="1"/&gt;</description><feedburner:origLink>http://oracleopensource.com/2010/03/11/surveying-mysqls-popular-storage-engines/</feedburner:origLink></item><item><title>Jersey 1.1.5.1 released</title><link>http://feedproxy.google.com/~r/orana/~3/a8kXmPLyrDs/jersey-1151-released.html</link><category>jax-rs</category><category>jersey</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Gerard Davison</dc:creator><pubDate>Thu, 11 Mar 2010 04:35:12 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/77e84a09dfb14f4b</guid><description>&lt;p&gt;There is a &lt;a href="http://blogs.sun.com/sandoz/entry/jersey_1_1_5_1"&gt;minor point release&lt;/a&gt; of Jersey that includes a pair of significant bug fixes for weblogic and JDeveloper users. Worth using in preference to 1.1.5. Thanks a lot to Paul for putting this one together.&lt;/p&gt;&lt;div&gt;
&lt;img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/2648073517459434852-367057747299530726?l=kingsfleet.blogspot.com" alt=""&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KingsfleetBlog/~4/Qn7CdYUi7FE" height="1" width="1"&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/1kBE092DZcrhkg2LQ2cJ6HEo5s8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1kBE092DZcrhkg2LQ2cJ6HEo5s8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/1kBE092DZcrhkg2LQ2cJ6HEo5s8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/1kBE092DZcrhkg2LQ2cJ6HEo5s8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/a8kXmPLyrDs" height="1" width="1"/&gt;</description><feedburner:origLink>http://feedproxy.google.com/~r/KingsfleetBlog/~3/Qn7CdYUi7FE/jersey-1151-released.html</feedburner:origLink></item><item><title>New projects – OIF</title><link>http://feedproxy.google.com/~r/orana/~3/VGR8CKlO0Kg/</link><category>Federation</category><category>Middleware</category><category>Oracle</category><category>saml federation middleware</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Espen Barroso-Gomez</dc:creator><pubDate>Thu, 11 Mar 2010 02:27:43 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/56830ddbe44ad641</guid><description>&lt;p&gt;Periods of silence shouldn’t last more than a few weeks.. &lt;/p&gt;
&lt;p&gt;Anyway, I’m busy planning aspects of a Oracle Identity Federation implementation, so that’s the current status. Further out, I’ll supply more details of how OIF is to work with, but if someone has specific OIF related questions, I’d be glad to try to help.&lt;/p&gt;
&lt;p&gt;Version used in our project, by the way, will be 11.1.1.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/fusiontech.wordpress.com/175/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/fusiontech.wordpress.com/175/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/fusiontech.wordpress.com/175/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/fusiontech.wordpress.com/175/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/fusiontech.wordpress.com/175/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/fusiontech.wordpress.com/175/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/fusiontech.wordpress.com/175/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/fusiontech.wordpress.com/175/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/fusiontech.wordpress.com/175/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/fusiontech.wordpress.com/175/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=fusiontech.wordpress.com&amp;amp;blog=4181402&amp;amp;post=175&amp;amp;subd=fusiontech&amp;amp;ref=&amp;amp;feed=1"&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sivbp74v6OgsEwMr_Kpxw9hP-W0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sivbp74v6OgsEwMr_Kpxw9hP-W0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sivbp74v6OgsEwMr_Kpxw9hP-W0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sivbp74v6OgsEwMr_Kpxw9hP-W0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/VGR8CKlO0Kg" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/40cdfe21d326ac8ae555d5e8637e17bd?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://fusiontech.wordpress.com/2010/03/11/new-projects-oif/</feedburner:origLink></item><item><title>Our Oracle Recruitment Team is Growing - Multiple Job Opportunities in Bangalore, India</title><link>http://feedproxy.google.com/~r/orana/~3/zLHTH5v2kRY/our_oracle_recruitment_team_is.html</link><category>High Caliber</category><category>Recruiter</category><category>Recruitment</category><category>David Talamelli</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">david.talamelli</dc:creator><pubDate>Thu, 11 Mar 2010 01:14:34 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/197723fb0499d795</guid><description>&lt;p&gt;&lt;font size="3" color="red"&gt;&lt;strong&gt;&lt;div style="text-align:center"&gt;DON"T GET STUCK IN THE MATRIX&lt;/div&gt;&lt;/strong&gt;&lt;div style="text-align:center"&gt;&lt;strong&gt;SEE YOUR FUTURE&lt;/strong&gt;&lt;/div&gt;&lt;div style="text-align:center"&gt;&lt;strong&gt;VISIT THE ORACLE&lt;/strong&gt;&lt;/div&gt; &lt;/font&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The position(s): CORPORATE RECRUITING RESEARCH ANALYST(S)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ABOUT ORACLE	&lt;/p&gt;

&lt;p&gt;Oracle's business is information--how to manage it, use it, share it, protect it. For three decades, Oracle, the world's largest enterprise software company, has provided the software and services that allow organizations to get the most up-to-date and accurate information from their business systems.&lt;/p&gt;

&lt;p&gt;Only Oracle powers the information-driven enterprise by offering a complete, integrated solution for every segment of the process industry.  When you run Oracle applications on Oracle technology, you speed implementation, optimize performance, and maximize ROI.&lt;br&gt;
Great hiring doesn't happen by accident; it's the culmination of a series of thoughtfully planned and well executed events. At the core of any hiring process is a sourcing strategy.  This is where you come in...&lt;/p&gt;

&lt;p&gt;Do you want to be a part of a world-class recruiting organization that's on the cutting edge of technology?  Would you like to experience a rewarding work environment that allows you to further develop your skills, while giving you the opportunity to develop new skills?   If you answered yes, you've taken your first step towards a future with Oracle.  We are building a Research Team to support our North America Recruitment Team, and we need creative, smart, and ambitious individuals to help us drive our research department forward.  &lt;/p&gt;

&lt;p&gt;Oracle has a track record for employing and developing the very best in the industry.  We invest generously in employee development, training and resources.  Be a part of the most progressive internal recruiting team in the industry.  For more information about Oracle, please visit our Web site at http://www.oracle.com&lt;/p&gt;

&lt;p&gt;Escape the hum drum job world matrix, visit the Oracle and be a part of a winning team, apply today.  &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POSITION:  Corporate Recruiting Research Analyst&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;LOCATION:  Bangalore, India&lt;/p&gt;

&lt;p&gt;RESPONSIBILITIES: &lt;/p&gt;

&lt;p&gt;•Develop candidate pipeline using Web 2.0 sourcing strategies and advanced Boolean Search techniques to support U.S. Recruiting Team for various job functions and levels.   &lt;/p&gt;

&lt;p&gt;•Engage with assigned recruiters to understand the supported business as well as the recruiting requirements; partner with recruiters to meet expectations and deliver a qualified pipeline of candidates.&lt;/p&gt;

&lt;p&gt;•Source candidates to include both active and passive job seekers to provide a strong pipeline of qualified candidates for each recruiter; exercise creativity to find candidates using Oracle's advanced sourcing tools/techniques.&lt;/p&gt;

&lt;p&gt;•Fully evaluate candidate's background against the requirements provided by recruiter, and process leads using ATS (Applicant Tracking System).&lt;/p&gt;

&lt;p&gt;•Manage your efforts efficiently; maintain the highest levels of client satisfaction as well as strong operations and reporting of research activities.&lt;/p&gt;

&lt;p&gt;PREFERRED QUALIFICATIONS:&lt;/p&gt;

&lt;p&gt;•Fluent in English, with excellent written and oral communication skills.&lt;br&gt;
•Undergraduate degree required, MBA or Masters preferred.&lt;br&gt;
•Proficiency with Boolean Search techniques desired.&lt;br&gt;
•Ability to learn new software applications quickly.&lt;br&gt;
•Must be able to accommodate some U.S. evening hours.&lt;br&gt;
•Strong organization and attention to detail skills.&lt;br&gt;
•Prior HR or corporate in-house recruiting experiences a plus.&lt;br&gt;
•The fire in the belly to learn new ideas and succeed.&lt;br&gt;
•Ability to work in team and individual environments.&lt;/p&gt;

&lt;p&gt;This is an excellent opportunity to join Oracle in our Bangalore Offices. Interested applicants can send their resume to &lt;a href="mailto:david.talamelli@oracle.com"&gt;david.talamelli@oracle.com&lt;/a&gt; or contact David on +61 3 8616 3364&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/eM6dMGni_V8rPNLW6sZyrFum13A/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eM6dMGni_V8rPNLW6sZyrFum13A/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/eM6dMGni_V8rPNLW6sZyrFum13A/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eM6dMGni_V8rPNLW6sZyrFum13A/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/zLHTH5v2kRY" height="1" width="1"/&gt;</description><feedburner:origLink>http://blogs.oracle.com/jobsatoracle/2010/03/our_oracle_recruitment_team_is.html</feedburner:origLink></item><item><title>20. - 21. May EMEA Harmony Conference in Tallinn</title><link>http://feedproxy.google.com/~r/orana/~3/xFbAr31Ds9Q/20-21-may-emea-harmony-conference-in.html</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">ilmar.kerm@gmail.com (Ilmar Kerm)</dc:creator><pubDate>Wed, 10 Mar 2010 23:03:00 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/ab4f2a45cc30c025</guid><description>&lt;p&gt;An excellent joint conference for Estonian, Finnish, Latvian and Russian Oracle User Groups in Tallinn, 20. - 21. May 2010.&lt;br&gt;
Speakers also include Tom Kyte, Tanel Põder, Chris J. Date and Steven Feuerstein.&lt;/p&gt;

&lt;p&gt;Read the agenda &lt;a href="http://ougf.fi/index.php?option=com_docman&amp;amp;task=doc_download&amp;amp;gid=305&amp;amp;Itemid=42%E2%8C%A9%3Dfi&amp;amp;lang=en"&gt;here&lt;/a&gt; and register in &lt;a href="http://ougf.fi/index.php?lang=en"&gt;OUGF home page&lt;/a&gt; (250€+VAT registration fee).&lt;/p&gt;

&lt;p&gt;In addition, just before the conference, 17.-18. May, Chris Date will perform his "How to Write Correct SQL and Know It: A Relational Approach to SQL" seminar in Helsinki. &lt;a href="http://ougf.fi/index.php?option=com_content&amp;amp;view=article&amp;amp;id=85%3Acj-date-seminaari&amp;amp;catid=923&amp;amp;Itemid=2&amp;amp;lang=en"&gt;More info here&lt;/a&gt; and more detailed information &lt;a href="http://www.kantamestarit.fi/Files/Chris_Date_Seminar_Helsinki_2010.pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;div&gt;&lt;img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/8455543779867801145-1881008695708393911?l=ilmarkerm.blogspot.com" alt=""&gt;&lt;/div&gt;&lt;div&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ilmarkerm?a=xFbAr31Ds9Q:4E9TTGhnW7c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ilmarkerm?d=yIl2AUoC8zA" border="0"&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ilmarkerm?a=xFbAr31Ds9Q:4E9TTGhnW7c:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ilmarkerm?i=xFbAr31Ds9Q:4E9TTGhnW7c:F7zBnMyn0Lo" border="0"&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ilmarkerm?a=xFbAr31Ds9Q:4E9TTGhnW7c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ilmarkerm?i=xFbAr31Ds9Q:4E9TTGhnW7c:V_sGLiPBpWU" border="0"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/vUUKDFjmlIhgJf0hsDnNQh1k3vA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vUUKDFjmlIhgJf0hsDnNQh1k3vA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/vUUKDFjmlIhgJf0hsDnNQh1k3vA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/vUUKDFjmlIhgJf0hsDnNQh1k3vA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/xFbAr31Ds9Q" height="1" width="1"/&gt;</description><feedburner:origLink>http://ilmarkerm.blogspot.com/2010/03/20-21-may-emea-harmony-conference-in.html</feedburner:origLink></item><item><title>What does ‘extended’ mean when referring to RAC clusters</title><link>http://feedproxy.google.com/~r/orana/~3/lXgvwN0Gct8/</link><category>Oracle</category><category>extended</category><category>extended RAC</category><category>RAC</category><category>rac distance between nodes</category><category>stretched cluster</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">John Hallas</dc:creator><pubDate>Thu, 11 Mar 2010 00:12:19 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/2aa76bb7cc39d134</guid><description>&lt;p&gt;Yesterday I attended and spoke at the Scotland DBA SIG. It was an enjoyable day with not one but two presentation from Julian Dyke. The second one was an overview of HA options and it in Julian mentioned extended RAC. Now I have worked at several sites which have used or wanted to use extended RAC and I have heard a number of views on what exactly the term extended means.&lt;/p&gt;
&lt;p&gt;From MoS Note 220970.1 we get the definitive statement on the maximum distance for extended RAC&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The high impact of latency create practical limitations as to where this architecture can be deployed. While there is not fixed distance limitation, the additional latency on round trip on I/O and a one way cache fusion will have an affect on performance as distance increases. For example tests at 100km showed a 3-4 ms impact on I/O and 1 ms impact on cache fusion, thus the farther distance is the greater the impact on performance. This architecture fits best where the 2 datacenters are relatively close (&amp;lt;~25km) and the impact is negligible. Most customers implement under this distance w/ only a handful above and the farthest known example is at 100km. Largest distances than the commonly implemented may want to estimate or measure the performance hit on their application before implementing. Do ensure a proper setup of SAN buffer credits to limit the impact of distance at the I/O layer.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So we now have figures that 100Km is technically possible but probably won’t work well and most sites use less than 25Km. Co-incidentally in the paragraph above that quotation it refers to SE RAC (10g) as being limited to having all nodes located in the same room.  So in two lines of text we have just gone from a 100ft to 100KMs.&lt;/p&gt;
&lt;p&gt;Yesterday Julian’s answer when I asked him the question was that he thought extended was defined as a couple of kilometres or above which is reasonable. However I am thinking that a better definition of extended is not to focus on distance of nodes apart but on what connects the nodes. My thoughts are that that if fibre channel or DWDM is involved and the nodes are not in the same building  then that would count as an extended RAC cluster despite potentially only being a 100 yards apart. Another view would be that if some form of array based mirroring is in place and RAC technology is in place then that again would define the system as ‘extended’ .&lt;/p&gt;
&lt;p&gt;Now that all fits with a view I have heard from a well-known RAC consultant from Oracle who defines any RAC system where nodes are in different rooms as extended.  I have never subscribed to that theory but I can see that the definition I provided in the previous paragraph would lead to that conclusion.&lt;/p&gt;
&lt;p&gt;Now we just need to consolidate on the word ‘extended’ rather than the use of  ’stretched’ which I see occasionally.&lt;/p&gt;
&lt;p&gt;I really do hope I get some comments on this piece as I am sure it is worthy of debate and comment and anything that helps to create a shared view on the matter can only be good.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jhdba.wordpress.com/570/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jhdba.wordpress.com/570/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jhdba.wordpress.com&amp;amp;blog=1223552&amp;amp;post=570&amp;amp;subd=jhdba&amp;amp;ref=&amp;amp;feed=1"&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/rsrdQzFqL5e7Xg3eHRo1uZUxrWY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rsrdQzFqL5e7Xg3eHRo1uZUxrWY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/rsrdQzFqL5e7Xg3eHRo1uZUxrWY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rsrdQzFqL5e7Xg3eHRo1uZUxrWY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/lXgvwN0Gct8" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://1.gravatar.com/avatar/bd215d7013d9c1179651d5524f40b981?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://jhdba.wordpress.com/2010/03/11/what-does-extended-mean-when-referring-to-rac-clusters/</feedburner:origLink></item><item><title>Configuring Lost Password management in Oracle Access Manager</title><link>http://feedproxy.google.com/~r/orana/~3/BYyj_Wd8rCY/</link><category>idm</category><category>oam</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mahendra</dc:creator><pubDate>Wed, 10 Mar 2010 22:40:08 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/b8b87019d3ac3677</guid><description>&lt;p&gt;If you want to configure Lost password policy management using Oracle Access Manager for your application, then here it is how you can do. In real time scenario, once the user clicks on Lost Password link, the user will be challenged with questions registered during first login, followed by a change password.&lt;/p&gt;
&lt;p&gt;How can we do this?&lt;/p&gt;
&lt;p&gt;1. Create an LDIF schema file with a new auxiliary object class and two new attributes as shown below.  Here only 1 challenge attribute is used, if your requirement demands, you can add many more.&lt;/p&gt;
&lt;p&gt;dn: cn=schema&lt;br&gt;
changetype: modify&lt;br&gt;
add: attributetypes&lt;br&gt;
attributetypes: ( 1.3.6.1.4.1.9999.1.1094.204 NAME ‘Challenge2′ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15  )&lt;br&gt;
 &lt;br&gt;
dn: cn=schema&lt;br&gt;
changetype: modify&lt;br&gt;
add: attributetypes&lt;br&gt;
attributetypes: ( 1.3.6.1.4.1.9999.1.1094.205 NAME ‘Response2′ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15  )&lt;/p&gt;
&lt;p&gt;dn: cn=schema&lt;br&gt;
changetype: modify&lt;br&gt;
add: objectclasses&lt;br&gt;
objectclasses: ( 1.3.6.1.4.1.9999.1.1094.206 NAME ‘oblixAuxPerson4LPM’ DESC ‘User defined objectclass’ SUP top AUXILIARY MAY ( Challenge2 $ Response2 ) )&lt;/p&gt;
&lt;p&gt;2. Import the LDIF file into the LDAP where OAM stores user data.&lt;/p&gt;
&lt;p&gt;3. Now we have to configure this new object class in the OAM. Goto the Identity System Console and click on Common Configuration tab. Click on object classes in left pane and Add the new class by selecting the type as Person object class.&lt;/p&gt;
&lt;p&gt;4. Goto Identity System Console -&amp;gt; System Configuration. Click password policy.&lt;/p&gt;
&lt;p&gt;5. Enter the URL for &lt;strong&gt;Lost Password Redirect URL&lt;/strong&gt;. Please remember to enable both checkboxes of &lt;strong&gt;Successful Attempts Attribute &lt;/strong&gt;and &lt;strong&gt;Failed Attempts Attribute &lt;/strong&gt;with attributes oblastSuccessFulLogin and oblastFailedLogin respectively.&lt;/p&gt;
&lt;p&gt;6. Click on Lost Password Policy.&lt;/p&gt;
&lt;p&gt;7. Enter the name. You can specify the challenge phrases to be user defined or pre-defined or both. If you wish to have pre-defined, then Enter the challenge phrase in the text box and click Add.&lt;/p&gt;
&lt;p&gt;8. Enter the values for &lt;strong&gt;Minimum Challenges to be configured&lt;/strong&gt; which specifies the no. of challenge phrases that will appear.&lt;/p&gt;
&lt;p&gt;9. Enter values for &lt;strong&gt;Challenge Response Minimum Length &lt;/strong&gt;and &lt;strong&gt;Allow Duplicate Responses &lt;/strong&gt;appropriately.&lt;/p&gt;
&lt;p&gt;10. Enter value for &lt;strong&gt;Minimum Challenges to be Answered &lt;/strong&gt;which specifies the no. of challenges that user has to answer.&lt;/p&gt;
&lt;p&gt;11. Select value for &lt;strong&gt;Challenge Pose Type. All at Once &lt;/strong&gt;allows all the challenge phrases to appear at the same time. &lt;strong&gt;One after the other &lt;/strong&gt;allows the challenges to appear after the user answers the first question.&lt;/p&gt;
&lt;p&gt;12. Enable &lt;strong&gt;Send Email after password change &lt;/strong&gt;if you want to configure SMTP stuff.&lt;/p&gt;
&lt;p&gt;13. In the end, enable check box of password policy.&lt;/p&gt;
&lt;p&gt; In your custom application, you can insert the Lost Password link as shown below.&lt;/p&gt;
&lt;p&gt;&lt;a href="javascript:void(0);"&gt;http://machinename:portnumber/identity/oblix/apps/lost_pwd_mgmt/bin/lost_pwd_mgmt.cgi?program=passwordChallengeResponse&amp;amp;login=%scheme1_uid_parameter_value%%scheme2_uid_parameter_value%%schemeN_uid_parameter_value%&amp;amp;target=top&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;14. To enable the password policies to the resources protected by the OAM, then modify the authentication scheme that protects those resources. In the validate_password plugin of your Authentication Scheme, add this &lt;strong&gt;obReadPasswdMode=”LDAP”,obWritePasswdMode=”LDAP” &lt;/strong&gt;and the new validate_password plugin appears like this&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;obCredentialPassword=”password”,obReadPasswdMode=”LDAP”, obWritePasswdMode=”LDAP”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more information, &lt;a href="http://onlineappsdba.com/index.php/2010/02/19/oam-password-policies/"&gt;check this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://download.oracle.com/docs/cd/E10761_01/doc/oam.1014/b32419/idconfig.htm#BABDFCGI"&gt;Oracle Documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://onlineappsdba.com/?p=1559&amp;amp;akst_action=share-this" title="E-mail this, post to del.icio.us, etc." rel="nofollow"&gt;Share This&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sK7oiiSZdnWn4xQBUYl_C9r2Qcw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sK7oiiSZdnWn4xQBUYl_C9r2Qcw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sK7oiiSZdnWn4xQBUYl_C9r2Qcw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sK7oiiSZdnWn4xQBUYl_C9r2Qcw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/BYyj_Wd8rCY" height="1" width="1"/&gt;</description><feedburner:origLink>http://onlineappsdba.com/index.php/2010/03/11/configuring-lost-password-management-in-oracle-access-manager/</feedburner:origLink></item><item><title>WebCenter Spaces 11g - UI Customization</title><link>http://feedproxy.google.com/~r/orana/~3/tVraJIn6w5k/webcenter_spaces_11g_-_ui_cust.html</link><category>Enterprise 2.0</category><category>Spaces</category><category>Web 2.0</category><category>WebCenter</category><category>spaces</category><category>ui customization</category><category>web 2.0</category><category>webcenter</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">john.brunswick</dc:creator><pubDate>Wed, 10 Mar 2010 20:59:58 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/2a943b5c71b2458b</guid><description>&lt;p&gt;When developing on top of a portal platform to support an intranet or extranet, a portion of the development time is spent adjusting the out-of-box user templates to adjust the look and feel of the platform for your organization.  Generally your deployment will not need to look like anything like the sites posted on &lt;a href="http://cssremix.com/"&gt;http://cssremix.com/&lt;/a&gt; or &lt;a href="http://www.webcreme.com/"&gt;http://www.webcreme.com/&lt;/a&gt;, but will meet business needs by adjusting basic elements like navigation, color palate and logo placement.&lt;/p&gt;

&lt;p&gt;After spending some time doing custom UI development with WebCenter Spaces 11G I have gathered a few tips that I hope can help to speed anyone's efforts to quickly "skin" a WebCenter Spaces deployment.  A detailed white paper was released that outlines a technique to quickly update the UI during runtime - &lt;a href="http://www.oracle.com/technology/products/webcenter/pdf/owcs_r11120_cust_skins_runtime_wp.pdf"&gt;http://www.oracle.com/technology/products/webcenter/pdf/owcs_r11120_cust_skins_runtime_wp.pdf&lt;/a&gt;.  Customizing at "runtime" means using CSS and images to adjust the page layout and feel, which when creatively done can change the pages drastically.&lt;/p&gt;

&lt;p&gt;WebCenter also allows for detailed templates to manage the placement of major page elements like menus, sidebar, etc, but by adjusting only images and CSS we can end up with something like the custom solution shown below.&lt;/p&gt;

&lt;p&gt;&lt;span style="display:inline"&gt;&lt;img alt="Overview620.png" src="http://blogs.oracle.com/fusionecm/Overview620.png" width="620" height="316" style="float:left;margin:0 20px 20px 0"&gt;&lt;/span&gt;&lt;br&gt;
&lt;span style="display:inline"&gt;&lt;a href="http://blogs.oracle.com/fusionecm/assets_c/2010/03/Overview-4409.html"&gt;view large image&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Let's dive right in and take a look at some tools to make our efforts more efficient.&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/QEFubF4JhHCbhO0QYT4D8-GO_AE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QEFubF4JhHCbhO0QYT4D8-GO_AE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/QEFubF4JhHCbhO0QYT4D8-GO_AE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QEFubF4JhHCbhO0QYT4D8-GO_AE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/tVraJIn6w5k" height="1" width="1"/&gt;</description><feedburner:origLink>http://blogs.oracle.com/fusionecm/2010/03/webcenter_spaces_11g_-_ui_cust.html</feedburner:origLink></item><item><title>Select Statement Causing an ORA-00001?</title><link>http://feedproxy.google.com/~r/orana/~3/ml-lLr4kQVQ/</link><category>General Administration</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Charles Hooper</dc:creator><pubDate>Wed, 10 Mar 2010 22:00:24 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/5b600d4bd5d6064a</guid><description>&lt;p&gt;March 11, 2010&lt;/p&gt;
&lt;p&gt;Sometimes I receive seemingly interesting emails showing Oracle errors – leaving me to ponder… certainly, that can’t cause an error, can it?  Here is one that I received a year ago (paraphrased):&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The commercially developed application that we are using displayed an error message identifying a SELECT statement as the source of an ORA-00001 error.  What is the source of the Oracle constraint error?  The error message displayed by the application is as follows:&lt;/p&gt;
&lt;pre&gt;select account_period
from PROJECT_SUMMARY
where project_id = :m_saProjSumProjectID[nProjSumIndex]
and id =:m_saProjSumSavedID[nProjSumIndex]
into :nACCOUNT_PERIOD

ORA-00001: unique constraint (TESTUSER.SYS_C006354) violated

This transaction has resulted in violating an Oracle defined constraint.
Constraints are enforced by the database manager.  This transaction has
been rolled back.&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;My first thought at the sight of this error was that the commercially developed application was actually displaying one of the SQL statements that was executed after the SQL statement which triggered the primary key violation.  It is easy to let a runtime error slide through for a period of time before the program notices that an error happened – maybe it is just a sign of sloppy programming (I hope not, because this has happened in some of my custom-developed programs too).&lt;/p&gt;
&lt;p&gt;How would we start to troubleshoot this error message?  The “SYS_C” portion of the constraint name indicates that the constraint is most likely a system generated constraint name, probably intended to enforce a uniqueness requirement for a primary key column.  Exporting the data from the database using Datapump export (or the legacy exp utility) and importing the data into a new database could cause the number following “SYS_C” to change, and it is likely that constraint SYS_C006354 in my database (that is used by the same application) is very different from that of the person who posed the question to me.&lt;/p&gt;
&lt;p&gt;Let’s see if we are able to find the answer by working the problem in a circular fashion.  For example, let’s find the name of the index that is used to enforce the primary key constraint on of one the application’s tables:&lt;/p&gt;
&lt;pre&gt;SELECT
  INDEX_NAME
FROM
  DBA_INDEXES
WHERE
  INDEX_NAME LIKE 'SYS%'
  AND TABLE_NAME='INVENTORY_TRANS';

INDEX_NAME
-----------
SYS_C005168&lt;/pre&gt;
&lt;p&gt;Now that we know that the index is named SYS_C005168, we could do something like this:&lt;/p&gt;
&lt;pre&gt;SELECT
  DC.OWNER,
  DC.CONSTRAINT_NAME,
  DC.CONSTRAINT_TYPE,
  DC.TABLE_NAME,
  DC.STATUS,
  SUBSTR(DCC.COLUMN_NAME,1,30) COLUMN_NAME
FROM
  DBA_CONSTRAINTS DC,
  DBA_CONS_COLUMNS DCC
WHERE
  DC.CONSTRAINT_NAME='SYS_C005168'
  AND DC.OWNER='TESTUSER'
  AND DC.OWNER=DCC.OWNER
  AND DC.CONSTRAINT_NAME=DCC.CONSTRAINT_NAME
ORDER BY
  DCC.POSITION;

OWNER   CONSTRAINT_NAME  CONSTRAINT_TYPE  TABLE_NAME       STATUS   COLUMN_NAME
------  ---------------  ---------------  ---------------  -------  --------------
SYSADM  SYS_C005168      P                INVENTORY_TRANS  ENABLED  TRANSACTION_ID&lt;/pre&gt;
&lt;p&gt;The above output shows that the primary key constraint SYS_C005168 enforces the uniqueness of the primary key (TRANSACTION_ID) column in the table INVENTORY_TRANS.  We just demonstrated that we now know what we already mostly knew.&lt;/p&gt;
&lt;p&gt;In the case of the person who sent the email to me, the table name was not known.  So, we could take the last of the above SQL statements and substitute SYS_C006354 in place of SYS_C005168 to find the table name and primary key column that was violated.  If the SQL statement failed to return usable information the next step might be to &lt;a href="http://hoopercharles.wordpress.com/2009/12/01/10046-extended-sql-trace-interpretation/"&gt;enable a 10046 trace&lt;/a&gt; at level 4 for one of the affected sessions, and try to reproduce the problem.  A 10046 trace will list the sequence of events that led up to the error message appearing in the client application.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/hoopercharles.wordpress.com/1665/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/hoopercharles.wordpress.com/1665/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hoopercharles.wordpress.com&amp;amp;blog=10738606&amp;amp;post=1665&amp;amp;subd=hoopercharles&amp;amp;ref=&amp;amp;feed=1"&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sjNfw5MVOuG-ORtxPwLY7Jw9Jk4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sjNfw5MVOuG-ORtxPwLY7Jw9Jk4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sjNfw5MVOuG-ORtxPwLY7Jw9Jk4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sjNfw5MVOuG-ORtxPwLY7Jw9Jk4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/ml-lLr4kQVQ" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://1.gravatar.com/avatar/feb0aae889ae283c65d94de211e7f8d9?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://hoopercharles.wordpress.com/2010/03/11/select-statement-causing-an-ora-00001/</feedburner:origLink></item><item><title>Lesson learned today</title><link>http://feedproxy.google.com/~r/orana/~3/GMiVxfzOT70/lesson-learned-today.html</link><category>disasters</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jeffrey Kemp</dc:creator><pubDate>Wed, 10 Mar 2010 21:35:39 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/b604a91cb65ec2d7</guid><description>Comment out all &amp;quot;DROP TABLE&amp;quot; commands in my scripts.(I accidentally hit F5 when the focus was in the wrong window - which happened to contain a &amp;quot;DROP TABLE / CREATE TABLE&amp;quot; script - my Toad session goes and happily drops the table that I&amp;#39;d been gradually accumulating statistics into for the past 3 days - and no, there&amp;#39;s no flashback table in 9i)At least I kept all my scripts - rerunning them all
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/imztQUj4zt10ad7a_wb21RCFD1s/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/imztQUj4zt10ad7a_wb21RCFD1s/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/imztQUj4zt10ad7a_wb21RCFD1s/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/imztQUj4zt10ad7a_wb21RCFD1s/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/GMiVxfzOT70" height="1" width="1"/&gt;</description><feedburner:origLink>http://jeffkemponoracle.blogspot.com/2010/03/lesson-learned-today.html</feedburner:origLink></item><item><title>ODTUG Kaleidoscope Conference 2010</title><link>http://feedproxy.google.com/~r/orana/~3/Xj544-qS7e4/odtug-kaleidoscope-conference-2010.html</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Optimizer Development Group</dc:creator><pubDate>Wed, 10 Mar 2010 19:57:40 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/c384ec7fde0b9367</guid><description>&lt;strong style="font-weight:normal"&gt;&lt;a style="font-weight:bold" href="http://www.odtugkaleidoscope.com/index.html"&gt;ODTUG Kaleidoscope 2010&lt;/a&gt;, &lt;span style="font-weight:bold"&gt;June 27 - July 1st Washington DC&lt;/span&gt; &lt;/strong&gt;&lt;span&gt;is a great conference for   Oracle developers and architects, offering the best content by renowned experts&lt;/span&gt;&lt;strong style="font-weight:normal"&gt;. &lt;/strong&gt;We will have the privilege of delivering two Optimizer sessions this year,  '&lt;a href="http://www.odtugkaleidoscope.com/databasedevelopment.html#colgan"&gt;Explaining the Explain plan&lt;/a&gt;' and '&lt;a href="http://www.odtugkaleidoscope.com/databasedevelopment.html#colgan"&gt;Finally Plan Stability during Database Upgrade with SQL Plan Management&lt;/a&gt;'. In the Explain the Explain plan session we will discuss each aspect of an &lt;a href="http://optimizermagic.blogspot.com/2008/02/displaying-and-reading-execution-plans.html"&gt;execution plan&lt;/a&gt; (from selectivity to parallel execution), explain what information you should be getting from the plan, and how it will affects the execution.  While in the &lt;a href="http://optimizermagic.blogspot.com/2009/01/plan-regressions-got-you-down-sql-plan.html"&gt;SQL Plan Management&lt;/a&gt; session, we will will give detailed instructions on how to capture your existing execution plans before you upgrade to 11g, as well as an in-depth discussion on what to expect from the Optimizer after you upgrade to 11&lt;em&gt;g&lt;/em&gt;.&lt;br&gt;ODTUG  is a great conference where you can learn lots in a fun and casual atmosphere.   Looking forward to seeing some of you there!&lt;div&gt;&lt;img width="1" height="1" src="https://blogger.googleusercontent.com/tracker/3086558868352622884-6754966356556914384?l=optimizermagic.blogspot.com" alt=""&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/SsQWXwaGRQALeF50e5ogWBq7vKk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SsQWXwaGRQALeF50e5ogWBq7vKk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/SsQWXwaGRQALeF50e5ogWBq7vKk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SsQWXwaGRQALeF50e5ogWBq7vKk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/Xj544-qS7e4" height="1" width="1"/&gt;</description><feedburner:origLink>http://optimizermagic.blogspot.com/2010/03/odtug-kaleidoscope-conference-2010.html</feedburner:origLink></item><item><title>Greetings!!!!</title><link>http://feedproxy.google.com/~r/orana/~3/iEnRcqUoMc0/greetings.html</link><category>Presentations</category><category>Webcasts</category><category>welcome</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">alex.blyth@oracle.com</dc:creator><pubDate>Wed, 10 Mar 2010 19:24:41 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/83045e04c24a8080</guid><description>Greetings everyone!&lt;br&gt;&lt;br&gt;If you're reading this, hopefully it's because you have been following our series of webcasts on Oracle 11gR2 that we've been hosting on Wordpress. If you found us some other way, well that's even better - the more the merrier as they say.&lt;br&gt;&lt;br&gt;In either case, welcome to our new blog!!! Over the next few days, Ill move the old posts from wordpress to here its all in the one location.&lt;br&gt;&lt;br&gt;Right! Who are we? The authors of this blog are the ANZ Inside Consulting Team.&lt;br&gt;&lt;br&gt;Currently, this is made of of:&lt;br&gt;&lt;ol&gt;&lt;li&gt;Tom Jurcic&lt;/li&gt;&lt;li&gt;Yasin Mohammed&lt;/li&gt;&lt;li&gt;Andrew Clarke&lt;/li&gt;&lt;li&gt;Rene Poels and &lt;br&gt;&lt;/li&gt;&lt;li&gt;me - Alex Blyth&lt;/li&gt;&lt;/ol&gt;Basically, our role in Oracle is to help users of our technologies get the most of their existing investments as well as what's new, old, blue, what have you...&lt;br&gt;&lt;br&gt;Ideally, this is all going to be technical in nature and not of a marketing nature (we'll leave the marketing up to others).&lt;br&gt;&lt;br&gt;For now, there's obviously not much here. But that won't last too long. In the mean time, those who are interested can find replays and slides of our previous webcasts on the "&lt;a href="http://blogs.oracle.com/techtalk/oracle11gwebcasts/oracle_11g_webcast_series.html"&gt;Oracle 11g Webcasts&lt;/a&gt;" page.&lt;br&gt;&lt;br&gt;Till next time&lt;br&gt;&lt;br&gt;Alex&lt;br&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/VVTTRpoRmlbg0X9YnU_LGaaIfAc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VVTTRpoRmlbg0X9YnU_LGaaIfAc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/VVTTRpoRmlbg0X9YnU_LGaaIfAc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VVTTRpoRmlbg0X9YnU_LGaaIfAc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/iEnRcqUoMc0" height="1" width="1"/&gt;</description><feedburner:origLink>http://blogs.oracle.com/techtalk/2010/03/greetings.html</feedburner:origLink></item><item><title>Clustering Factor: Row Migration’s Victim</title><link>http://feedproxy.google.com/~r/orana/~3/uGxJwbRv5jY/</link><category>Performance</category><category>database</category><category>oracle</category><category>sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 09 Mar 2010 02:15:46 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/fffa95c0fa10e8d9</guid><description>&lt;p&gt;This article describes the effects of a high row migration rate on the clustering factor and the optimizer’s ability to select the best execution plan. &lt;/p&gt;
&lt;p&gt;In my previous article—&lt;a href="http://blog.fatalmind.com/2010/02/23/row-migration-and-row-movement/"&gt;Row Migration and Row Movement&lt;/a&gt;—I have demonstrated that the “insert empty, update everything” anti-pattern can lead to 100% row migration. This article continues the research on row migration and unveils surprising effects on the clustering factor. To be precise, the clustering factor can become &lt;em&gt;completely bogus&lt;/em&gt; in presence of a very high row migration rate. Once the clustering factor is “wrong”, it’s just a finger exercise to construct an optimizer trap and proof that row migration can affect the query plan.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;To start off, I create a table similar to the one in the previous article. However, I add one more column and populate it with random values from 0 to 9. I will need this column to build my optimizer trap later on.&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;CREATE TABLE row_mig1 (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  x NUMBER      NOT NULL,
  filter NUMBER NOT NULL,
  CONSTRAINT row_mig1_pk PRIMARY KEY (x)
) ENABLE ROW MOVEMENT;

BEGIN
   FOR i IN 1..100000 LOOP
      INSERT INTO row_mig1
                    (x, filter)
             VALUES (i, trunc(dbms_random.value(0, 10)));

      UPDATE row_mig1
         SET a =&amp;#39;a&amp;#39;, b = &amp;#39;b&amp;#39;, c = &amp;#39;c&amp;#39;
       WHERE x=i;
   END LOOP;
END;
/
COMMIT;

EXEC DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG1&amp;#39;, CASCADE=&amp;gt;TRUE);&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Up till now everything is very similar to the last article, except that I used &lt;code&gt;DBMS_STATS&lt;/code&gt; instead of &lt;code&gt;ANALYZE TABLE&lt;/code&gt;. Although we know exactly that almost every row was migrated, &lt;code&gt;DBMS_STATS&lt;/code&gt; doesn’t care about that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT num_rows, chain_cnt
       FROM user_tables
      WHERE table_name='ROW_MIG1';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
    100000          0

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, let’s have a look at the index:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT num_rows, leaf_blocks, clustering_factor
       FROM user_indexes
      WHERE index_name = 'ROW_MIG1_PK';&lt;/b&gt;

  NUM_ROWS LEAF_BLOCKS CLUSTERING_FACTOR
---------- ----------- -----------------
    100000         187               918

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, that’s an excellent clustering factor. A low clustering factor indicates that the index is in the same sequence as the table. That’s true in that case because the index column corresponds to the order in which the rows were inserted to the table. However, 918, how can this be? The clustering factor is supposed to be between the number of table blocks—which indicates a good clustering factor—and the number of index rows—which is the worst case. So, let’s look at the table size:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE index_name = 'ROW_MIG1_PK';&lt;/b&gt;

TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
------------ ----------------------- ----------
      100877                     918     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;According to that, the lower bound for the clustering factor is higher than the upper bound. Hmm, let’s investigate the clustering factor manually and verify the distribution of the rows across the table blocks:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT * FROM (
       SELECT DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_number
            , COUNT(*) rows_in_block
         FROM row_mig1
        GROUP BY DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)
     ) WHERE ROWNUM &amp;lt;=10;&lt;/b&gt;

BLOCK_NUMBER ROWS_IN_BLOCK
------------ -------------
         523           109
         524           113
         525           109
         526           109
         527           110
         536           109
         537           110
         538           109
         539           109
         540           110

10 rows selected.

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are 109 different &lt;code&gt;ROWIDs&lt;/code&gt; that refer to the block number 523. But we know that each record has about 6k. It is impossible to fit 109 rows into a single block of 8k. However, the “insert empty, update everything” anti-pattern makes it possible. The game goes like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The very first row is inserted into a new block.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first row is updated, and fits into the same block. The free space in that particular block is still more than a kilobyte.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The second row is inserted into the very same block, because there is enough free space available in that block.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The update of the second row triggers the migration of that row to a different block.&lt;/p&gt;
&lt;p&gt;The row migration changes neither the &lt;code&gt;ROWID&lt;/code&gt; nor the index entry. That means that the forwarding address—that is, somehow, the new &lt;code&gt;ROWID&lt;/code&gt;—is stored in the original block so that the index can find the row.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The third row is inserted into the very first table block, again.&lt;/p&gt;
&lt;p&gt;Because the second row was moved into a different block, the very first block has still free space. There is only the first row—as a whole—and one forwarding address stored in that block. So, the insert of the third row can take place there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;And so on. Until the forwarding addresses fill the block—up to &lt;code&gt;PCTFREE&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s a good one, hmm?&lt;/p&gt;
&lt;p&gt;We can even verify that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT * FROM (
       SELECT MIN(X)                               min_x
            , MAX(x)                               max_x
            , MAX(x) - MIN(x) + 1                  diff_x
            , DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) block_number
            , COUNT(*)            rows_in_block
         FROM row_mig1
        GROUP BY DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)
        ORDER BY min_x
     ) WHERE ROWNUM &amp;lt;=10;&lt;/b&gt;

     MIN_X      MAX_X     DIFF_X BLOCK_NUMBER ROWS_IN_BLOCK
---------- ---------- ---------- ------------ -------------
         1        113        113          524           113
       114        222        109          523           109
       223        332        110          537           110
       333        441        109          538           109
       442        550        109          539           109
       551        660        110          540           110
       661        769        109          541           109
       770        878        109          542           109
       879        987        109          543           109
       988       1096        109          525           109

10 rows selected.

SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You see that the first row was inserted into block number 524. All subsequent rows up to the 113&lt;sup&gt;th&lt;/sup&gt; were put into the same block. When that block was finally filled up—with one row and 112 forwarding addresses—the game starts over in the next block. All the &lt;code&gt;INSERT&lt;/code&gt; statements took place in just 918 distinct blocks. Because the &lt;code&gt;ROWID&lt;/code&gt; is assigned during the &lt;code&gt;INSERT&lt;/code&gt;, the subsequent migration of the row due to the &lt;code&gt;UPDATE&lt;/code&gt; is not reflected in the &lt;code&gt;ROWID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Neither &lt;code&gt;DBMS_STATS&lt;/code&gt; nor &lt;code&gt;ANALYSE TABLE&lt;/code&gt; look into the table to check if the row is really there or if the particular block it is just an accumulation of forwarding addresses. From their perspective, all the rows are in the same block—this is how the clustering factor is calculated. Although correctly calculated—technically—and up to date, the clustering factor of this index does not reflect the real situation.&lt;/p&gt;
&lt;p&gt;The “correct” value—in that sense that it reflects the data distribution correctly—for the clustering factor would be 100.000; that is, the number of rows. If the clustering factor equals the number of rows in the index—which is the worst possible case—it means that there are no two adjacent index entries that refer to the same table block. This is actually the case because no 8k block can contain two complete 6k rows.&lt;/p&gt;
&lt;h3&gt;The Clustering Factor is Wrong, So What?&lt;/h3&gt;
&lt;p&gt;So, what’s the problem if the clustering factor is wrong?&lt;/p&gt;
&lt;p&gt;The problem is that the Cost Based Optimizer (CBO) uses the clustering factor in its cost calculation for an &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; (see &lt;a href="http://www.centrexcc.com/Fallacies%20of%20the%20Cost%20Based%20Optimizer.pdf"&gt;Fallacies of the Cost Based Optimizer [pdf]&lt;/a&gt;). That means, the cost of the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; will be too low, because the clustering factor is way too low. Luckily there is an effect that makes all that less problematic; all indexes on that table are affected.&lt;/p&gt;
&lt;p&gt;Let’s make a second index to verify that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;CREATE INDEX row_mig1_idx ON row_mig1(filter);&lt;/b&gt;

Index created.

SQL&amp;gt; &lt;b&gt;BEGIN
       DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG1&amp;#39;,
                                     CASCADE =&amp;gt; TRUE);
     END;
     /&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;SELECT i.index_name         index_name
          , t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE table_name = 'ROW_MIG1';&lt;/b&gt;

INDEX_NAME   TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
------------ ------------ ----------------------- ----------
ROW_MIG1_PK        100877                     918     100000
ROW_MIG1_IDX       100877                    9180     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The clustering factor of the new index is bigger than the one of the original index, but still way off its real value. The “correct” clustering factor for the new index would be 100.000 because there are no two adjacent index entries that refer to the table block. Well, they actually do, but there is nothing more than the forwarding address in those blocks. &lt;/p&gt;
&lt;div&gt;
&lt;h6&gt;Ten Times as High&lt;/h6&gt;
&lt;p&gt;The clustering factor is ten times as high because the filter column has ten distinct value. That means that the adjacent index entries will refer to ten blocks at least, because those entries were not inserted in the same order as they are stored in the index. On the other hand, the index on the &lt;code&gt;filter&lt;/code&gt; column is not only sorted by the &lt;code&gt;filter&lt;/code&gt; value, but also by the &lt;code&gt;ROWID&lt;/code&gt;—as every nonunique index in Oracle—so that the clustering factor is kept at a minimum. Finally, the clustering factor is ten times as high because it refers to ten times as many table blocks, but does not grow above that because the index order keeps the clustering factor at a minimum.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Although the clustering factor does not correctly reflect the efforts to fetch the table rows, it correctly reflects the relation between the two indexes. The primary key index has a lower clustering factor because it has the same sequence as the table itself. On the other side, the index on the &lt;code&gt;filter&lt;/code&gt; column has a different order, thus the value is higher. The side note explains why it is &lt;a href="http://blog.fatalmind.com/#TenTimesAsHigh"&gt;Ten Times as High&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If all indexes are affected, there is hardly any real problem I can see for a single table access. Even queries that can be executed with two different indexes, the CBO will most likely not do wrong because both clustering factors are misleading.&lt;/p&gt;
&lt;h3&gt;The Join Trap&lt;/h3&gt;
&lt;p&gt;If it’s not possible to confuse the optimizer with a single table, let’s use more of them. So, I try to build a trap where the optimizer’s decision of the join order is influenced by the phony clustering factor so that the optimizer takes the less efficient execution plan.&lt;/p&gt;
&lt;p&gt;For that purpose, I build a second table very similar to the first one. There are only two differences:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I don’t follow the “insert empty, update everything” anti-pattern—there will be no row migration.&lt;/li&gt;
&lt;li&gt;The selectivity of the &lt;code&gt;filter&lt;/code&gt; columns is slightly increased (about 6% instead of 10%).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the overall script:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;CREATE TABLE row_mig2 (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  x NUMBER      NOT NULL,
  filter NUMBER NOT NULL,
  CONSTRAINT row_mig2_pk PRIMARY KEY (x)
) ENABLE ROW MOVEMENT;

INSERT INTO row_mig2 (x, filter, a, b, c)
     SELECT level, trunc(dbms_random.value(0,17)), &amp;#39;a&amp;#39;, &amp;#39;b&amp;#39;, &amp;#39;c&amp;#39;
       FROM dual CONNECT BY level &amp;lt;= 100000;
COMMIT;

CREATE INDEX row_mig2_idx ON row_mig2(filter);

EXEC DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG2&amp;#39;, CASCADE=&amp;gt;TRUE);&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now let’s check the statistics:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT i.index_name         index_name
          , t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE table_name = 'ROW_MIG2';&lt;/b&gt;

INDEX_NAME   TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
------------ ------------ ----------------------- ----------
ROW_MIG2_PK        100749                  100000     100000
ROW_MIG2_IDX       100749                  100000     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that the clustering factor is at the upper bound of the expected range; that means, it indicates that there are no two adjacent index entries referring to the same table block. That’s somehow logical, if we consider that every row is in its own table block.&lt;/p&gt;
&lt;p&gt;After that preparation, I can present my “trapQL”:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;SELECT *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is a rather trivial join on the primary keys. Additionally each table is filtered on the &lt;code&gt;filter&lt;/code&gt; column for one particular value. The trap works because a &lt;code&gt;NESTED LOOPS&lt;/code&gt; join is possible in both ways. Either by filtering the first table by an &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; on &lt;code&gt;filter&lt;/code&gt; and then fetch the corresponding entry from the second by a primary key lookup, or vice versa. However, because we—as well as the optimizer—know that the second table is more selective than the first one, the more efficient way to execute that query is to first perform the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; on the second table and then join in the first one. In that way, the number of primary key lookups is reduced and the overall performance will be better. Considering that the first table suffers from heavy row migration, that effect becomes even more relevant. However, it is of course the purpose of the discussion to proof that the optimizer is doing “wrong”:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;SET AUTOTRACE TRACEONLY;
SET TIMING ON;

SELECT /* original clustering factor */ *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;

SET TIMING OFF
SET AUTOTRACE OFF&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the result is:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;577 rows selected.

Elapsed: &lt;b&gt;00:01:08.35&lt;/b&gt;

Execution Plan
----------------------------------------------------------
Plan hash value: 4162018446

---------------------------------------------------------------------
| Id | Operation                     | Name         | Rows  | Cost  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT              |              |  5882 | 10941 |
|  1 |  NESTED LOOPS                 |              |       |       |
|  2 |   NESTED LOOPS                |              |  5882 | 10941 |
|  3 |    TABLE ACCESS BY INDEX ROWID| ROW_MIG1     | 10000 |   &lt;b&gt;938&lt;/b&gt; |
|* 4 |     &lt;b&gt;INDEX RANGE SCAN          | ROW_MIG1_IDX | 10000&lt;/b&gt; |    20 |
|* 5 |    INDEX UNIQUE SCAN          | ROW_MIG2_PK  |     1 |     0 |
|* 6 |   TABLE ACCESS BY INDEX ROWID | ROW_MIG2     |     1 |     1 |
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D1"."FILTER"=0)
   5 - access("D1"."X"="D2"."X")
   6 - filter("D2"."FILTER"=0)

Statistics
----------------------------------------------------------
        913  recursive calls
          0  db block gets
      &lt;b&gt;35584&lt;/b&gt;  consistent gets
      21027  physical reads
          0  redo size
      39012  bytes sent via SQL*Net to client
        837  bytes received via SQL*Net from client
         40  SQL*Net roundtrips to/from client
         12  sorts (memory)
          0  sorts (disk)
        577  rows processed&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The optimizer has chosen to perform the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; on &lt;code&gt;ROW_MIG1_IDX&lt;/code&gt; first. The optimizer is well aware of the fact that the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; will return about 10000 rows; still it was preferred over the alternative execution plan.&lt;/p&gt;
&lt;p&gt;So let’s check what happens if we tell the optimizer the truth about that table’s indexes?&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;BEGIN
  DBMS_STATS.SET_INDEX_STATS(null, &amp;#39;ROW_MIG1_PK&amp;#39;, clstfct=&amp;gt;100000);
  DBMS_STATS.SET_INDEX_STATS(null, &amp;#39;ROW_MIG1_IDX&amp;#39;,clstfct=&amp;gt;100000);
END;
/

SET AUTOTRACE TRACEONLY;
SET TIMING ON;

SELECT /* updated clustering factor */ *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;

SET TIMING OFF
SET AUTOTRACE OFF&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The only change is that the statistics have been manually updated to the “more correct” clustering factor of 100.000. Unfortunately neither &lt;code&gt;DBMS_STATS&lt;/code&gt; nor &lt;code&gt;ANALYZE TABLE&lt;/code&gt; can be used for that purpose, so I did it manually. Please note that the table itself was not changed; most of the rows are still migrated. &lt;/p&gt;
&lt;p&gt;And the result is:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;577 rows selected.

Elapsed: &lt;b&gt;00:00:59.91&lt;/b&gt;

Execution Plan
----------------------------------------------------------
Plan hash value: 3004301745

---------------------------------------------------------------------
| Id | Operation                     | Name         | Rows  | Cost  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT              |              |  5882 | 11780 |
|  1 |  NESTED LOOPS                 |              |       |       |
|  2 |   NESTED LOOPS                |              |  5882 | 11780 |
|  3 |    TABLE ACCESS BY INDEX ROWID| ROW_MIG2     |  5882 |  &lt;b&gt;5896&lt;/b&gt; |
|* 4 |     &lt;b&gt;INDEX RANGE SCAN          | ROW_MIG2_IDX |  5882&lt;/b&gt; |    12 |
|* 5 |    INDEX UNIQUE SCAN          | ROW_MIG1_PK  |     1 |     0 |
|* 6 |   TABLE ACCESS BY INDEX ROWID | ROW_MIG1     |     1 |     1 |
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D2"."FILTER"=0)
   5 - access("D1"."X"="D2"."X")
   6 - filter("D1"."FILTER"=0)

Statistics
----------------------------------------------------------
        913  recursive calls
          0  db block gets
      &lt;b&gt;20695&lt;/b&gt;  consistent gets
      12817  physical reads
          0  redo size
      39012  bytes sent via SQL*Net to client
        837  bytes received via SQL*Net from client
         40  SQL*Net roundtrips to/from client
         12  sorts (memory)
          0  sorts (disk)
        577  rows processed&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The more representative clustering factor makes the optimizer take the expected plan. The filtering takes place on the more selective table first, which matches about 5900 rows. The other table is joined in later. The execution is about 13% faster, the logical and physical gets dropped by about 40% both. &lt;em&gt;That&lt;/em&gt; makes quite a difference.&lt;/p&gt;
&lt;p&gt;The cost of the &lt;code&gt;TABLE ACCESS BY INDEX ROWID&lt;/code&gt; that follows the &lt;code&gt;INDEX RANGE SCAN&lt;/code&gt; reflects the clustering factor’s impact. The second query plan has a cost of about 5900, that actually means that each fetched row will need a block read. The original execution plan had a cost value of 938 for that step, so that the overall cost value was lower.&lt;/p&gt;
&lt;p&gt;After that I must remind the reader that the rows in table &lt;code&gt;ROW_MIG1&lt;/code&gt; are still migrated. The performance difference is not cause by the row migration per se, but by the misleading statistics that result out of the row migration.&lt;/p&gt;
&lt;h3&gt;Correcting the Row Migration&lt;/h3&gt;
&lt;p&gt;To complete the exercise, I will correct the row migration, run the statement again, and compare the performance improvement:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;ALTER TABLE row_mig1 MOVE;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;ALTER INDEX row_mig1_pk REBUILD;&lt;/b&gt;

Index altered.

SQL&amp;gt; &lt;b&gt;ALTER INDEX row_mig1_idx REBUILD;&lt;/b&gt;

Index altered.

SQL&amp;gt; &lt;b&gt;BEGIN
       DBMS_STATS.GATHER_TABLE_STATS(null, &amp;#39;ROW_MIG1&amp;#39;,CASCADE=&amp;gt;TRUE);
     END;
     /&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;SELECT i.index_name         index_name
          , t.blocks             table_blocks
          , i.clustering_factor  index_clustering_factor
          , i.num_rows           index_rows
       FROM user_indexes i
       JOIN user_tables t USING (table_name)
      WHERE table_name = 'ROW_MIG1';&lt;/b&gt;

INDEX_NAME     TABLE_BLOCKS INDEX_CLUSTERING_FACTOR INDEX_ROWS
-------------- ------------ ----------------------- ----------
ROW_MIG1_PK          100506                  100000     100000
ROW_MIG1_IDX         100506                  100000     100000

SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then execute the statement again:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;SET AUTOTRACE TRACEONLY;
SET TIMING ON;

SELECT /* no row migration */ *
  FROM row_mig1 d1
  JOIN row_mig2 d2 ON (d1.x = d2.x)
 WHERE d1.filter = 0
   AND d2.filter = 0;

SET TIMING OFF
SET AUTOTRACE OFF&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because the statistics are almost identical, the plan doesn’t change, nor does the cost. What &lt;em&gt;does&lt;/em&gt; changed is the execution time as well as the number of logical gets:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;577 rows selected.

Elapsed: &lt;b&gt;00:00:30.90&lt;/b&gt;

Execution Plan
----------------------------------------------------------
Plan hash value: 3004301745

---------------------------------------------------------------------
| Id | Operation                     | Name         | Rows  | Cost  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT              |              |  5882 | 11780 |
|  1 |  NESTED LOOPS                 |              |       |       |
|  2 |   NESTED LOOPS                |              |  5882 | 11780 |
|  3 |    TABLE ACCESS BY INDEX ROWID| ROW_MIG2     |  5882 |  &lt;b&gt;5896&lt;/b&gt; |
|* 4 |     &lt;b&gt;INDEX RANGE SCAN          | ROW_MIG2_IDX |  5882&lt;/b&gt; |    12 |
|* 5 |    INDEX UNIQUE SCAN          | ROW_MIG1_PK  |     1 |     0 |
|* 6 |   TABLE ACCESS BY INDEX ROWID | ROW_MIG1     |     1 |     1 |
---------------------------------------------------------------------            

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("D2"."FILTER"=0)
   5 - access("D1"."X"="D2"."X")
   6 - filter("D1"."FILTER"=0)

Statistics
----------------------------------------------------------
        925  recursive calls
          0  db block gets
      &lt;b&gt;14271&lt;/b&gt;  consistent gets
      11952  physical reads
          0  redo size
      39012  bytes sent via SQL*Net to client
        837  bytes received via SQL*Net from client
         40  SQL*Net roundtrips to/from client
         13  sorts (memory)
          0  sorts (disk)
        577  rows processed&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;The article investigates the effects of row migration on the clustering factor and the optimizer. A “proof of concept” SQL demonstrates that row migration can affect the optimizer. The lessons from this article are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The “insert empty, update everything” pattern can lead to a very high row migration rate.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DBMS_STATS&lt;/code&gt; doesn’t populate the &lt;code&gt;CHAIN_CNT.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A high row migration rate can cut the clustering factor, even below its theoretic minimum.&lt;/li&gt;
&lt;li&gt;A wrong clustering factor can affect the optimizer and result in an suboptimal plan.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Disclaimer&lt;/h3&gt;
&lt;p&gt;Please note that this trap was intentionally built, just to proof that row migration can potentially influence the optimizer. There are better ways to tune that particular SQL, foremost a better indexing approach.&lt;/p&gt;
&lt;p&gt;I have put many adjectives in quotation marks because they are not in line with the technical definition of the respective noun.&lt;/p&gt;
&lt;p&gt;The “insert empty, update everything“ anti-pattern was used to create a very high row migration rate. Although that anti-pattern can lead to 100% migration rate, it does not always.&lt;/p&gt;
&lt;p&gt;I have successfully verified my results on Oracle 10gR1, 11gR1 and 11gR2.&lt;/p&gt;
&lt;h3&gt;Thanks&lt;/h3&gt;
&lt;p&gt;Thanks to the guys at &lt;a href="http://www.25th-floor.com"&gt;25th-floor&lt;/a&gt; who verified my results on Oracle 11gR1.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/285/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/285/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=285&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3sq8EBEiyfeXAW0VJiw6qrA1_W4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3sq8EBEiyfeXAW0VJiw6qrA1_W4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3sq8EBEiyfeXAW0VJiw6qrA1_W4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3sq8EBEiyfeXAW0VJiw6qrA1_W4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/uGxJwbRv5jY" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/03/09/clustering-factor-row-migrations-victim/</feedburner:origLink></item><item><title>Row Migration and Row Movement</title><link>http://feedproxy.google.com/~r/orana/~3/KBgbFGOj2-E/</link><category>Performance</category><category>anit-pattern</category><category>oracle</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 23 Feb 2010 02:52:34 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/4f261c2973a3617b</guid><description>&lt;p&gt;The Oracle database knows three distinct processes that are easily mixed up: Row Chaining, Row Migration and Row Movement.&lt;/p&gt;
&lt;p&gt;Luckily all three are well described in excellent articles: &lt;a href="http://www.akadia.com/services/ora_chained_rows.html"&gt;The Secrets of Oracle Row Chaining and Migration&lt;/a&gt; and &lt;a href="http://www.databasejournal.com/features/oracle/article.php/3676401/Row-Movement-in-Oracle.htm"&gt;Row Movement in Oracle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the impatient, I provide some very short definitions:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Row Chaining&lt;/dt&gt;
&lt;dd&gt;Distribution of a single table row across multiple data blocks.&lt;/dd&gt;
&lt;dt&gt;Row Migration&lt;/dt&gt;
&lt;dd&gt;Relocation of an entire table row to a new place, without updating the indexes.&lt;/dd&gt;
&lt;dt&gt;Row Movement&lt;/dt&gt;
&lt;dd&gt;Relocation of an entire table row to a new place and updating the indexes.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;This article was inspired by the question if Oracle 11r2 performs &lt;i&gt;Row Movement&lt;/i&gt; instead of &lt;i&gt;Row Migration&lt;/i&gt; for ordinary &lt;code&gt;UPDATE&lt;/code&gt; statements—that is, in absence of partitions. The short answer is: no, it doesn’t. The long answer is the rest of this article.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;The difference between &lt;i&gt;Row Chaining&lt;/i&gt; and &lt;i&gt;Row Migration&lt;/i&gt; is somehow understandable: If the row doesn’t fit into a single data block, it must be chained; otherwise it can be migrated as a whole. The limitation of the Row Migration is that it does not update the indexes on the table. That means, the &lt;code&gt;ROWID&lt;/code&gt; that is stored in the index still refers to the old location of the row. An additional block, the new location of the row, must be read to fetch the required data.&lt;/p&gt;
&lt;p&gt;The more modern &lt;i&gt;Row Movement&lt;/i&gt; is different as it updates the corresponding indexes—the &lt;code&gt;ROWID&lt;/code&gt; actually changes. This has benefits on the long run, because the additional block read can be avoided in the &lt;i&gt;TABLE ACCESS BY INDEX ROWID&lt;/i&gt; operation. On the short run, the actual &lt;code&gt;UPDATE&lt;/code&gt; operation is much more complex. I suppose this is the reason why a regular update does not (yet) trigger a Row Movement.&lt;/p&gt;
&lt;p&gt;The Row Movement was introduced to support an &lt;code&gt;UPDATE&lt;/code&gt; on a partition key—so that a table row is moved from one table partition to another one. In the meanwhile it is also used for flashback and space management—as described in the &lt;a href="http://www.databasejournal.com/features/oracle/article.php/3676401/Row-Movement-in-Oracle.htm"&gt;above-mentioned article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As of Oracle Release 11g2, Row Movement is optional and disabled per default. It can be enabled per table with a very trivial &lt;code&gt;ALTER TABLE&lt;/code&gt; statement. The usual reason to enable it is that one of the features which require Row Movement is used; partition key update, table shrink and flashback. There is hardly any reason not to enable row movement—the only side effect is that &lt;code&gt;ROWID&lt;/code&gt;’s might change; that should not affect well designed applications.&lt;/p&gt;
&lt;p&gt;Row Migration has some considerable problems, as pointed out in the later in this article. On the other hand, Row Movement has also a drawback; that is, the update of all the affected indexes can be very expansive. It’s a trade off between read and write performance. While Row Movement is more expansive for &lt;code&gt;UPDATES&lt;/code&gt;, it maintains best index performance. In contrast, the Row Migration favors &lt;code&gt;UPDATE&lt;/code&gt; performance over index speed. Although not true for all cases, I believe that most data is read more often than it is written—especially in our modern society where nobody every deletes data—so that Row Movement is generally the better choice.&lt;/p&gt;
&lt;p&gt;The article examines how to get 100% migrated rows with the “insert empty, update everything” anti-pattern, why &lt;code&gt;DBMS_STATS&lt;/code&gt; isn’t a perfect substitute for &lt;code&gt;ANALYZE TABLE&lt;/code&gt;, the immunity of migrated rows to &lt;code&gt;alter table ... shrink space&lt;/code&gt; and why &lt;code&gt;PCTFREE&lt;/code&gt; is still the only rescue from DBAs perspective.&lt;/p&gt;
&lt;p&gt;Just to show what I mean, I repeat a modified version of the test originally created by Tom Kyte and reused by Martin Zahn:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;CREATE TABLE row_migration_demo (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  d CHAR(2000),
  e CHAR(2000),
  x int,
  constraint row_migration_pk primary key (x)
) enable row movement
/&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have changed the size of the CHAR field to 2k because my block size is 8k whereas it was 4k in the original example. I have also put the x column to the end for a better verification of Row Migration versus Row Chaining. Finally I enable Row Movement, which does actually not change anything but allows me to perform an &lt;code&gt;alter table ... shrink space&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next step is a classical insert empty, update everything sequence:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x) values (1);
UPDATE row_migration_demo set a = 'a', b='b', c='c' where x=1;
COMMIT; &lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Up till now everything is perfectly fine and the entire row is in a single block:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;select * from row_migration_demo where x=1;&lt;/b&gt;

         X
----------
         1

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            0&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, lets do it again and insert a second row:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x) values (2);
UPDATE row_migration_demo set a = 'a', b='b', c='c' where x=2;
COMMIT;&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;And select it again:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;select * from row_migration_demo where x=2;&lt;/b&gt;

         X
----------
         1

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            1&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hooray, the row is migrated:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;ANALYZE TABLE row_migration_demo COMPUTE STATISTICS;&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
         2          1&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; &lt;code&gt;ANALYZE&lt;/code&gt; is deprecated. Yup, I know, but &lt;code&gt;DMBS_STATS.GATHER_TABLE_STATS&lt;/code&gt; does not propagate the &lt;code&gt;CHAIN_CNT&lt;/code&gt; column. &lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1830023856761"&gt;Ask Tom!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s hard to figure out if the row is chained or migrated—I am actually not sure is there any difference in the data structure. Because the additional &lt;code&gt;table fetch continued row&lt;/code&gt; occurs for the very first column, I believe the row is migrated:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select length(a) from row_migration_demo where x=2;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            2&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;To be on the safe side, I will double verify:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x, a) values (3, 'a');
UPDATE row_migration_demo
   SET b = 'b', c = 'c', d = 'd', e = 'e'
 WHERE x=3; &lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Indeed, the &lt;code&gt;a&lt;/code&gt; column is at the place where the row was inserted while the &lt;code&gt;e&lt;/code&gt; column is somewhere else:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select length(&lt;b&gt;a&lt;/b&gt;) from row_migration_demo where x=3;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            2

SQL&amp;gt;  select length(&lt;b&gt;e&lt;/b&gt;) from row_migration_demo where x=3;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            3&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;To make things even worse, single rows can be migrated &lt;em&gt;and&lt;/em&gt; chained. If we insert two more records, it becomes visible:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;INSERT INTO row_migration_demo (x) values (4);
UPDATE row_migration_demo
   SET a='a', b='b', c='c', d='d', e='e'
 WHERE x=4;

INSERT INTO row_migration_demo (x) values (5);
UPDATE row_migration_demo
   SET a='a', b='b', c='c', d='d', e='e'
 WHERE x=5;

COMMIT;&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each of those rows must be chained, because they need more space than a single block has. However, due to migration, the row is actually distributed to three blocks:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select length(&lt;b&gt;a&lt;/b&gt;) from row_migration_demo where x=5;

 LENGTH(A)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            &lt;b&gt;4&lt;/b&gt;

SQL&amp;gt;  select length(&lt;b&gt;b&lt;/b&gt;) from row_migration_demo where x=5;

 LENGTH(B)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            &lt;b&gt;5&lt;/b&gt;

SQL&amp;gt;  select length(&lt;b&gt;c&lt;/b&gt;) from row_migration_demo where x=5;

 LENGTH(C)
----------
      2000

SQL&amp;gt; &lt;b&gt;SELECT a.name, b.value
       FROM v$statname a, v$mystat b
      WHERE a.statistic# = b.statistic#
        AND lower(a.name) = 'table fetch continued row';&lt;/b&gt;

NAME                             VALUE
--------------------------- ----------
table fetch continued row            &lt;b&gt;7&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that the “continued fetch” increased by two for the select on column &lt;code&gt;c&lt;/code&gt; . The statistics show that all but one row is chained (the very first row):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;ANALYZE TABLE row_migration_demo COMPUTE STATISTICS;&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
         5          4&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we continue the game and continue to insert data in this way, we get an astonishing chain count:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;begin
   for i in 10..1000 loop
      INSERT INTO row_migration_demo (x) values (i);
      UPDATE row_migration_demo set a =&amp;#39;a&amp;#39;, b = &amp;#39;b&amp;#39;, c = &amp;#39;c&amp;#39; where x=i;
   end loop;
end;
/

SQL&amp;gt; &lt;b&gt;ANALYZE TABLE row_migration_demo COMPUTE STATISTICS;&lt;/b&gt;

PL/SQL procedure successfully completed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       996        995&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;All the inserted rows are chained.&lt;/p&gt;
&lt;p&gt;The only way to clean up the mess is to move the table. Shrinking doesn’t help to much, especially not if there was no &lt;code&gt;DELETE&lt;/code&gt; or &lt;code&gt;UPDATE&lt;/code&gt; that has freed some space:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;alter table row_migration_demo shrink space;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       996        995
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even if some space is freed and rows are moved, the chaining is not notably reduced:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;delete from row_migration_demo where x &amp;lt; 200;&lt;/b&gt;

195 rows deleted.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       801        801

SQL&amp;gt; &lt;b&gt;alter table row_migration_demo shrink space;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       801        800&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that the shrink has corrected one chained row. That proofs that a Row Movement assigns a new &lt;code&gt;ROWID&lt;/code&gt; and updates the index. However, one “unchained” row isn’t really the correction I would like to see. The only way to correct all chains, is to mote the table:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;alter table row_migration_demo move;&lt;/b&gt;

Table altered.

SQL&amp;gt; &lt;b&gt;alter index row_migration_pk rebuild;&lt;/b&gt;

Index altered.

SQL&amp;gt; &lt;b&gt;analyze table row_migration_demo compute statistics;&lt;/b&gt;

Table analyzed.

SQL&amp;gt; &lt;b&gt;select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
       801          0&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;No chaining anymore.&lt;/p&gt;
&lt;p&gt;There is actually another way to correct row migration. The &lt;a href="http://download.oracle.com/docs/cd/B12037_01/server.101/b10739/general.htm#sthref2787"&gt;Oracle documentation&lt;/a&gt; suggest to delete and re-insert all affected rows. Watch out for your triggers.&lt;/p&gt;
&lt;p&gt;From administrators‛ perspective, the only way to &lt;em&gt;prevent&lt;/em&gt; row migration in the first place is to increase &lt;code&gt;PCTFREE&lt;/code&gt;. The complete re-execution of this test with &lt;code&gt;PCTFREE&lt;/code&gt; increased to 50% “solves” the problem and no chained rows occur anymore:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;drop table row_migration_demo;
CREATE TABLE row_migration_demo (
  a CHAR(2000),
  b CHAR(2000),
  c CHAR(2000),
  d CHAR(2000),
  e CHAR(2000),
  x int,
  constraint row_migration_pk primary key (x)
) enable row movement pctfree 50
/
begin
   for i in 1..1000 loop
      INSERT INTO row_migration_demo (x) values (i);
      UPDATE row_migration_demo set a ='a', b = 'b', c = 'c' where x=i;
   end loop;
end;
/
commit;
analyze table row_migration_demo compute statistics;
select num_rows, chain_cnt
       from user_tables
      where table_name='ROW_MIGRATION_DEMO';&lt;/b&gt;

  NUM_ROWS  CHAIN_CNT
---------- ----------
      1000          0&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;If this is not acceptable, there is one very last option: change the application.&lt;/p&gt;
&lt;p&gt;However, it would be very nice if an operation—similar to shrink—would move migrated rows. It would be even more compelling if a table option would allow to disable Row Migration in favor of Row Movement.&lt;/p&gt;
&lt;h3&gt;Appendix&lt;/h3&gt;
&lt;p&gt;After some years of professional experience, I always wonder why a particular idea of mine should be unique. So I suspected that this feature might be there, somewhere hidden in Oracle. So I checked for hidden parameters (thanks to a &lt;a href="http://www.adp-gmbh.ch/ora/misc/x.html#ksppi"&gt;little documentation on that topic&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;select a.ksppinm parameter, b.kspftctxvl
       from x$ksppi a join x$ksppcv2 b on (a.INDX+1= b.kspftctxpn)
      where a.KSPPDESC like '%movement%';&lt;/b&gt;

PARAMETER                                KSPFTCTXVL
---------------------------------------- ----------
_disable_implicit_row_movement           FALSE

SQL&amp;gt; &lt;b&gt;alter session set "_disable_implicit_row_movement" = true;&lt;/b&gt;

Session altered.

SQL&amp;gt; &lt;b&gt;select a.ksppinm parameter, b.kspftctxvl
       from x$ksppi a join x$ksppcv2 b on (a.INDX+1= b.kspftctxpn)
      where a.KSPPDESC like '%movement%';&lt;/b&gt;

PARAMETER                                KSPFTCTXVL
---------------------------------------- ----------
_disable_implicit_row_movement           TRUE

SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;As suggested by the name of the parameter, this doesn’t change anything in the direction I would like.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/193/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/193/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/193/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/193/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/193/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/193/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/193/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/193/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/193/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/193/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=193&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FxsQROnbSKs0iNXAtWSRLxy1puU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FxsQROnbSKs0iNXAtWSRLxy1puU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/FxsQROnbSKs0iNXAtWSRLxy1puU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FxsQROnbSKs0iNXAtWSRLxy1puU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/KBgbFGOj2-E" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/02/23/row-migration-and-row-movement/</feedburner:origLink></item><item><title>Oracle Trace File Rotation</title><link>http://feedproxy.google.com/~r/orana/~3/DcMj1668WhI/</link><category>Maintainability</category><category>oracle</category><category>trace</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Mon, 01 Feb 2010 09:30:11 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/5eb3bd5a538171aa</guid><description>&lt;p&gt;Under very rare circumstances, I need Oracle SQL trace files from a long period of time. Because trace files usually grow large—especially over several days—there is the need to rotate the trace file during that time so that they can be compressed and put away. The problem is that there is no “rotate tracefile“ button in Oracle. However, I have found a “undocumented feature“ that does exactly that—without disabling tracing.&lt;/p&gt;
&lt;p&gt;My procedure uses the &lt;code&gt;close_trace&lt;/code&gt; call of &lt;code&gt;oradebug&lt;/code&gt;. This call closes the currently written trace file for a session. &lt;a href="http://www.oracloid.com/2006/05/closing-trace-file-with-oradebug/"&gt;Alex Gorbachev&lt;/a&gt; has used this to delete big trace files that are still open. My procedure goes a little bit further and exposes one more side effect of &lt;code&gt;oradebug close_trace&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;&lt;b&gt;Warning: The technique described here uses the undocumented and unsupported &lt;code&gt;&lt;a href="http://www.psoug.org/reference/oradebug.html"&gt;oradebug&lt;/a&gt;&lt;/code&gt; facility. Although I am not aware of any negative side effects of this procedure, the use of this method takes place at your own risk.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;oradebug&lt;/code&gt; utility can be used from the SQL*Plus prompt. If you have an account with sufficient rights (sigh) you can attach to any session using the &lt;code&gt;oradebug setorapid&lt;/code&gt; command. To obtain the required Oracle PID, just query the &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/dynviews_2022.htm"&gt;&lt;code&gt;v$process&lt;/code&gt;&lt;/a&gt; view:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;SELECT s.sid, s.serial#, p.pid &lt;/b&gt;
       &lt;b&gt;FROM v$session s, v$process p&lt;/b&gt;
      &lt;b&gt;WHERE s.paddr=p.addr;&lt;/b&gt;

       SID    SERIAL#        PID
---------- ---------- ----------
         1          1          2
[... skipped ...]
        20       3126         21

26 rows selected.

SQL&amp;gt; &lt;b&gt;oradebug setorapid 21&lt;/b&gt;
Oracle pid: 21, Unix process pid: 145, image: oracle@test.fatalmind.com
SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once attached to a specific process, every &lt;code&gt;oradebug&lt;/code&gt; operation is applied to that particular process. The trick to rotate the trace file is very simple and exploits a “feature” of UNIX file systems; that is, that the name of the file is used only at the moment the file is opened. Once the file is open, all access is managed with the so called &lt;code&gt;inode&lt;/code&gt; and the name is not relevant for the read and write operations anymore. The &lt;code&gt;inode&lt;/code&gt; system has some more &lt;a href="http://en.wikipedia.org/wiki/Inode#Implications"&gt;implications&lt;/a&gt;, one of them is frequently used to rotate log files without data loss. Another implications makes it hard to delete open files, as &lt;a href="http://www.oracloid.com/2006/05/closing-trace-file-with-oradebug/"&gt;Alex Gorbachev&lt;/a&gt; explained.&lt;/p&gt;
&lt;p&gt;The log rotation trick is to rename the open file and then cause the writing process to re-open that file. All write operations to the renamed file will be written properly to new file name—more correctly expressed: to the file which can be accessed under the new name. If the writing process re-opens the file under its original name, it will create a new file because there is no file with that name anymore. Every subsequent write operation goes to the new file. The original file will not grow anymore and can be taken away safely.&lt;/p&gt;
&lt;p&gt;Although the &lt;code&gt;oradebug&lt;/code&gt; command name &lt;code&gt;close_trace&lt;/code&gt; doesn’t suggest that the trace file is reopened, it is—if SQL tracing is enabled. So we have everything to rotate a trace file. Consider the following example:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;$ &lt;b&gt;ls -lrt TEST_ora_14457.trc*&lt;/b&gt;
-rw-r----- 1 oracle oinstall 14758723 2010-02-01 TEST_ora_14457.trc
$ &lt;b&gt;mv  TEST_ora_14457.trc  TEST_ora_14457.trc.old&lt;/b&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;After renaming the file, the file still grows as the SQL trace is written. You can now use &lt;code&gt;oradebug close_trace&lt;/code&gt; to actually rotate the file:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;oradebug close_trace&lt;/b&gt;
Statement processed.
SQL&amp;gt; &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The verification in the file system shows that the ‘old’ file became bigger, and the new file is also filling up:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;$ &lt;b&gt;ls -lrt TEST_ora_14457.trc*&lt;/b&gt;
-rw-r----- 1 oracle oinstall 18878704 2010-02-01 TEST_ora_14457.trc.old
-rw-r----- 1 oracle oinstall  1555972 2010-02-01 TEST_ora_14457.trc
$ &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because I often need to trace many sessions, I wrote a very tiny script (&lt;code&gt;switch_traces.sql&lt;/code&gt;) that creates another script to re-open all trace files:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;set feedback off
set pages 0
set heading off
set echo off

spool oradebug_close_traces.sql

select cmd from (
select pid, 1 o, &amp;#39;oradebug setorapid &amp;#39; || pid cmd from v$process
UNION ALL
select pid, 2 o, &amp;#39;oradebug flush&amp;#39; from v$process
UNION ALL
select pid, 3 o, &amp;#39;oradebug close_trace&amp;#39; from v$process) order by pid,o;

spool off
set echo on
set feedback on
set pages 1000
set heading 0n

prompt
prompt c&amp;amp;p &amp;gt;&amp;gt;&amp;gt; @oradebug_close_traces.sql &amp;lt;&amp;lt;&amp;lt; to switch traces&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This script will close all traces in all sessions and continue tracing to new files if SQL tracing is enabled. You might want to add additional &lt;code&gt;where&lt;/code&gt; clauses to limit your scope—e.g., to skip background processes. The script will also issue a &lt;code&gt;flush&lt;/code&gt; before the &lt;code&gt;close_trace&lt;/code&gt;. I don’t know if this is required or not, it doesn’t seem to make any harm. The use of the script is very simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; &lt;b&gt;@switch_traces.sql&lt;/b&gt;
SQL&amp;gt; set feedback off
SQL&amp;gt; set pages 0
SQL&amp;gt; set heading off
SQL&amp;gt; set echo off
oradebug setorapid 1
oradebug flush
oradebug close_trace

[... skipped ...]

oradebug setorapid 29
oradebug flush
oradebug close_trace
SQL&amp;gt; set feedback on
SQL&amp;gt; set pages 1000
SQL&amp;gt; set heading on
SQL&amp;gt;
SQL&amp;gt; prompt

SQL&amp;gt; prompt c&amp;amp;p &amp;gt;&amp;gt;&amp;gt; @oradebug_close_traces.sql &amp;lt;&amp;lt;&amp;lt; to switch traces
c&amp;amp;p &amp;gt;&amp;gt;&amp;gt; @oradebug_close_traces.sql &amp;lt;&amp;lt;&amp;lt; to switch traces
SQL&amp;gt; &lt;b&gt;@oradebug_close_traces.sql&lt;/b&gt;
SQL&amp;gt; oradebug setorapid 1
ORA-00072: process &amp;quot;Unix process pid: 0, image: PSEUDO&amp;quot; is not active
SQL&amp;gt; oradebug flush
Statement processed.
SQL&amp;gt; oradebug close_trace
Statement processed.
SQL&amp;gt; oradebug setorapid 2
Oracle pid: 2, Unix process pid: 137, image: oracle@test.fatalmind.com
SQL&amp;gt; oradebug flush
Statement processed.
SQL&amp;gt; oradebug close_trace
Statement processed.

[... skipped ...]

Oracle pid: 29, Unix process pid: 138, image: oracle@test.fatalmind.com
SQL&amp;gt; oradebug flush
Statement processed.
SQL&amp;gt; oradebug close_trace
Statement processed.
SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;In case you have not forgotten to rename your trace files you will see the new files growing now.&lt;/p&gt;
&lt;p&gt;Read also my previous article &lt;a href="http://myfatalmind.wordpress.com/2009/11/24/to-trace-or-not-to-trace/"&gt;To Trace or Not to Trace&lt;/a&gt; for information about fine grained SQL tracing.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/174/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/174/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=174&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/jPIqqWBPxG_2rkfHp1qiSTW7lkM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jPIqqWBPxG_2rkfHp1qiSTW7lkM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/jPIqqWBPxG_2rkfHp1qiSTW7lkM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jPIqqWBPxG_2rkfHp1qiSTW7lkM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/DcMj1668WhI" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/02/01/oracle-trace-file-rotation/</feedburner:origLink></item><item><title>Oracle JDBC PreFetch Portability</title><link>http://feedproxy.google.com/~r/orana/~3/HQXRAFmFiEs/</link><category>Portability</category><category>java</category><category>JDBC</category><category>oracle</category><category>sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Fri, 29 Jan 2010 06:23:11 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/bbf95072067e8ac6</guid><description>&lt;p&gt;In a &lt;a href="http://myfatalmind.wordpress.com/2009/12/22/latency-security-vs-performance/"&gt;previous article about (network) latencies&lt;/a&gt;, I have presented the &lt;code&gt;OracleStatement.setRowPrefetch()&lt;/code&gt; method to reduce round trips for &lt;code&gt;SELECT&lt;/code&gt; statements. I must admit that I was a little bit wrong. Not in the essence; increasing the PreFetch size is still a great (read: simple) way to reduce latencies. However, there is a better approach to set the PreFetch size. &lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;Since the raise of Java, many portability issues have vanished. There are most definitely new ones—think of mobile devices—and some others are still eminent—think of Internet Explorer versus all other browsers. Nevertheless, Java improved the situation quite notable—most credible: Unicode support from the beginning. &lt;/p&gt;
&lt;p&gt;Still there are some people like me who just cast the portable JDBC class to whatever they believe it is, and do some cool things with that. Every now and then there &lt;i&gt;is&lt;/i&gt; the need to use proprietary functions, but I should have checked the portable API first. Shame on me. In fact there is the standard JDBC method &lt;code&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/java/sql/Statement.html#setFetchSize%28int%29"&gt;Statement.setFetchSize()&lt;/a&gt;&lt;/code&gt; that does exactly the same. Even more interesting, it’s there since ages (&lt;a href="http://java.sun.com/products/jdbc/download.html#102spec"&gt;JDBC 2.1 at least&lt;/a&gt;). &lt;/p&gt;
&lt;p&gt;Although the documentation seems to be exactly what I was looking for, I wrote a little test program to verify. Just because I know that Oracle can profit from an increased PreFetch size and the standard provides a method to set it, it doesn’t mean that it works. Especially not if the standards says that the method is a &lt;em&gt;hint&lt;/em&gt; to the JDBC driver. So let’s double check.&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;import java.sql.*;
import oracle.jdbc.*;

class JDBCFetchSize {
  public static void main(String[] argv) throws Exception {
    if (argv.length != 4) {
      throw new RuntimeException(&amp;quot;JDBCFetchSize &amp;lt;jdbc-connect url&amp;gt; &amp;quot;
                               + &amp;quot;&amp;lt;username&amp;gt; &amp;lt;password&amp;gt; &amp;lt;FetchSize&amp;gt;&amp;quot;);
    }
    Connection con = connect(argv[0], argv[1], argv[2]);
    int roundtripoffset = getRoundTripOffset(con);

    PreparedStatement ps = con.prepareStatement(seqsql);

    setFetchSize(ps, argv[3]);

    int start = getCurrentRoundTripCount(con);
    long startts = System.currentTimeMillis();

    int rowsfetched = executeTest(ps);

    int end    = getCurrentRoundTripCount(con) - roundtripoffset;
    long endts = System.currentTimeMillis();

    ps.close();

    System.out.println(rowsfetched + &amp;quot; rows fetched in &amp;quot;
                       + (end-start) + &amp;quot; server round trips and &amp;quot;
                       + (endts-startts) + &amp;quot; ms&amp;quot;);
  }

  // oracle SQL to fetch round trip counter
  final static String rtsql =
      &amp;quot; select value &amp;quot;
    + &amp;quot;   from v$mystat ms, v$statname sn&amp;quot;
    + &amp;quot;  where ms.value &amp;gt; 0&amp;quot;
    + &amp;quot;    and ms.statistic#=sn.statistic#&amp;quot;
    + &amp;quot;    and sn.name IN (&amp;#39;SQL*Net roundtrips to/from client&amp;#39;)&amp;quot;;

  // sequence generator
  final static String seqsql =
      &amp;quot; select level from dual connect by level &amp;lt;= ?&amp;quot;;

  static PreparedStatement rtstm;

  private static Connection
    connect (String url, String user, String pass)
    throws ClassNotFoundException, SQLException
  {
    Class.forName(&amp;quot;oracle.jdbc.driver.OracleDriver&amp;quot;);
    return DriverManager.getConnection(url, user, pass);
  }

  private static int
    getRoundTripOffset(Connection con)
    throws SQLException
  {
    // The very first time we call it, it is not prepared.
    // so we don&amp;#39;t consider that at all.
    getCurrentRoundTripCount(con);

    int first = getCurrentRoundTripCount(con);
    return getCurrentRoundTripCount(con) - first;
  }

  static void
    setFetchSize(Statement stm, String size)
    throws SQLException
  {
    System.out.println(&amp;quot;getFetchSize(): &amp;quot; + stm.getFetchSize());

    stm.setFetchSize(Integer.parseInt(size));

    // uncomment the not portable OracleStatement
    // variant for verification
    // ((OracleStatement)stm).setRowPrefetch(Integer.parseInt(size));

    System.out.println(&amp;quot;getFetchSize(): &amp;quot; + stm.getFetchSize());
  }

  static int
    executeTest(PreparedStatement ps)
    throws SQLException
  {
    int cnt = 0;
    ps.setInt(1, 100000);
    ResultSet rs = ps.executeQuery();
    while (rs.next()) {
      ++cnt;
    }
    rs.close();
    return cnt;
  }

  static int
    getCurrentRoundTripCount(Connection con)
    throws SQLException
  {
    if (rtstm == null) {
      rtstm = con.prepareStatement(rtsql);
    }
    ResultSet rs = rtstm.executeQuery();
    rs.next();
    int rv = rs.getInt(1);
    rs.close();

    return rv;
  }
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The result proofs that the portable JDBC &lt;code&gt;setFetchSize()&lt;/code&gt; is the better choice over Oracle’s proprietary &lt;code&gt;setRowPrefetch()&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;$ java JDBCFetchSize jdbc:oracle:thin:@//host/service user password 10
getFetchSize(): 10
getFetchSize(): 10
100000 rows fetched in 10002 server round trips and 521 ms
$ java JDBCFetchSize jdbc:oracle:thin:@//host/service user password 20
getFetchSize(): 10
getFetchSize(): 20
100000 rows fetched in 5002 server round trips and 324 ms
$ java JDBCFetchSize jdbc:oracle:thin:@//host/service user password 50
getFetchSize(): 10
getFetchSize(): 50
100000 rows fetched in 2002 server round trips and 191 ms
$ &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Calling &lt;code&gt;setRowPrefetch()&lt;/code&gt; affects the number of server round trips as expected.&lt;/p&gt;
&lt;p&gt;And because consistency (=Quality) matters, I will right away update the previous article.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/java.112/e10589/oraperf.htm#CHDHDADD"&gt;Recent oracle docs&lt;/a&gt; list some limitations.&lt;/p&gt;
&lt;p&gt;After all, there is still some room for portability pitfalls. If you want to change the default value from 10 to some other value, Oracle has two solutions for that. Read again: &lt;em&gt;Oracle&lt;/em&gt; has two solutions for that. JDBC has none (correct me if I am wrong).&lt;/p&gt;
&lt;p&gt;When passing &lt;code&gt;Properties&lt;/code&gt; to the &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DriverManager.html#getConnection%28java.lang.String,%20java.util.Properties%29"&gt;DriverManager.getConnection()&lt;/a&gt; method, Oracle allows to set the &lt;a href="http://download.oracle.com/docs/cd/B13789_01/java.101/b10979/basic.htm#g1028323"&gt;defaultRowPrefetch&lt;/a&gt; property. The other way is to cast the JDBC &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Connection.html"&gt;Connection&lt;/a&gt; to &lt;a href="http://www.oracle.com/technology/docs/tech/java/sqlj_jdbc/doc_library/javadoc/oracle.jdbc.driver.OracleConnection.html"&gt;OracleConnection&lt;/a&gt; and use the &lt;a href="http://www.oracle.com/technology/docs/tech/java/sqlj_jdbc/doc_library/javadoc/oracle.jdbc.driver.oracleconnection.html#setDefaultRowPrefetch%28int%29"&gt;setDefaultRowPrefetch()&lt;/a&gt; method (hurray — cast again!). Disclaimer: I did not test any of those.&lt;/p&gt;
&lt;p&gt;Please note that my tests were based on Oracle 11r2, other release might behave differently. During my research I found some Oracle 8.1.6 docs which also mention the portable &lt;code&gt;setFetchSize()&lt;/code&gt; method, that makes me believe it works since then.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/159/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/159/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=159&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/WgenhzhquFOHAm4XmdFh97VfkoE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WgenhzhquFOHAm4XmdFh97VfkoE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/WgenhzhquFOHAm4XmdFh97VfkoE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WgenhzhquFOHAm4XmdFh97VfkoE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/HQXRAFmFiEs" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2010/01/29/oracle-jdbc-prefetch-portability/</feedburner:origLink></item><item><title>Latency: Security vs. Performance</title><link>http://feedproxy.google.com/~r/orana/~3/1fG5d-7Wt1Q/</link><category>Performance</category><category>java</category><category>oracle</category><category>security</category><category>sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 22 Dec 2009 00:01:53 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/79cffb5b104cdef6</guid><description>&lt;p&gt;At a client’s Christmas party, I have witnessed a very short talk between a network engineer and a top level manager. The network guy explained that the firewall adds about 0.2 milliseconds latency to each round trip between the application server and the database, which adds up to some hours in one particular case. So the manager asked what could be done and the network guy quickly provided two solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the application to make less round trips&lt;/li&gt;
&lt;li&gt;Accept the security risk and don’t put a firewall in between those two tiers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Funny enough the network guy explained that the second option needs the managers signature because it bypasses the corporate security guidelines and somebody must take the responsibility for that.&lt;/p&gt;
&lt;p&gt;Consider you are the manager, would you sign such a paper?&lt;/p&gt;
&lt;p&gt;I guess I would not, and I also guess my client didn’t (otherwise it was against my advice &lt;img src="http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif" alt=";)"&gt; ).&lt;/p&gt;
&lt;p&gt;In this article I will explain the term latency, give some insight why it is so easy to build latency bandit applications and show some ways to reduce latencies.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;br&gt;
&lt;h3&gt;Weekend Shopping&lt;/h3&gt;
&lt;p&gt;The best explanation for latency I ever came across is from the guys over at &lt;a href="http://www.roughsea.com" title="RoughSea Ltd."&gt;RoughSea&lt;/a&gt;. There is a video that describes shopping like that (simplified):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Drive to the super market&lt;/li&gt;
&lt;li&gt;Locate what’s needed (e.g., milk)&lt;/li&gt;
&lt;li&gt;Pay&lt;/li&gt;
&lt;li&gt;Store the item in the car&lt;/li&gt;
&lt;li&gt;Drive back home&lt;/li&gt;
&lt;li&gt;Store the item (e.g., fridge)&lt;/li&gt;
&lt;li&gt;Then start again for the next item on the shopping list (e.g., corn flakes).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;a href="http://www.roughsea.com/vids/SQL_Best_Practices.html" title="RoughSea 20 minutes Video"&gt;whole 20 minutes video&lt;/a&gt; is a great watch and covers many other frequent issues as well. If you just want to watch that particular scene, &lt;a href="http://www.youtube.com/watch?v=GbZgnAINjUw#t=5m00s" title="Weekend shopping @ YouTube"&gt;here is a direct youtube link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When watching this video it’s hard not to &lt;a href="http://en.wikipedia.org/wiki/D%27oh!" title="D&amp;#39;oh! @ Wikipedia"&gt;D’oh!&lt;/a&gt; The video makes it very obvious that most of the time is spent to drive. The actual shopping takes much less time than all the overhead for driving, parking, paying,….&lt;/p&gt;
&lt;p&gt;Latency in software has very similar causes, most prominently that the application asks the database for many items individually.&lt;/p&gt;
&lt;h3&gt;How Can This Happen?&lt;/h3&gt;
&lt;p&gt;This question has actually two sides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can somebody implement software that way?&lt;/li&gt;
&lt;li&gt;How can a latency of 0.2 milliseconds make any difference?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First of all let’s look at the 0.2 milliseconds statement again. This value was provided by the network engineer as &lt;em&gt;additional&lt;/em&gt; latency because of the new firewall. That’s on top of the latency the network anyway, even without firewall. A typical value for a switched LAN, without firewall, is around 0.1 – 0.2 milliseconds. If a firewall adds 0.2ms to that, the overall latency raises to about 0.4ms per round trip. In other words 2500 round trips take a second. Although this sounds a lot, it’s actually not so much if you consider how many round trips take place for an average operation. A average latency of 0.4 milliseconds is still very reasonable and will cause problems only in extreme cases. Nevertheless latencies can grow because of increased network complexity or during peak load.&lt;/p&gt;
&lt;p&gt;The situation becomes more relevant because one round trip is hardly enough to perform any reasonable operation. Realistically, an application issues &lt;em&gt;many&lt;/em&gt; different SQLs to accomplish a single task. A single SQL statement might also need multiple round trips to complete; a &lt;code&gt;SELECT&lt;/code&gt; statement against an Oracle database needs typically two round trips, as explained farther down the article. An application that executes 10 &lt;code&gt;SELECT&lt;/code&gt;s has already 8ms of latencies. More complex applications might execute 100 SQL’s and reach 80 milliseconds of latencies. I have seen batch jobs that issue 50 million SQLs so that the round trip latency sums up to 10 hours or more!&lt;/p&gt;
&lt;p&gt;So, why! Why is it done that way? Because it is easier to implement. Therefore faster to implement and less error prone. Consider the shopping example again. The algorithm is very simple; just repeat all steps until the shopping list is done. This works irrespective of the size of the shopping list.&lt;/p&gt;
&lt;p&gt;An improved variant of the algorithm, that is latency aware, might look like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Drive to the super market&lt;/li&gt;
&lt;li&gt;&lt;i&gt;FOR EACH ITEM:&lt;/i&gt; Locate item (milk, corn flakes)&lt;/li&gt;
&lt;li&gt;Pay&lt;/li&gt;
&lt;li&gt;&lt;i&gt;FOR EACH ITEM:&lt;/i&gt; Store into the car&lt;/li&gt;
&lt;li&gt;Drive back home&lt;/li&gt;
&lt;li&gt;&lt;i&gt;FOR EACH ITEM:&lt;/i&gt; Store the item (e.g., fridge, larder)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Please note that what has been one loop over the complete algorithm before, was turned into three loops somewhere inside the algorithm. The &lt;a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity"&gt;complexity&lt;/a&gt; has grown. Even worse, there are more complex error scenarios with the new algorithm. E.g., a very long shopping list might exceed the capacity of the car to transport all the items. Such scenarios need special error handling. Alternatively the algorithm could prevent that scenario by splitting up large purchases into multiple smaller parts. The additional complexity requires more time to implement and introduces more scenarios to test. The efforts quickly explode, especially since the &lt;em&gt;overflow&lt;/em&gt; scenarios are hard to test.&lt;/p&gt;
&lt;p&gt;One more word of warning: The case that your shopping doesn’t fit into your car might look very unlikely, and perhaps it is. However, in computer programs, there a less natural limits than in real life—e.g., what your family can eat. From personal experience I would say chances are 2:1 to hit this error scenario within the first year that implementation is used.&lt;/p&gt;
&lt;p&gt;After all, there is a trade off between software complexity and software performance. From another point of view, it is even a trade off between software development speed and software runtime speed. If you are pushed to deliver working software, better sooner than later, which algorithm would you implement?&lt;/p&gt;
&lt;p&gt;Finally, there is also a reason why latency bastard applications can be produced by accident:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;But it works in development/test environment&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Very often, development takes place on desktop hardware where everything is installed on the same machine. Obviously, there is no firewall in place nor is there any network involved. The involved latency in such an environment is only a fraction (e.g., a 10th) of the latency involved in an n-tier production deployment. In the shopping example, this would mean that the super market is in the room next to the kitchen. Item-by-item shopping might work fine in that case. Unfortunately this type of shopping infrastructure is not yet commonplace.&lt;/p&gt;
&lt;h3&gt;How to Avoid it&lt;/h3&gt;
&lt;p&gt;So let’s look into the more technical part of the story. Which techniques can be used to reduce the number of database round trips? The following list gives the three most effective ways to avoid unneeded latency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Joins&lt;/li&gt;
&lt;li&gt;Use array/batch functionality&lt;/li&gt;
&lt;li&gt;Use advanced techniques (open cursors, fetch buffers, bulk/implicit commit, …)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Join&lt;/h3&gt;
&lt;p&gt;The most important technique to avoid latencies is to tell the database which data is needed &lt;em&gt;at once&lt;/em&gt;. A very problematic implementation is to query one table and issue another select within the fetch loop of the first statement. I refer to this method as &lt;em&gt;nested select&lt;/em&gt; as opposed to a &lt;em&gt;nested loop&lt;/em&gt; join. Both methods implement the same idea while the &lt;em&gt;nested select&lt;/em&gt; involves additional round trips. With SQL, it is always possible to construct a join that delivers the same result within the same time—but usually much faster.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;nested select&lt;/em&gt; anti-pattern can be applied to any extend. They can be nested several levels deep, or kept at one level that triggers more than just one nested select. I have mentioned the 50 million SQL batch job that took more than 10 hours, just for latency. This particular job has queried some 10 additional child tables for each record fetched from the main query. The main query has returned some million records so that the latency became the most dominant performance factor. In that particular case, the developers explained that the coding guidelines force them to use the existing functions instead of writing new SQL. Another difficulty was that the nested statements were depended on the data from the main table so that a trivial join doesn’t work (conditional join). In fact there were only three different ways to join the tables, therefore the correct solution is to make three different SQLs. In case a single result set is needed (e.g., because of a order clause), a &lt;code&gt;UNION&lt;/code&gt; can be used. After all, 10 out of 12 hours overall run time was saved just by using joins.&lt;/p&gt;
&lt;h3&gt;Batch Execution&lt;/h3&gt;
&lt;p&gt;The second major latency reduction technique is to properly use batch execution that is sometimes also called array execution. Many databases provide a way to execute more than one statement in one round trip. Not all APIs make this functionality available to the client, but &lt;a href="http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame6.html" title="JDBC Batch API"&gt;JDBC does&lt;/a&gt;. However, there are some pitfalls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Error handling is terrible.&lt;/span&gt;&lt;br&gt;Although the API defines methods to identify which statement failed, the API is very cumbersome to use. To make things worse it’s not even defined if the execution of the batch stops on errors. My Best Current Practice is to set a &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Savepoint.html" title="JDBC Savepoint"&gt;Savepoint&lt;/a&gt; prior array execution. In case an error occurs, a rollback to the Savepoint can be done.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Very large batches.&lt;/span&gt;&lt;br&gt;Very large batches can have their own problems. As with the shopping example, every implementation must be prepared to use multiple batches in case the number of executions becomes very high.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Does not always work faster.&lt;/span&gt;&lt;br&gt;The actual performance gain is vendor specific and, more importantly, depends on the method used. For example, Oracle benefits from batching only if &lt;code&gt;&lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/PreparedStatement.html" title="JDBC PreparedStatement"&gt;PreparedStatements&lt;/a&gt;&lt;/code&gt; are used. Batching &lt;code&gt;Statement&lt;/code&gt; objects doesn’t give any performance gain with Oracle. On the other side, MySQL can also benefit from &lt;code&gt;Statement&lt;/code&gt; batches. As always, the advice is to &lt;strong&gt;use &lt;code&gt;PreparedStatements&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;whenever possible&lt;/strong&gt;. This will almost always result in major performance improvements.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;The JDBC API is &lt;em&gt;optional&lt;/em&gt;.&lt;/span&gt;&lt;br&gt;Prior using that API you must check if the driver supports it. Realistically I would suggest to build an abstraction that takes care of this and executes the statements individually if batch execution is not support. Ideally the JDBC driver would do so.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are important issues, that cause considerable efforts, to take care of when using batch execution. Still all the efforts can easily pay off. In many cases the execution of a &lt;code&gt;PreparedStatement&lt;/code&gt; batch with two elements takes no longer than the execution of a single statement, so that the execution is effectively twice as fast. For trivial SQL statements, a speed improvement of factor 10 is within easy reach. That justifies some efforts.&lt;/p&gt;
&lt;h3&gt;Advanced Techniques&lt;/h3&gt;
&lt;p&gt;Besides the two most powerful techniques explained above, there are many more ways to reduce latency. They can be classified in two different types: SQL improvements or API improvements.&lt;/p&gt;
&lt;p&gt;Let’s start with SQL improvements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Join.&lt;/span&gt;&lt;br&gt;Again, just as a reminder. The most powerful way to reduce latencies.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;IN&lt;/code&gt; clauses&lt;/span&gt;&lt;br&gt;Consider to use an &lt;code&gt;IN&lt;/code&gt; clause to fetch multiple records form the same table in one run. Unfortunately, the SQL language doesn’t guarantee that the order of the result sets corresponds to the &lt;code&gt;IN&lt;/code&gt; clause order. An additional layer (e.g., HashMap) might be used to handle that issue. Another potential problem is that the number of items in the &lt;code&gt;IN&lt;/code&gt; list has a maximum—1000 in Oracle: ORA-01795. However, if the list can grow that big, chances are good that the list itself was retrieved from the database. In that case, a join might be the better solution—just to mention it once more.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;UNION&lt;/code&gt;&lt;/span&gt;&lt;br&gt;If data from two different tables is needed that have the same structure, you can use a &lt;code&gt;UNION&lt;/code&gt; to fetch both at once. For example a &lt;code&gt;CURRENT&lt;/code&gt; and a &lt;code&gt;HISTORY&lt;/code&gt; table might contain the life-cycle of a row. To obtain the current and all past versions, both tables must be queried. Another examples are pre-calculated aggregates; one table contains all the aggregates as of yesterday and a second table all the new records since then. To get the total figure—as of “now”—both tables must be queried.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;RETURNING&lt;/code&gt; clause. &lt;/span&gt;&lt;br&gt;If you use a surrogate primary key that is generated from a sequence, you must usually perform two steps to fetch the surrogate key. The least troublesome solution is to first &lt;code&gt;SELECT&lt;/code&gt; the new ID and the perform the &lt;code&gt;INSERT&lt;/code&gt; with that ID. The Oracle answer to this problem is the &lt;code&gt;RETURNING&lt;/code&gt; clause of the &lt;code&gt;INSERT&lt;/code&gt; statement. Although the JDBC API &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Statement.html#getGeneratedKeys%28%29" title="getGeneratedKeys JDBC 3.0"&gt;acknowledges the problem since version 3.0&lt;/a&gt; the Oracle implementation is not very useful—it returns the &lt;code&gt;ROWID&lt;/code&gt;. A &lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=336591" title="INSERT...RETRUNING in JDBC"&gt;workaround&lt;/a&gt; is available.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;1×1 Cartesian product or &lt;code&gt;CROSS&lt;/code&gt; join&lt;/span&gt;&lt;br&gt;
&lt;p&gt;The so called &lt;a href="http://en.wikipedia.org/wiki/Cartesian_product" title="Cartesian prudction @ Wikipedia"&gt;Cartesian production&lt;/a&gt; (everything with everything) is sometimes used accidentally and results in huge result sets. That’s the reason why &lt;a href="http://blog.fatalmind.com/?%3F%3F"&gt;SQL-92&lt;/a&gt; introduced a distinct keyword for it: &lt;code&gt;CROSS&lt;/code&gt;. On the other side, the Cartesian product of two single row statements is, again, a single row: &lt;/p&gt;
&lt;p&gt;&lt;code&gt;SELECT a.*, b.* FROM a CROSS JOIN b WHERE a.id=1 AND b.id=2&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;This technique can be used to combine multiple single-row statements into a single serve round trip. Please note that this works only if both statements return exactly one row, not more, not less. This is more often a last resort option than a first choice.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the typical SQL based techniques to reduce the number of individual SQLs executed that in turn reduces the number of round trips. Another strategy to reduce the number of round trips is to use the API differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Batch execution.&lt;/span&gt;&lt;br&gt;Sorry for the repetition, but it’s the most important technique.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Open cursors&lt;/span&gt;&lt;br&gt;When executing the same &lt;code&gt;PreparedStatement&lt;/code&gt; multiple times in a row–with different bind values—the same&lt;code&gt; PreparedStatement&lt;/code&gt; &lt;em&gt;instance&lt;/em&gt; should be used for all executions. The first execution of a &lt;code&gt;Statement&lt;/code&gt; involves an additional round trip to open the cursor. This additional round trip can be avoided when the same &lt;code&gt;PreparedStatement&lt;/code&gt; instance is used again. As a side effect, &lt;code&gt;PARSING&lt;/code&gt; overhead is also reduced.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Increased PreFetch&lt;/span&gt;&lt;br&gt;The Oracle network layer utilizes a technique called &lt;em&gt;PreFetch&lt;/em&gt; to reduce round trips for &lt;code&gt;SELECT&lt;/code&gt; statements. Instead of retrieving every single row individually from the server, a certain number or rows is transferred in one round trip. The Oracle client library handles this &lt;em&gt;PreFetch&lt;/em&gt; transparently and still provides one row after each other to the application software. For &lt;code&gt;SELECT&lt;/code&gt; statements, PreFetch is almost as powerful as batch execution for &lt;code&gt;INSERT&lt;/code&gt;/&lt;code&gt;UPDATE&lt;/code&gt;/&lt;code&gt;DELETE&lt;/code&gt; statements and can reduce the latencies by an order of magnitude for large result sets. The current default for the Oracle JDBC driver is to fetch at most 10 rows in each server round trip. The JDBC standard provides a way manipulate this value: &lt;tt&gt;Statement.setFetchSize(20);&lt;/tt&gt; If your application typically fetches 20 records, you might set the value accordingly. There is no (notable) harm for statements that return less than 20 records. &lt;br&gt;&lt;b&gt;UPDATE 2010-01-29:&lt;/b&gt; This paragraph was updated to reflect my later findings regarding &lt;a href="http://myfatalmind.wordpress.com/2010/01/29/oracle-jdbc-prefetch-portability/"&gt;Oracle JDBC PreFetch Portability&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following chart shows various select executions with different &lt;em&gt;PreFetch&lt;/em&gt; sizes. The first pair is a mass select to retrieve 40000 rows from the database. Fetching twice as many records in one round trip, cuts the overall time down to the half. The other bars show the execution of a statement that returns 20 rows. To get reasonable values those statements were executed 1000 times in a row. The repeated execution allows the &lt;em&gt;open cursor&lt;/em&gt; optimization to be applied as well. The middle pair of bars shows the execution performance with a &lt;code&gt;PreparedStatement&lt;/code&gt; that is closed after each execution. The last bars show the same but the &lt;code&gt;PreparedStatement&lt;/code&gt; was opened only once and used then for all 1000 executions.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" height="250" src="http://myfatalmind.files.wordpress.com/2009/12/latency.png?w=580&amp;amp;h=250" title="The effects of server round trips" width="580"&gt;&lt;/p&gt;
&lt;p&gt;The latency difference between 10 records &lt;em&gt;PreFetch&lt;/em&gt; with a “closed cursor” to the 20 records &lt;em&gt;PreFetch&lt;/em&gt; with an “&lt;em&gt;open cursor&lt;/em&gt;” is 70%.&lt;/p&gt;
&lt;p&gt;Please note that the statement used for this statistics was very trivial and can be assumed to be executed instantly. The improvements visible in the chart above do therefore indicate the maximum possible improvement.&lt;/p&gt;
&lt;h3&gt;The Bottom Line&lt;/h3&gt;
&lt;p&gt;Increasing network complexity introduces additional latencies that might affect applications performance.&lt;/p&gt;
&lt;p&gt;There are many ways to reduce server round trips to make an application less sensitive to network latencies. Most of them involve revised process flows and increase application complexity.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Firewall qualified”&lt;/em&gt; applications already have an advantage in competition and attest awareness for complex production deployments.&lt;/p&gt;
&lt;p&gt;It’s not &lt;strong&gt;fatal&lt;/strong&gt; to open your &lt;strong&gt;mind&lt;/strong&gt; to new approaches.&lt;/p&gt;
&lt;h3&gt;How Internet Works&lt;/h3&gt;
&lt;p&gt;There is another &lt;a href="http://vladstudio.com/cs/wallpaper?how_internet_works/1280x1024/low"&gt;excellent cartoon that explains latency&lt;/a&gt; in context of the Internet.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/106/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/106/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=106&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/VrJ6uraD8ZjApEolL44VPC8YyJY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VrJ6uraD8ZjApEolL44VPC8YyJY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/VrJ6uraD8ZjApEolL44VPC8YyJY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VrJ6uraD8ZjApEolL44VPC8YyJY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/1fG5d-7Wt1Q" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /><media:content url="http://myfatalmind.files.wordpress.com/2009/12/latency.png" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2009/12/22/latency-security-vs-performance/</feedburner:origLink></item><item><title>To Trace or Not to Trace</title><link>http://feedproxy.google.com/~r/orana/~3/9NhrAGwypyk/</link><category>Maintainability</category><category>oracle</category><category>sql</category><category>trace</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Tue, 24 Nov 2009 09:49:44 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/24dc6b17ffe5dc0d</guid><description>&lt;p&gt;I have always been a fan of the oracle SQL trace facility. It’s a very powerful method to analyze performance problems, especially if you don’t know the application very well. If you don’t know oracle SQL tracing yet, you will find some info at &lt;a href="http://www.orafaq.com/wiki/SQL_Trace" title="SQL Trace @ orafaq"&gt;orafaq&lt;/a&gt; and some more details at &lt;a href="http://www.oracle-base.com/articles/10g/SQLTrace10046TrcsessAndTkprof10g.php" title="Oracle-base on SQL Tracing"&gt;orable-base&lt;/a&gt;. Obviously, Oracle has also some &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14211/sqltrace.htm#i4640" title="Oracle&amp;#39;s SQL Trace documentation"&gt;documenation&lt;/a&gt; about it.&lt;/p&gt;
&lt;p&gt;In short, the Oracle SQL tracing facility causes the database server to write all executed SQL statements to a file for later analysis. One of the main issues with SQL tracing is the huge size of the trace files; every statement execution is logged, potentially with additional data about the execution plan, wait events, and bind values.&lt;/p&gt;
&lt;p&gt;This article introduces the new possibilities of Oracle 10g to enable tracing on a very fine level. With a properly instrumented application, tracing can be enabled for specific users or program modules only. With such a fine control over the tracing, the generated data volume can be kept small and SQL tracing becomes a much more useful tool.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;The first part of the article explains how a Java application can be instrumented to allow fine-grained tracing while the second part shows the &lt;a href="http://blog.fatalmind.com/#admininterface"&gt;administrative interface&lt;/a&gt; to enable tracing for specific modules.&lt;/p&gt;
&lt;h3&gt;Instrumenting the Application&lt;/h3&gt;
&lt;p&gt;First things first. Before the database can selectively trace for specific users or modules, the application must tell the database to which user or module an actual SQL statement belongs.&lt;/p&gt;
&lt;p&gt;Luckily we don’t have to explicitly specify this for each individual statement. It’s more like a session property that can be set at any time. Once set, it affects all SQLs until a different value is set.&lt;/p&gt;
&lt;p&gt;The three values that affect tracing are &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;, &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt;. Those must be properly set in order to allow selective tracing. With properly I mean two aspects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Allow meaningful filtering&lt;/span&gt;&lt;br&gt;&lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt; can be used freely. &lt;em&gt;&lt;code&gt;MODULE&lt;/code&gt;&lt;/em&gt; and &lt;code&gt;ACTION&lt;/code&gt; depend on each other; it’s not possible to trace a specific action in every module. In other words, a &lt;code&gt;MODULE&lt;/code&gt; is a container for &lt;code&gt;ACTIONS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Don’t introduce a performance impact&lt;/span&gt;&lt;br&gt;
&lt;p&gt;The Oracle database provides a way to set the &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;, &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; without doing an additional server round-trip. The information is sent piggyback with the next round-trip. You must take care to use the proper implementation to not introduce an additional server round-trip that would introduce a major performance impact.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; The issue about server round tips is also the topic of later article: &lt;a href="http://blog.fatalmind.com/2009/12/22/latency-security-vs-performance/"&gt;Latency: Security vs. Performance&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The Required JDBC API&lt;/h3&gt;
&lt;p&gt;Since all of the described functionality is an oracle specific extension, the JDBC standard doesn’t provide an API to set the required attributes. To access that functionality you must &lt;em&gt;cast&lt;/em&gt; the standard JDBC &lt;code&gt;Connection&lt;/code&gt; to an &lt;code&gt;OracleConnection&lt;/code&gt; object first. That’s all, the &lt;code&gt;OracleConnection&lt;/code&gt; class provides the two methods we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/B13789_01/java.101/b10979/endtoend.htm" title="Oracle End To End Metrics JDBC"&gt;&lt;code&gt;OracleConnection.setEndToEndMetrics()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://download.oracle.com/docs/cd/B13789_01/java.101/b10979/endtoend.htm" title="Oracle End To End Metrics JDBC"&gt;&lt;code&gt;OracleConnection.getEndToEndMetrics()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The naming might confuse in the first place, but when you consider that the actual identifier for a real human being is intended to be put into the &lt;code&gt;CLIENT_IDENTIFER&lt;/code&gt; field, it might become clear that this is really for end-to-end measurements.&lt;/p&gt;
&lt;p&gt;The actual data is passed as an &lt;code&gt;String&lt;/code&gt; array that contains an entry for the properties we are interested in —along with others. You can get all of them and provide new ones. Remember that neither &lt;code&gt;getEndToEndMetrics()&lt;/code&gt; nor &lt;code&gt;setEndToEndMetrics()&lt;/code&gt; causes a server round trip. In case you change any property, the change is sent piggyback with the next server round-trip. I have used the following class for my tests:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;class OracleEndToEnd {
 String oldData[];
 OracleConnection ocon;

 public
   OracleEndToEnd(Connection con, String module,
                  String client , String action)
   throws SQLException
 {
   if (con instanceof OracleConnection) {
     ocon = (OracleConnection)con;
     oldData = ocon.getEndToEndMetrics();
     if (oldData == null) {
       oldData =
          new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
     }

     String[] metrics =
        new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];

     int minidx = (metrics.length &amp;lt; oldData.length)
                                  ?
                   metrics.length : oldData.length;
     System.arraycopy(oldData, 0, metrics, 0, minidx);

     if (action != null) {
       metrics[OracleConnection.END_TO_END_ACTION_INDEX] = action;
     }
     if (client != null) {
       metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = client;
     }
     if (module != null) {
       metrics[OracleConnection.END_TO_END_MODULE_INDEX] = module;
     }
     ocon.setEndToEndMetrics(metrics, (short) 0);
   }
 }

 public void
 endSection()
 throws SQLException
 {
   if (ocon != null) {
     ocon.setEndToEndMetrics(oldData, (short) 0);
   }
 }
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This class is intended to be used as wrapper around a specific section of your code (not to say module or action) like this:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;OracleEndToEnd oee = new OracleEndToEnd(con, "MOD1", null, "ACT1");
try {
   selectIt(con);
} finally {
   oee.endSection();
}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;When done carefully, a hierarchical structure of &lt;code&gt;MODULES&lt;/code&gt; and &lt;code&gt;ACTIONS&lt;/code&gt; can be build. This code was of course only my proof-of-concept code.&lt;/p&gt;
&lt;p&gt;To instrument your existing application, you should look out for central places where you can add the code as shown above. In example, many applications use a database connection pool explicitly. By instrumenting the pool and passing additional parameters—like &lt;code&gt;CLIENT_IDENTIFER&lt;/code&gt; and &lt;code&gt;MODULE&lt;/code&gt;—to the &lt;code&gt;get()&lt;/code&gt;/&lt;code&gt;borrow()&lt;/code&gt;/&lt;code&gt;whatever()&lt;/code&gt; method, much of the overhead can be centralized.&lt;/p&gt;
&lt;p&gt;The best way to instrument your application depends to the frameworks in use. That’s the time to mention that I am &lt;a href="http://www.fatalmind.com/consulting/" title="Markus Winand&amp;#39;s consulting page"&gt;consultant&lt;/a&gt;, let me know if I can help you &lt;img src="http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif" alt=";)"&gt; &lt;/p&gt;
&lt;h3&gt;Performance Comparison&lt;/h3&gt;
&lt;p&gt;I have written an example Java implementation—on basis of the class presented above—to check the performance impact of a properly instrumented application. To make the long story short: in a networked environment there isn’t any considerable impact when properly instrumenting a Java application. In case the application and the database are hosted on the same node, if no network involved, the impact might become noticeable (8% in my experiment), but this was still an extreme case. Generally I believe that a &lt;strong&gt;proper&lt;/strong&gt; instrumentation does not impact performance in a noticeable way.&lt;/p&gt;
&lt;p&gt;For my performance experiment, I used a program that issued the same SQL statement many thousand times. To keep the execution time of the statement low, I have used a very trivial statement:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;select 1 from dual&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have executed the program with and without instrumentation for the following scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Without any tracing enabled&lt;/li&gt;
&lt;li&gt;With full tracing enabled (trace everything)&lt;/li&gt;
&lt;li&gt;Trace only every second SQL execution (only with the instrumented code)&lt;/li&gt;
&lt;li&gt;Trace only 1% of the executed statements (only with the instrumented code).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The results are in the chart in a networked environment—application and database hosted on different nodes:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Performance comparison" height="250" src="http://myfatalmind.files.wordpress.com/2009/11/frame1.png?w=580&amp;amp;h=250&amp;amp;h=250" title="Performance comparison" width="580"&gt;&lt;/p&gt;
&lt;p&gt;There is &lt;strong&gt;NO&lt;/strong&gt; measurable impact when tracing is disabled. This is the most important aspect. You can just add the needed &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;, &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; and it will not harm performance—when done properly. When tracing is enabled, there actually &lt;strong&gt;IS&lt;/strong&gt; a performance drawback. I am not sure where this comes from; I could imagine that the additional data written into the trace file—module name and so on—cause the regression . However, that impact is still &lt;strong&gt;minor&lt;/strong&gt;, 5% in my extreme test scenario. For test case 3 and 4 there is no direct comparison because partial tracing requires the instrumented code. The checked bars are the results of the code without instrumentation but full tracing enabled—the only way to gather similar SQL trace files. You see that the performance impact relates to the traced volume. When tracing only a very small fraction of the overall code the impact is hardly measurable.&lt;/p&gt;
&lt;p&gt;All of this requires a &lt;strong&gt;proper instrumentation&lt;/strong&gt; of the application not to cause additional round trips.&lt;/p&gt;
&lt;h3&gt;The Administrative Interface&lt;/h3&gt;
&lt;p&gt;With Oracle 10g, yet another way to enable tracing is provided in form of the &lt;a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e10577/d_monitor.htm#i1003993"&gt;&lt;code&gt;DBMS_MONITOR&lt;/code&gt;&lt;/a&gt; package. This package adds some great tools to perform SQL Tracing in a real world scenario.&lt;/p&gt;
&lt;p&gt;Instead of&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;ALTER SESSION SET SQL_TRACE=TRUE;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;ALTER SESSION SET EVENT='10046 trace name context forever, level 4'&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;we can now use&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SESSION_TRACE_ENABLE(binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is actually the same in pink—except the less cryptic syntax—no benefit. Very often we want to enable tracing from &lt;em&gt;outside&lt;/em&gt; the current session, which is also possible with the same procedure:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SESSION_TRACE_ENABLE(sid, serial#, binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Again, a nicer way to do what was possible before; with &lt;code&gt;DBMS_SYSTEM.SET_EV&lt;/code&gt;. But now, you have only one package to remember: &lt;code&gt;DMBS_MONITOR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes we want to trace as session that will be opened somewhat later. For example, a batch job that runs at night. For this case the traditional solution was to create a &lt;code&gt;SYSTEM LOGON&lt;/code&gt; trigger. The body of the trigger typically checks some conditions to decide to trace or not to trace. Very often, the decision is based on the user-name that is fetched via &lt;code&gt;SYS_CONTEXT/USERENV&lt;/code&gt; or information from &lt;code&gt;v$session&lt;/code&gt;. Many times, applied guesswork is added to the trigger. As a last resort, if it’s not even possible to trace all session that are opened during a specific time frame, it’s also possible to trace much more than needed and filter afterwards.&lt;/p&gt;
&lt;p&gt;In a real world scenario, tracing everything is often not feasible because of the &lt;em&gt;huge data volume&lt;/em&gt; created. Many people also argue that there is a considerable performance hit. Although I admit, and demonstrate below, that there &lt;em&gt;IS&lt;/em&gt; a performance hit, it is usually negligible if neither waits nor binds are traced. The reason that tracing can’t be enabled globally on a production system, is the disk space requirement.&lt;/p&gt;
&lt;h3&gt;The DBMS_MONITOR Killer Features&lt;/h3&gt;
&lt;p&gt;The real power of &lt;code&gt;DBMS_MONTOR&lt;/code&gt; isn’t that it provides a more convenient interface to enable tracing. The real power comes from&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the ability to persistently configure tracing&lt;/li&gt;
&lt;li&gt;the ability to perform fine grained, selective tracing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With &lt;code&gt;DBMS_MONITOR&lt;/code&gt;, the need for &lt;code&gt;LOGON&lt;/code&gt; triggers is gone. E.g., to enable tracing for all active and all &lt;em&gt;future&lt;/em&gt; session for a specific service—like &lt;code&gt;SERVICE_NAME&lt;/code&gt; in the connect descriptor—use the following:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE('SERVICE_NAME');&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This will enable tracing for all current and future sessions with the specified &lt;code&gt;SERVICE_NAME&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second killer feature is the ability to perform selective tracing based on&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client identifier&lt;/li&gt;
&lt;li&gt;module&lt;/li&gt;
&lt;li&gt;action&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are session properties can be set the the application. When done properly, the application can change those properties at a &lt;em&gt;very high frequency&lt;/em&gt;—for every statement—&lt;em&gt;without&lt;/em&gt; introducing a considerable performance hit.&lt;/p&gt;
&lt;p&gt;For example, if the application sets the &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt; to reflect the user ID of a websites user, tracing can be enabled for that particular user only. Similarly selective tracing on &lt;code&gt;MODULE&lt;/code&gt; and &lt;code&gt;ACTION&lt;/code&gt; level is possible. Consider the following activities happening in a session:&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr align="left"&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;statement&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD1&lt;/td&gt;
&lt;td&gt;ACT1&lt;/td&gt;
&lt;td&gt;select * from …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD1&lt;/td&gt;
&lt;td&gt;ACT2&lt;/td&gt;
&lt;td&gt;insert into …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD2&lt;/td&gt;
&lt;td&gt;ACT1&lt;/td&gt;
&lt;td&gt;update…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MOD2&lt;/td&gt;
&lt;td&gt;ACT3&lt;/td&gt;
&lt;td&gt;delete …&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With &lt;code&gt;DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE&lt;/code&gt; we can enable tracing for &lt;code&gt;MOD1&lt;/code&gt; only if you like:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE('SERVICE_NAME', 'MOD1');&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The effect is as expected: only the statements attributed to MOD1 will be traced. Consequently you can drill down to trace only ACT1 within MOD1:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE(&amp;#39;SERVICE_NAME&amp;#39;, &amp;#39;MOD1&amp;#39;,
                                  &amp;#39;ACT1&amp;#39;, waits=&amp;gt;true, binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;This will trace only the &lt;em&gt;insert&lt;/em&gt; statement, with wait and bind data. Similar possibilities exist for the &lt;code&gt;CLIENT_IDENTIFIER&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;exec DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE(&amp;#39;CLIENT_IDENTIFIER&amp;#39;,
                                         waits=&amp;gt;false, binds=&amp;gt;true);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Remember that all of this is persistent, even across database restarts. So we need to disable it again. Of course we will forget what was enabled, so there is a table to query for that:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; SELECT trace_type, primary_id,
            qualifier_id1 ID1, qualifier_id2 ID2,
            waits, binds
       FROM dba_enabled_traces;&lt;/pre&gt;
&lt;pre&gt;TRACE_TYPE             PRIMARY_ID        ID1    ID2   WAITS BINDS
---------------------- ----------------- ------ ----- ----- -----
SERVICE_MODULE_ACTION  SERVICE_NAME      MOD1   ACT1  TRUE  TRUE
SERVICE                SERVICE_NAME                   TRUE  FALSE
SERVICE_MODULE         SERVICE_NAME      MOD1         TRUE  FALSE
CLIENT_ID              CLIENT_IDENTIFIER              FALSE TRUE&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;With these methods we can enable tracing on a very fine granularity in a very convenient way. Since the tracing can be focused, space and performance issues are less problematic.&lt;/p&gt;
&lt;h3&gt;Résumé&lt;/h3&gt;
&lt;p&gt;Once an application is instrumented, fine-grained tracing can be applied very straight. Because of the reduced overhead and disk space requirements, SQL Tracing might become a more frequently used tool to troubleshoot poor performance.&lt;/p&gt;
&lt;p&gt;You can choose the effort you put into instrumentation by yourself. For example, you might choose to set the &lt;code&gt;MODULE&lt;/code&gt; only for one particular module that you need to trace right now, and don’t adopt other modules. In a step-by-step manner you can extend the instrumentation as needed.&lt;/p&gt;
&lt;p&gt;During my experiments for this article I have found some traps you should be aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;The &lt;code&gt;ACTION&lt;/code&gt; seems to consider only 8 characters&lt;/span&gt;&lt;br&gt;During my tests I noticed that the significant length of the &lt;code&gt;ACTION&lt;/code&gt; seems to be only 8 characters (&lt;a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_appinf.htm#i999254" title="DBMS_APPLICATION_INFO package"&gt;although documented as 32 bytes&lt;/a&gt;). However, any attempt to enable tracing for an &lt;code&gt;ACTION&lt;/code&gt; longer than 8 bytes failed on my 10g test machine.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;A wrong implementation might introduce a major performance penalty&lt;/span&gt;&lt;br&gt;Double check your implementation for that.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;&lt;code&gt;SERVICE_NAME&lt;/code&gt; might be set to an unexpected value&lt;/span&gt;&lt;br&gt;Depending on the way the application connects, &lt;code&gt;SERVICE_NAME&lt;/code&gt; might not be present so that it defaults to &lt;code&gt;SYS$USER&lt;/code&gt;. If it doesn’t work on the first try, verify that the sessions you want to trace has the expected &lt;code&gt;SERVICE_NAME&lt;/code&gt; (&lt;code&gt;select serivce_name from v$session....&lt;/code&gt;). Use the &lt;a href="http://www.rojotek.com/blog/2008/01/04/oracle-sid-service_name/" title="SID != SERVICE_NAME"&gt;correct connect URL&lt;/a&gt; with the thin driver.&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;When using &lt;code&gt;DBMS_MONITOR&lt;/code&gt;, wait event tracing is &lt;em&gt;TRUE&lt;/em&gt; per default&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s not &lt;strong&gt;fatal&lt;/strong&gt; to open your &lt;strong&gt;mind&lt;/strong&gt; to new approaches.&lt;/p&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/45/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/45/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=45&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/xKaCpHUUP2uSvlgqgQxBotdUh9E/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xKaCpHUUP2uSvlgqgQxBotdUh9E/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/xKaCpHUUP2uSvlgqgQxBotdUh9E/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xKaCpHUUP2uSvlgqgQxBotdUh9E/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/9NhrAGwypyk" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /><media:content url="http://myfatalmind.files.wordpress.com/2009/11/frame1.png?w=580&amp;h=250" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2009/11/24/to-trace-or-not-to-trace/</feedburner:origLink></item><item><title>Pipelined Functions: Better Than DBMS_OUTPUT</title><link>http://feedproxy.google.com/~r/orana/~3/GZhAxPzKqUU/</link><category>Portability</category><category>oracle</category><category>pl/sql</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Markus Winand</dc:creator><pubDate>Wed, 11 Nov 2009 07:36:06 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/7b1f219d99ed5c13</guid><description>&lt;p&gt;Every now and then I need some PL/SQL that prints something to the terminal. The traditional solution for this is &lt;code&gt;DBMS_OUTPUT.PUT_LINE&lt;/code&gt;. If you used it before, you probably know that there are some obstacles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t forget to &lt;code&gt;set serveroutput on&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Don’t forget to set an appropriate buffer size (but there is still a absolute maximum)&lt;/li&gt;
&lt;li&gt;Don’t wonder about missing blanks&lt;/li&gt;
&lt;li&gt;No way to &lt;em&gt;flush&lt;/em&gt; the output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In case you don’t know yet, the first three can be taken care of by issuing the following in SQL*Plus:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;set serveroutput on size 1000000 format wrapped;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, the absolute buffer size limit remains. The years have passed, and the limitations were accepted. Until I noticed that there is an alternative.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;
&lt;p&gt;With release 9iR2, Oracle delivered a package to format the &lt;code&gt;PLAN_TABLE&lt;/code&gt;’s content: &lt;code&gt;DBMS_XPLAN&lt;/code&gt;. A very nifty tool which finally rendered the SQLs to format the explain plan useless. But explain plan is not today’s topic; lets just focus in how the formatted plan reaches our screen:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;select * from table(DBMS_XPLAN.DISPLAY);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The easiest way to use the package is shown above. It’s obvious that the data is actually fetched by regular &lt;code&gt;select&lt;/code&gt; statement. You will also notice this by SQL*Plus artifacts like the feedback line (8 rows selected):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt; explain plan for select * from dual;

Explained.

SQL&amp;gt;  select * from table(DBMS_XPLAN.DISPLAY);

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------
Plan hash value: 272002086

---------------------------------------------------------------------
| Id | Operation         | Name | Rows | Bytes | Cost (%CPU)| Time  |
---------------------------------------------------------------------
|  0 | SELECT STATEMENT  |      |    1 |     2 |     2   (0)| 00:01 |
|  1 |  TABLE ACCESS FULL| DUAL |    1 |     2 |     2   (0)| 00:01 |
---------------------------------------------------------------------

8 rows selected.

SQL&amp;gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The table you see isn’t actually a table (as the database knows it), it’s just formatted output. When using &lt;code&gt;DBMS_XPLAN&lt;/code&gt;, I always noted to myself to investigate this. Some years later, the time has come.&lt;/p&gt;
&lt;h3&gt;Pipelined Table Functions&lt;/h3&gt;
&lt;p&gt;Oracle’s (pipelined) table functions are PL/SQL functions that return data as (more or less) regular tables that can be accessed with regular SQL. The &lt;em&gt;pipelined&lt;/em&gt; feature makes it possible to fetch the first records while the function is still running.&lt;/p&gt;
&lt;p&gt;It seems that the primary target of this feature are custom analytic functions and data re-arranging like pivot. However, I use it to get rid of &lt;code&gt;DBMS_OUTPUT&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Hello World&lt;/h3&gt;
&lt;p&gt;The following is the most simplistic example how to use a pipelined function to return arbitrary output:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;create or replace type piped_output_table as table of varchar2(4000);
/&lt;/b&gt;

create or replace
 function hello_world &lt;b&gt;return piped_output_table pipelined&lt;/b&gt; IS
 text varchar2(4000);
begin
 text := 'Hello world';
&lt;b&gt; pipe row(text);
 RETURN;&lt;/b&gt;
END;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;hello_world&lt;/code&gt; function must be wrapped in a &lt;code&gt;table()&lt;/code&gt; expression to access it like a regular table in SQL:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  select * from table(hello_world);
COLUMN_VALUE
---------------------------------------------------------------------
Hello world

1 row selected.

SQL&amp;gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;We have already mastered everything important to replace &lt;code&gt;DBMS_OUTPUT&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define a &lt;code&gt;table type&lt;/code&gt; to be used as return type of the function&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;pipelined&lt;/code&gt; keyword&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;PIPE ROW()&lt;/code&gt; statement to actually produce a row.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;RETURN&lt;/code&gt; statement without an argument.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, there are two more issues to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You might want to have a different column name (instead of &lt;code&gt;COLUMN_VALUE&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You might need to produce output from nested functions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Custom Column Names&lt;/h3&gt;
&lt;p&gt;The first issue is rather simple; to name the returned column(s) you must create a object type and base the table type upon this object type:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;&lt;b&gt;create or replace type piped_output is object (output varchar2(4000));
/&lt;/b&gt;

create or replace type piped_output_table is table of &lt;b&gt;piped_output&lt;/b&gt;;
/

create or replace function
   hello_world return piped_output_table pipelined IS
   &lt;b&gt;rec piped_output := piped_output(NULL);&lt;/b&gt;
begin
   &lt;b&gt;rec.output&lt;/b&gt; := 'Hello world';
   pipe row(&lt;b&gt;rec&lt;/b&gt;);
   RETURN;
END;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The column header can have an arbitrary column name. Since it is a column name, all respective limitations apply (e.g., no spaces).&lt;/p&gt;
&lt;h3&gt;Nested Functions&lt;/h3&gt;
&lt;p&gt;The final requirement to replace &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; is produce output from nested function calls. With &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; this is rather straight:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE dbms_output_nested IS
   PROCEDURE hello_world;
END;
/

CREATE OR REPLACE PACKAGE body dbms_output_nested IS
   PROCEDURE nested_procedure IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('  hello world from nested function') ;
   END nested_procedure;

   PROCEDURE hello_world IS
   BEGIN
      DBMS_OUTPUT.ENABLE();
      DBMS_OUTPUT.PUT_LINE('hello world') ;
      nested_procedure;
   END hello_world;
END dbms_output_nested;
/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately, I am not aware of an easy way to accomplish this with pipelined table functions. It seems that there is no way to directly pipe one functions output into the output pipe of another function. So far, the only solution I found is to explicitly copy the records over:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE piped_nested IS
   TYPE piped_output IS RECORD (output VARCHAR2(4000));
   TYPE piped_output_table IS TABLE OF piped_output;

   FUNCTION hello_world
     RETURN piped_nested.piped_output_table PIPELINED;
&lt;b&gt;   FUNCTION nested_procedure
     RETURN piped_nested.piped_output_table PIPELINED;&lt;/b&gt;
END;
/

CREATE OR REPLACE PACKAGE BODY piped_nested IS
   FUNCTION nested_procedure
     RETURN piped_nested.piped_output_table PIPELINED IS
      rec piped_nested.piped_output;
   BEGIN
      rec.output := '   hello world from nested function';
      PIPE ROW(rec);
      RETURN;
   END;

   FUNCTION hello_world
     RETURN piped_nested.piped_output_table PIPELINED IS
      rec piped_nested.piped_output;
   BEGIN
      rec.output := 'Hello World';
      PIPE ROW(rec);

&lt;b&gt;      FOR r IN (select * from table(nested_procedure)) LOOP
         rec.output := r.output;
         PIPE ROW(rec);
      END LOOP;&lt;/b&gt;

      RETURN;
   END;
END piped_nested;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You see that there are two major drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;nested_procedure&lt;/code&gt; is public&lt;/li&gt;
&lt;li&gt;The copy bloats the &lt;code&gt;hello_world&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As mentioned above, I don’t have a good solution for this (let me know if you find something).&lt;/p&gt;
&lt;h3&gt;Mixing DBMS_OUTPUT with Pipelined Functions&lt;/h3&gt;
&lt;p&gt;If you have legacy PL/SQL code that uses &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; and would like to migrate to pipelined functions, you might consider the following wrapper:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE dbms_output_table IS
   TYPE piped_output IS RECORD (dbms_output VARCHAR2(4000));
   TYPE piped_output_table IS TABLE OF piped_output;
   FUNCTION display RETURN piped_output_table PIPELINED;
END;
/

CREATE OR REPLACE PACKAGE BODY dbms_output_table IS
   FUNCTION display RETURN piped_output_table PIPELINED IS
      rec piped_output;
      p_status NUMBER;
   BEGIN
      DBMS_OUTPUT.GET_LINE(rec.dbms_output, p_status);
      WHILE p_status = 0 LOOP
         PIPE ROW(rec);
         DBMS_OUTPUT.GET_LINE(rec.dbms_output, p_status);
      END LOOP;
      RETURN;
   END;
END;
/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;With this function you can retrieve the current content of the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; buffer via a pipelined table function. Please note that the producer of the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; must enable the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; facility and SQL*Plus must be &lt;code&gt;set serverout off&lt;/code&gt; (otherwise SQL*Plus would retrieve it):&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;SQL&amp;gt;  exec dbms_output_nested.hello_world;
hello world
hello world from nested function

PL/SQL procedure successfully completed.

SQL&amp;gt; set serverout off
SQL&amp;gt;  exec dbms_output_nested.hello_world;

PL/SQL procedure successfully completed.

SQL&amp;gt; select * from table(dbms_output_table.display);

DBMS_OUTPUT
---------------------------------------------------------------------
hello world
 hello world from nested function

SQL&amp;gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The Best of Both Worlds?&lt;/h3&gt;
&lt;p&gt;This—and similar methods—can be used so that nested functions still use the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; interface, while the front end function returns its output as pipelined table. A mixed approach allows you to hide the nested function—no need to have them in the public package declaration—but reapplies some of the &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; limitations; the maximum buffer size (although at smaller level), no flush.&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;CREATE OR REPLACE PACKAGE mixed_package IS
   TYPE piped_output IS RECORD (output VARCHAR2(4000));
   TYPE piped_output_table IS TABLE OF piped_output;
   FUNCTION hello_world RETURN piped_output_table PIPELINED;
END;
/

CREATE OR REPLACE PACKAGE BODY mixed_package IS
   PROCEDURE prepare_dbms_output IS
   BEGIN
      DBMS_OUTPUT.DISABLE; -- removes any stale data
      DBMS_OUTPUT.ENABLE(1000000);
   END;

   PROCEDURE nested_function IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('   Hello world from nested function');
   END;

   FUNCTION hello_world RETURN piped_output_table PIPELINED IS
      rec piped_output;
      p_status NUMBER;
   BEGIN
      prepare_dbms_output;

      rec.output := 'Hello World';
      PIPE ROW(rec);

      nested_function;

      DBMS_OUTPUT.GET_LINE(rec.output, p_status);
      WHILE p_status = 0 LOOP
         PIPE ROW(rec);
         DBMS_OUTPUT.GET_LINE(rec.output, p_status);
      END LOOP;

      RETURN;
   END;
END;
/
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can also use other methods to return row data from nested functions and pass it on to the PIPE ROW statement in the topmost stack frame. No matter in which way pass rows from nested functions, you must be aware that they are passed as a bulk when the nested function finishes. That means, the are kind of &lt;em&gt;flushed&lt;/em&gt; once for each nested function call. If all you data is returned from a nested function, you might &lt;em&gt;effectively disable the pipelined&lt;/em&gt; feature.&lt;/p&gt;
&lt;h3&gt;Final Comparison and Critique&lt;/h3&gt;
&lt;p&gt;The user interface of pipelined table functions is much better than the well established &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;No separate API needed to access the returned data&lt;/span&gt;&lt;br&gt;Most programming languages don’t have a convenient way to access the &lt;code&gt;serveroutput&lt;/code&gt;. Very often the only way to fetch the buffer is to manually call &lt;code&gt;DBMS_OUTPUT.GET_LINES&lt;/code&gt;. On a quick view, only the perl DBI/DBD::Oracle framework has addressed the problem (a little bit):
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://search.cpan.org/~pythian/DBD-Oracle-1.23/Oracle.pm#dbms_output_enable_/_dbms_output_put_/_dbms_output_get" title="DBD::Oracle"&gt;perl DBD::Oracle documentation about DBMS_OUTPUT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:45027262935845" title="Ask Tom on DBMS_OUTPUT via JDBC"&gt;Tom Kyte about fetching DBMS_OUTPUT buffer with JDBC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=204531" title="Fetching DBMS_OUTPUT with php"&gt;forums.oracle.com entry about php and DBMS_OUTPUT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But wait, your scripts are anyways intended for SQL*Plus usage only? Besides the fact that you &lt;em&gt;never &lt;/em&gt;know in which context your scripts are used, there are also other user interfaces than SQL*Plus to access an Oracle database. There are many IDE that support database access. Most of those have support for &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; in the meanwhile but it’s different in each implementation. You can avoid a great deal of questions where to find the output of your script in tool XYZ by using table functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;No size limitation&lt;/span&gt;&lt;br&gt;But wait, your script isn’t returning much data? Yes, you usually know how much data your script will return. With a call to &lt;code&gt;DBMS_OUTPUT.ENABLE&lt;/code&gt; you can make the buffer big enough. But if you are wrong? You are lucky if the user knows how to enlarge the buffer. You are even more happy if the hard limit of the buffer size is not reached (1,000,000 bytes). I know of a case where the user was required to change a script to use &lt;code&gt;UTL_FILE&lt;/code&gt; because the output was a few megabytes (luckily they had access to the database back-end, where the file was written).&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Pipelined&lt;/span&gt;&lt;br&gt;Self-explaining? No major usability benefit, I guess. However, it is a benefit to see that something is happening while the function is running. It is definitely a benefit not to buffer everything in memory until the end—that is probably the reason why there is a hard limit on the buffer size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I guess the article gave you insight to the major drawback of the pipelined table function method as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;Producing output from nested function calls is a pain in the a**&lt;/span&gt;&lt;br&gt;It is possible, but inconvenient—if not delusive because it exploits internal functions in the public interface. The use of collections in the nested function makes it possible to maintain a clean public interface. However, copying the data over and over again is inconvenient, if not problematic, because it might reintroduce the size issue (when using &lt;code&gt;DBMS_OUTPUT&lt;/code&gt; the already mentioned limit of 1,000,000 bytes per nested function call apply).&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration:underline"&gt;DML in the table function cannot participate in the caller’s transaction&lt;/span&gt;&lt;br&gt;&lt;b&gt;UPDATE 2010-03-11:&lt;/b&gt; &lt;a href="http://blog.sydoracle.com/"&gt;Gary&lt;/a&gt; &lt;a href="http://blog.fatalmind.com/2009/11/11/pipelined-functions-better-then-dbms_output/#comments"&gt;commented&lt;/a&gt; that
&lt;pre&gt;select * from table(hello_world)&lt;/pre&gt;
&lt;p&gt;works only if the function does not perform any DML. Gary is of course right; any attempt to do so results in &lt;a href="http://ora-14551.ora-code.com/"&gt;ORA-14551&lt;/a&gt;. The only possibility to perform DML in the function is to use an &lt;a href="http://www.orafaq.com/wiki/Autonomous_transaction"&gt;autonomous transaction&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My personal Best Current Practice is to use other methods (collections or even &lt;code&gt;DBMS_OUTPUT&lt;/code&gt;) in the private functions, but use a pipelined function interface for the user. This is sometimes very inconvenient to code but the improved user experience is worth that effort.&lt;/p&gt;
&lt;p&gt;Of course I would have some wishes how the situation could be improved. The most powerful way would be to allow something like this:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;PIPE ROW(nested_pipelined_function);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Where &lt;code&gt;nested_pipelined_function&lt;/code&gt; doesn’t need to be part of the public package interface. Each &lt;code&gt;PIPE ROW()&lt;/code&gt; in the nested function should directly output as if was written in the calling function. On the other hand, it would also be great to have something to pipe a complete collection at once:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;PIPE ROWS(collection);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am well aware that the usage of pipelined table functions as demonstrated in this article might not reflect the intended usage of that feature, thus its fitness for that purpose is limited.&lt;/p&gt;
&lt;p&gt;It’s not &lt;strong&gt;fatal&lt;/strong&gt; to open your &lt;strong&gt;mind&lt;/strong&gt; to new approaches.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Similar articles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html"&gt;http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/myfatalmind.wordpress.com/4/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/myfatalmind.wordpress.com/4/"&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=blog.fatalmind.com&amp;amp;blog=10300405&amp;amp;post=4&amp;amp;subd=myfatalmind&amp;amp;ref=&amp;amp;feed=1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/hJgA9LBUcYHfzJ0TdCALY6ua3kU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hJgA9LBUcYHfzJ0TdCALY6ua3kU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/hJgA9LBUcYHfzJ0TdCALY6ua3kU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hJgA9LBUcYHfzJ0TdCALY6ua3kU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/GZhAxPzKqUU" height="1" width="1"/&gt;</description><media:group xmlns:media="http://search.yahoo.com/mrss/"><media:content url="http://0.gravatar.com/avatar/6855feeb83ac8a3e397bc8260bad8294?s=96&amp;d=identicon&amp;r=G" /></media:group><feedburner:origLink>http://blog.fatalmind.com/2009/11/11/pipelined-functions-better-then-dbms_output/</feedburner:origLink></item><item><title>More on Single-Action UIs</title><link>http://feedproxy.google.com/~r/orana/~3/6RvIp5ZlLtQ/more-on-single-action-uis.html</link><category>usability</category><category>iPod</category><category>Apple</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">John Russell</dc:creator><pubDate>Wed, 10 Mar 2010 18:49:53 PST</pubDate><guid isPermaLink="false">tag:google.com,2005:reader/item/564ac4ddf1f7c79d</guid><description>In my post Deconstructing the iPod Shuffle UI, I talked a bit about the notion of a limited UI where you really only do one thing -- in that case, click the button on the headphones.Every now and then, I rediscover the infrared remote that goes with my iMac, and realize that for many mundane tasks, the remote does everything I need. (Mira lets me assign different actions for the remote button for
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/g7hqTf6Mw1AK7J4RZE2hCf-plk0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/g7hqTf6Mw1AK7J4RZE2hCf-plk0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/g7hqTf6Mw1AK7J4RZE2hCf-plk0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/g7hqTf6Mw1AK7J4RZE2hCf-plk0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/orana/~4/6RvIp5ZlLtQ" height="1" width="1"/&gt;</description><feedburner:origLink>http://tahitiviews.blogspot.com/2010/03/more-on-single-action-uis.html</feedburner:origLink></item></channel></rss>
