<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1271763227002553835</id><updated>2012-02-01T11:36:56.083+01:00</updated><category term='debug'/><category term='Function Based Index'/><category term='xml'/><category term='ODTUG KScope 2011'/><category term='Plugin'/><category term='overloading boolean'/><category term='SQL'/><category term='Red Gate'/><category term='2011'/><category term='associative arrays'/><category term='OOW2011'/><category term='PL/SQL'/><category term='Diacritic'/><category term='Oracle 10g'/><category term='Oracle 11g'/><category term='APEX'/><category term='regexp'/><category term='Analytic Functions'/><category term='SOA'/><category term='odtug kaleidoscope 2009'/><category term='XE'/><category term='EBR'/><category term='presentation'/><category term='Materialized View'/><category term='exceptions'/><category term='MERGE'/><category term='OOW2010'/><category term='SQL Developer'/><category term='UKOUG'/><category term='Oracle 9i'/><category term='Oracle Text'/><category term='ODTUG Kaleidoscope 2010'/><category term='Virtual Column'/><title type='text'>Notes on Oracle</title><subtitle type='html'>Oracle Things I Got to Remember Not to Forget</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>71</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3012777222314406965</id><published>2012-01-26T16:17:00.000+01:00</published><updated>2012-01-26T16:17:25.064+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Red Gate'/><title type='text'>Comparing Schemas: Red Gate Schema Compare for Oracle</title><content type='html'>This week I attended a webinar by Cary Millsap, organized by Red Gate. It was called "Real Developers DO use Tools" and was focussed on, well, tools. At the end of Cary's talk, James Murtagh showed a demo of their Schema Compare tool. The demo wasn't flawless, but he recovered nicely :) (note to self: don't do live demo's). And this - the demo, not the occuring error - triggered a memory that I wanted to write a blog on the Schema Compare tool.&lt;/br&gt;If you missed the webinar you can find it on the website of Red Gate here: &lt;a href="http://www.red-gate.com/products/oracle-development/deployment-suite-for-oracle/webinars/webinar-archive" target="_blank"&gt;http://www.red-gate.com/products/oracle-development/deployment-suite-for-oracle/webinars/webinar-archive&lt;/a&gt;&lt;/br&gt;I had the pleasure of meeting James Murtagh during the &lt;a href="http://nuijten.blogspot.com/2011/12/looking-back-at-ukoug-2011.html" target="_blank"&gt;UKOUG annual conference at the end of last year&lt;/a&gt;. James also hosts &lt;a href="http://allthingsoracle.com/" target="_blank"&gt;the All Things Oracle site&lt;/a&gt;. While we were discussing different topics (even non-oracle) he asked me which tool is my "development tool of choice". The tool I use predominantly is PL/SQL Developer by Allround Automations. "We have a plugin for that",... time to take a closer look at the tool.&lt;/br&gt;&lt;a name='more'&gt;&lt;/a&gt;First the installation of Schema Compare, simply following the wizard and you're done in a jiffy.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-VQDVkD7ieIk/TyA6cPqjhFI/AAAAAAAABOU/i2zR5xcpiqI/s1600/RedGate1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="238" width="320" src="http://2.bp.blogspot.com/-VQDVkD7ieIk/TyA6cPqjhFI/AAAAAAAABOU/i2zR5xcpiqI/s320/RedGate1.png" /&gt;&lt;/a&gt;&lt;/div&gt;Even though I found out later, if you're on at least PL/SQL Developer 9.0.1, that it is not necessary to download the product separately. This can also be done from within the plugin for PL/SQL Developer. When you're on an older version of PL/SQL Developer, as I am, you need to install the plugin yourself.&lt;/br&gt;Next the plugin, which can be found on the website of Allround Automations in the plugin listing.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-pYnJI5rTg6E/TyA7KIGbGtI/AAAAAAAABOg/4862pMjM2cI/s1600/RedGate2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="171" width="320" src="http://2.bp.blogspot.com/-pYnJI5rTg6E/TyA7KIGbGtI/AAAAAAAABOg/4862pMjM2cI/s320/RedGate2.png" /&gt;&lt;/a&gt;&lt;/div&gt;Installing a plugin in PL/SQL Developer is always very easy, just double click the executable and you're set.&lt;/br&gt;The big question with plugins in PL/SQL Developer is: "How can you access the plugin?" In this case it is very simple, not hidden in some of the other menu's, but available in the main toolbar.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-bnUMBlsmuzg/TyA7sOkWfrI/AAAAAAAABOs/uQORRj1hAjE/s1600/RedGate3.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="174" width="301" src="http://1.bp.blogspot.com/-bnUMBlsmuzg/TyA7sOkWfrI/AAAAAAAABOs/uQORRj1hAjE/s320/RedGate3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/br&gt;Time to try out the Schema Compare from my PL/SQL Developer environment. It start the tool automatically, and prompt you for the database connections. Unfortunately it doesn't use the same "connection list" as PL/SQL Developer, so you will have to enter the connection information yourself. At least for me it didn't work as I hoped, I checked with James Murtagh and showed me that it actually does work. Maybe a PL/SQL Developer version specific thingy (I use version 8).&lt;/br&gt; Fortunate for me, the plugin now support Oracle Instant Client (thanks Tom).&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-tphrlaHtf48/TyA8rx74JVI/AAAAAAAABO4/LO8LB2yEm3k/s1600/RedGate4.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="196" width="320" src="http://2.bp.blogspot.com/-tphrlaHtf48/TyA8rx74JVI/AAAAAAAABO4/LO8LB2yEm3k/s320/RedGate4.png" /&gt;&lt;/a&gt;&lt;/div&gt;Just fill in the Source and the Target database connection information, pick the schemas that you want to compare and push the "Compare Schema" button.&lt;/br&gt;Next is a progress window shown... The following screenshot took me several takes to get, the comparison is done really fast. Of course this was done on my test database with only several hundreds of objects in the user schemas, but even on our production system the speed was amazing.... The following screenshot took me several takes to get, maybe I'm just too slow.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/--8wgEBcidnE/TyBDeZzLMOI/AAAAAAAABPQ/32BoUxfm-a8/s1600/comparing.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="215" width="320" src="http://3.bp.blogspot.com/--8wgEBcidnE/TyBDeZzLMOI/AAAAAAAABPQ/32BoUxfm-a8/s320/comparing.png" /&gt;&lt;/a&gt;&lt;/div&gt;When the comparison is done, you get an overview of all the differences that exist between the two schemas as shown in the next screenshot.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-37J0QT3zONw/TyBDvFzO6EI/AAAAAAAABPk/W5LOrp6UVhY/s1600/differences.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="213" width="320" src="http://1.bp.blogspot.com/-37J0QT3zONw/TyBDvFzO6EI/AAAAAAAABPk/W5LOrp6UVhY/s320/differences.png" /&gt;&lt;/a&gt;&lt;/div&gt;Double clicking on the big blue arrow will reverse the comparison, which can come in very handy if you reverse the Source and Target databases, &lt;i&gt;right, James?&lt;/i&gt;&lt;/br&gt;Now you can inspect all differences, and decide which differences you want to deploy to the Target schema. Just check the boxes of the objects you want to deploy.&lt;/br&gt;After you make your selection, start up the Deployment Wizard (top of the screen, which is visible in the screenshot. Resizing the window apparently moves the button to the second line of buttons.).&lt;/br&gt;In three steps you can deploy what you have selected.&lt;/br&gt;In step one, you decide the deployment method - I choose a deployment script. What is kind of odd that the default "Open the script in " option is set to Oracle SQL Developer, even though I started the program from PL/SQL Developer. Can be changed easily, odd none the less.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-VFwtycmjXFE/TyBGAA-gLDI/AAAAAAAABP0/ourT6CiTGwE/s1600/deploy1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="204" width="320" src="http://3.bp.blogspot.com/-VFwtycmjXFE/TyBGAA-gLDI/AAAAAAAABP0/ourT6CiTGwE/s320/deploy1.png" /&gt;&lt;/a&gt;&lt;/div&gt;The second step analyses the dependencies for you. In my example I choose to deploy a few sequences, which apparently have a dependency in some triggers, packages and even a view. &lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-VhkSGKQZ3wo/TyBGAUT31DI/AAAAAAAABP8/TqXcTRPmYw8/s1600/deploy2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="204" width="320" src="http://3.bp.blogspot.com/-VhkSGKQZ3wo/TyBGAUT31DI/AAAAAAAABP8/TqXcTRPmYw8/s320/deploy2.png" /&gt;&lt;/a&gt;&lt;/div&gt;The last step is an overview of the steps that are going to be taken, like dropping object, creating or recreating objects. Always a good idea to review, you can never be too careful.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-im667H6-2uI/TyBGAm7N9hI/AAAAAAAABQI/CeaCYdOIh6k/s1600/deploy3.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="204" width="320" src="http://2.bp.blogspot.com/-im667H6-2uI/TyBGAm7N9hI/AAAAAAAABQI/CeaCYdOIh6k/s320/deploy3.png" /&gt;&lt;/a&gt;&lt;/div&gt;Maybe it's a language thing - I'm not a native English speaker - but in step two of the deployment wizard, where it shows the referenced objects, there is a check box with the label "Deploy all affected objects (Recommended)". My assumption was that the objects listed, like the packages, triggers and the view were going to be deployed to the target database. As often with assumptions, I was wrong. The only thing regarding these objects in the deployment script is "Drop the object", which seems odd to me. Deploying sequences can - at best - invalidate some objects, no reason to drop the affected objects.&lt;/br&gt;&lt;/br&gt;&lt;h3&gt;Overall Impression&lt;/h3&gt;I like the way it works, right from my development tool I can start it up. Would be nice if I could use my existing database connections (all listed in PL/SQL Developer) but that's only a minor detail. But this should work and when I upgrade to a newer version of PL/SQL Developer it should be gone. The connections can be saved in Schema Compare as well.&lt;/br&gt;The differences between the two schemas are mapped out in a very orderly fashion. Differences of objects which exist in both the source as the target schema, objects that exist in either the source or the target, and there is even a section of objects which are identical in both schemas. This list is very clear, easy to navigate through and the differences per object are highlighted so you can easily see the differences.&lt;/br&gt;Personally I would also opt to have a script which I can review and keep in version control, instead of having it deploy automatically. Maybe I'm just paranoid, but that's the way I would do it. Of course you can also do an automatic deployment, followed by another comparison, and deploy again, continuing until there are no more differences left, but this wouldn't be my choice.&lt;/br&gt;This doesn't mean you can't review the script before you run it, you can. And I strongly suggest you do this.&lt;/br&gt;What really impressed me was the speed of the comparison. Comparing two schemas is done really quickly, even with a couple of medium sized production schemas.&lt;/br&gt;&lt;/br&gt;&lt;h2&gt;Links&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://allthingsoracle.com/" target="_blank"&gt;All Things Oracle&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.red-gate.com/products/oracle-development/" target="_blank"&gt;RedGate Oracle Deployment Tools&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.allroundautomations.com/" target="_blank"&gt;PL/SQL Developer&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3012777222314406965?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3012777222314406965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2012/01/comparing-schemas-red-gate-schema.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3012777222314406965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3012777222314406965'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2012/01/comparing-schemas-red-gate-schema.html' title='Comparing Schemas: Red Gate Schema Compare for Oracle'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-VQDVkD7ieIk/TyA6cPqjhFI/AAAAAAAABOU/i2zR5xcpiqI/s72-c/RedGate1.png' height='72' width='72'/><thr:total>1</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5596781224882784031</id><published>2012-01-11T17:58:00.000+01:00</published><updated>2012-01-11T17:58:55.343+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='XE'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Upgrade Oracle XE from 10 to 11: A word of caution</title><content type='html'>Finally I had some time to upgrade my Oracle XE database from version 10 to version 11, something I wanted to try out but could never find the time to do it. I wanted to try it out, to see if it all worked as advertised, usually it does. This time however I ran into some problems with the export and import of the APEX applications, good thing it was just on my sandbox database.&lt;a name='more'&gt;&lt;/a&gt;The first step in the documentation (link at the bottom of this blogpost) is to log in as SYS and run the gen_inst.sql script which comes bundled with the Oracle XE11 database.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-cQO1VQYSM1g/Tw2n5pvwu3I/AAAAAAAABLg/8hiD20Mo5Io/s1600/xe10.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="162" width="320" src="http://2.bp.blogspot.com/-cQO1VQYSM1g/Tw2n5pvwu3I/AAAAAAAABLg/8hiD20Mo5Io/s320/xe10.png" /&gt;&lt;/a&gt;&lt;/div&gt;Running the gen_inst.sql script creates a number of scripts in the directory, each being an APEX application.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-K5cgmkt63zQ/Tw22exZgsXI/AAAAAAAABL4/fKcg9oNGtJk/s1600/gen_inst.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="210" width="320" src="http://3.bp.blogspot.com/-K5cgmkt63zQ/Tw22exZgsXI/AAAAAAAABL4/fKcg9oNGtJk/s320/gen_inst.png" /&gt;&lt;/a&gt;&lt;/div&gt;Now the thing to be cautious about: check the generated application install scripts.. or read the rest of this blogpost before running the gen_inst.sql script.&lt;/br&gt;I found out the hard way that they don't always work as expected. When following along with the "regular upgrade steps" (export the data, uninstall the Oracle XE10 database, install the Oracle XE11 database, import the data), the final step is to install all the applications into the newly created database. When doing so, the process stopped...&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-XluJe4WnWnE/Tw23mmyhx_I/AAAAAAAABME/rfF5CrQ40i4/s1600/apexapps_stop.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="158" width="320" src="http://3.bp.blogspot.com/-XluJe4WnWnE/Tw23mmyhx_I/AAAAAAAABME/rfF5CrQ40i4/s320/apexapps_stop.png" /&gt;&lt;/a&gt;&lt;/div&gt;As you can see in the image above, the script halted at line 19 waiting for input... entering a slash (/) resulted in the exception shown, any other input would have resulted in a similar exception. Upon examination of the application scripts, I discovered this:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-CLAZfzR5GMw/Tw24VTUiibI/AAAAAAAABMQ/vl03tqxm9r8/s1600/apexapps_stop2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="190" width="320" src="http://3.bp.blogspot.com/-CLAZfzR5GMw/Tw24VTUiibI/AAAAAAAABMQ/vl03tqxm9r8/s320/apexapps_stop2.png" /&gt;&lt;/a&gt;&lt;/div&gt;The gen_inst.sql script didn't generate the complete application script, it ran out of the DBMS_OUTPUT buffer resulting in a ORU-10027 exception (buffer overflow).&lt;/br&gt;The cause of this exception is the following code in the gen_inst.sql script&lt;pre class="brush: sql"&gt;&lt;br /&gt;set serveroutput on size 1000000&lt;br /&gt;&lt;/pre&gt;This instruction limits the output buffer to 1000000 bytes of data.Ever since Oracle 10g Release 2, it is possible to do this&lt;pre class="brush: sql"&gt;&lt;br /&gt;set serveroutput on size unlimited&lt;br /&gt;&lt;/pre&gt;Changing this line in the gen_inst.sql will generate correct application files. Note that there are multiple place in the script where this command is issued (two if I'm not mistaken).&lt;/br&gt;And just to be sure that this command also works in Oracle Express Edition 10g:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-XVpgjXUMTTo/Tw29Gm0xDSI/AAAAAAAABMc/g9EyyPqbYPI/s1600/serveroutputunlimited.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="121" width="320" src="http://3.bp.blogspot.com/-XVpgjXUMTTo/Tw29Gm0xDSI/AAAAAAAABMc/g9EyyPqbYPI/s320/serveroutputunlimited.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/br&gt;Finally, another problem I ran into, if you use the &lt;a href="http://apexlib.oracleapex.info/" target="_blank"&gt;APEXLIB&lt;/a&gt; and you created a separate schema with a name like "APEXLIB", this schema is not exported with the EXPDP command. When I exported my APEXLIB schema and imported it into the target database a lot of views, packages and triggers stayed invalid. I decided to download a new version of APEXLIB and install that in the newly created database, and all was fine.Documentation links:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.oracle.com/technetwork/database/express-edition/downloads/index.html" target="_blank"&gt;Oracle 11g Release 2 Express Edition&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://docs.oracle.com/cd/E17781_01/index.htm" target="_blank"&gt;Oracle Database Express Edition Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://docs.oracle.com/cd/E17781_01/install.112/e18803/toc.htm#XEINW136" target="_blank"&gt;Importing and Exporting Data between 10.2 XE and 11.2 XE&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12040.htm#SQPUG099" target="_blank"&gt;Serveroutput &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5596781224882784031?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5596781224882784031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2012/01/upgrade-oracle-xe-from-10-to-11-word-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5596781224882784031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5596781224882784031'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2012/01/upgrade-oracle-xe-from-10-to-11-word-of.html' title='Upgrade Oracle XE from 10 to 11: A word of caution'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-cQO1VQYSM1g/Tw2n5pvwu3I/AAAAAAAABLg/8hiD20Mo5Io/s72-c/xe10.png' height='72' width='72'/><thr:total>0</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3004927558427473893</id><published>2012-01-03T16:43:00.000+01:00</published><updated>2012-01-03T16:43:00.174+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>APEX: Friendlier exception message from AJAX call</title><content type='html'>While it is very easy to create a "Form and a Report" on a single table (or view), just follow the wizard, for end users it is not always intuitive that they should navigate to the form page to remove the record. A nicer solution is the one described by &lt;a href="http://anthonyrayner.blogspot.com" target="_blank"&gt;Anthony Rayner&lt;/a&gt; in &lt;a href="http://apex.oracle.com/pls/otn/f?p=39830:29:1674781190386244::NO:::" target="_blank"&gt;this demo page.&lt;/a&gt; This solution uses a number of -very simple- dynamic actions to include a little trashcan on the report page.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rPn_zW2HBXE/Tv3WJAfQ22I/AAAAAAAABKY/U8VydvoSDd4/s1600/trashcan.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="153" width="320" src="http://4.bp.blogspot.com/-rPn_zW2HBXE/Tv3WJAfQ22I/AAAAAAAABKY/U8VydvoSDd4/s320/trashcan.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;In this blogpost I will not describe how to create the trashcan functionality in the report, this is already done by Anthony, but about how to handle this:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-H1e7nPT-5-w/Tv3WyHB7FeI/AAAAAAAABKk/vQPQNXjkhxM/s1600/ajax.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="161" width="320" src="http://3.bp.blogspot.com/-H1e7nPT-5-w/Tv3WyHB7FeI/AAAAAAAABKk/vQPQNXjkhxM/s320/ajax.png" /&gt;&lt;/a&gt;&lt;/div&gt;You might not be scared by this message, but your users might be. Probably most of them have no idea what is meant by&lt;blockquote&gt;AJAX call returned server error ORA-02292: integrity constraint (NUIJTEN.SYS_C006303602) violated - child record found for Execute PL/SQL Code.&lt;/blockquote&gt;This exception occurs when you create the PL/SQL Code to delete the department like this&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-w6R7GggDbMY/Tv3Y9Pz3Y7I/AAAAAAAABKw/4SSOo4c5Myk/s1600/block.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="315" width="320" src="http://3.bp.blogspot.com/-w6R7GggDbMY/Tv3Y9Pz3Y7I/AAAAAAAABKw/4SSOo4c5Myk/s320/block.png" /&gt;&lt;/a&gt;&lt;/div&gt;Instead of handling the exception in the application, I prefer to have actions done in stored procedures - preferably packages. So to remove a department I will write a procedure like this:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-N0ruZSdb_8A/Tv3ZkJELOSI/AAAAAAAABK8/EEZQKuTE7Z4/s1600/proc.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="149" width="320" src="http://3.bp.blogspot.com/-N0ruZSdb_8A/Tv3ZkJELOSI/AAAAAAAABK8/EEZQKuTE7Z4/s320/proc.png" /&gt;&lt;/a&gt;&lt;/div&gt;Here is the code so you don't have to type it in, but can copy and paste it.&lt;pre class="brush: sql"&gt;&lt;br /&gt;create or replace procedure remove_dept (p_deptno IN NUMBER)&lt;br /&gt;is&lt;br /&gt;   e_constraint exception;&lt;br /&gt;   pragma exception_init (e_constraint, -2292);&lt;br /&gt;begin&lt;br /&gt;   delete from dept d&lt;br /&gt;    where d.deptno = p_deptno;&lt;br /&gt;exception&lt;br /&gt;   when e_constraint&lt;br /&gt;   then&lt;br /&gt;      htp.p ('{"error":"Department has Employees"}');&lt;br /&gt;end remove_dept;​​&lt;br /&gt;&lt;/pre&gt;The key thing in the code is the use of HTP in the exception handler. This will return a JSON object with a user friendly error message.Just replace the original delete statement with a call to the procedure:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-3yMRA6ssPEA/Tv3aCBLm0qI/AAAAAAAABLI/of4XQwSVv2U/s1600/newblock.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="108" width="320" src="http://2.bp.blogspot.com/-3yMRA6ssPEA/Tv3aCBLm0qI/AAAAAAAABLI/of4XQwSVv2U/s320/newblock.png" /&gt;&lt;/a&gt;&lt;/div&gt;and when the user tries to remove a department which still has employees, he will see a friendlier message&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/--5siGwP2eIA/Tv3aXyMexsI/AAAAAAAABLU/eqhNta2zXNM/s1600/friendly.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="134" width="320" src="http://1.bp.blogspot.com/--5siGwP2eIA/Tv3aXyMexsI/AAAAAAAABLU/eqhNta2zXNM/s320/friendly.png" /&gt;&lt;/a&gt;&lt;/div&gt;You can try out an example &lt;a href="http://apex.oracle.com/pls/apex/f?p=47888:5:1485618343735736::NO:::" target="_blank"&gt;on my demo pages.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3004927558427473893?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3004927558427473893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2012/01/apex-friendlier-exception-message-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3004927558427473893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3004927558427473893'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2012/01/apex-friendlier-exception-message-from.html' title='APEX: Friendlier exception message from AJAX call'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-rPn_zW2HBXE/Tv3WJAfQ22I/AAAAAAAABKY/U8VydvoSDd4/s72-c/trashcan.png' height='72' width='72'/><thr:total>0</thr:total><georss:featurename>Ridderstraat 39, 4901 Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7854042578834799806</id><published>2012-01-02T15:40:00.001+01:00</published><updated>2012-01-02T15:40:30.279+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Generate multiple rows</title><content type='html'>For the first post of the year, one of my New Year's resolutions is to write more posts than last year which shouldn't be too hard, I wanted to collect some different ways of generating multiple records. Sometimes I need, for whatever reason, generate multiple records. These are a few different ways of doing so.&lt;a name='more'&gt;&lt;/a&gt;This is not really "generating multiple rows", but just a simple way of having multiple lines of output. Just take a big table and limit the number of rows by using "where rownum &lt;= ":&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select 'Happy New Year' msg&lt;br /&gt;  2    from all_objects&lt;br /&gt;  3   where rownum &lt;= 10&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;MSG&lt;br /&gt;--------------&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;One of my favorite ways is to use the "Connect by level" trick:&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select 'Happy New Year' msg&lt;br /&gt;  2    from dual&lt;br /&gt;  3  connect by level &lt;= 10&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;MSG&lt;br /&gt;--------------&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;Another way is to use the MODEL clause (Oracle 10g). I still find the MODEL clause quite hard to read and understand.&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select msg&lt;br /&gt;  2    from dual&lt;br /&gt;  3    model&lt;br /&gt;  4   dimension by (0 d)&lt;br /&gt;  5   measures (cast ('m' as varchar2(30)) msg)&lt;br /&gt;  6   rules iterate (10)&lt;br /&gt;  7     (msg [iteration_number] = 'Happy New Year')&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;MSG&lt;br /&gt;------------------------------&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;And lastly the Recursive Subquery Factoring way (Oracle 11g)&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; with lots(r)&lt;br /&gt;  2  as&lt;br /&gt;  3  (select 1 r from dual&lt;br /&gt;  4    union all&lt;br /&gt;  5   select r+1 from lots&lt;br /&gt;  6   where r &lt; 10&lt;br /&gt;  7  )&lt;br /&gt;  8  select 'Happy New Year' msg&lt;br /&gt;  9    from lots&lt;br /&gt; 10  /&lt;br /&gt;&lt;br /&gt;MSG&lt;br /&gt;--------------&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;Happy New Year&lt;br /&gt;&lt;br /&gt;10 rows selected.&lt;br /&gt;&lt;/pre&gt;I know there are more ways of generating multiple rows as output, this is not meant as a complete list of all possibilities. This post was meant to sent &lt;a href="http://en.wikipedia.org/wiki/Subliminal_message" target="_blank"&gt;a subliminal message&lt;/a&gt; to wish you a Happy New Year!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7854042578834799806?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7854042578834799806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2012/01/generate-multiple-rows.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7854042578834799806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7854042578834799806'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2012/01/generate-multiple-rows.html' title='Generate multiple rows'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>2</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5765081095877014275</id><published>2011-12-11T11:49:00.001+01:00</published><updated>2011-12-12T12:19:33.662+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='2011'/><category scheme='http://www.blogger.com/atom/ns#' term='UKOUG'/><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><title type='text'>Looking back at UKOUG 2011</title><content type='html'>Now that the UKOUG annual conference in Birmingham is over, it's time to write my thoughts down. As this was the second time that I attended the UKOUG conference, I already knew that it is a big conference. Lots of great speakers and a very good agenda. On the agenda were very interesting session, sometimes making it very hard to choose which session to go to. Guess you can't complain about that. &lt;a name='more'&gt;&lt;/a&gt;On the Sunday before the actual conference starts, &lt;a href="http://oaktable.net/" target="_blank"&gt;the OakTable&lt;/a&gt; organized a special day. Originally I didn't plan to attending this day, as I was flying in in the afternoon. Turned out, I was on time to attend &lt;a href="http://www.oracledba.co.uk/"  target="_blank"&gt;Connor McDonald's&lt;/a&gt; session. Lucky me, Connor is an awesome presenter, so I decided to stay for the rest of the day. Very enjoyable, they even had OakTable beer before the final OakTable panel session.&lt;/br&gt;In the evening there was the Ace dinner, in a nearby Thai restaurant. Good company, good food. A little nerdy perhaps, but as it turned out there were 42 Ace's and Ace Directors attending.&lt;/br&gt;On Monday I attended some really good sessions, like Kyle Hailey on SQL Tuning, Bryn Llewellyn on Using the PL/SQL Hierarchical Performance Profiler, and Connor McDonald on Partitioning 101.&lt;/br&gt;Late in the evening Cary Millsap did a keynote session: Learning about Life through Business and Software. Only one word for this session: Wow! The things he talked about are still going around in my mind.&lt;/br&gt;Tuesday morning, right after Roel Hartman's session Done in 60 seconds,  was my own session Who's afraid of Analytic Functions? - for which I was quite nervous. Got some nice feedback on the session, so was quite pleased with it. Of course I had to go and see Carl Dudley's session on Analysing Your Data with Analytic Functions - check out the competition, so to speak. Michael Salt's session on Indexing: It's All In The Index was also very interesting.&lt;/br&gt;Wednesday was time to head back home, so packed up my bags and went to see Jonathan Lewis' session on Redo. Can you keep an audience captivated for an hour talking about Redo? Jonathan Lewis can. Sadly this was my last session.&lt;/br&gt;Because I arrived early at the Birmingham International Airport, I could get an earlier -originally delayed- flight back to The Netherlands. Now I can look back at a wonderful conference....&lt;/br&gt;Oh,... did I mention I received the Inspiring Presentation Award for my session I did last year?Update: &lt;a href="http://www.liberidu.com/blog/" target="_blank"&gt;Marco&lt;/a&gt; told me that &lt;a target="_blank" href="http://connormcdonald.wordpress.com/"&gt;Connor McDonald &lt;/a&gt;has a new Blog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5765081095877014275?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5765081095877014275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/12/looking-back-at-ukoug-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5765081095877014275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5765081095877014275'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/12/looking-back-at-ukoug-2011.html' title='Looking back at UKOUG 2011'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7549616745849305102</id><published>2011-11-29T10:54:00.001+01:00</published><updated>2011-11-29T11:55:11.263+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>APEX: Make a report row clickable in Report</title><content type='html'>When you create a "Report with Form", there will be an icon in the report which allows you to navigate to &amp;nbsp;the form page. Only when the user clicks the icon this navigation will take place. For the current project, this was not what they wanted. They wanted to click on the row instead of just the icon. This can be simply implemented using jQuery.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;For this example we are going to use a "Report with Form" and modify it as described below.&lt;br /&gt;All of our pages in the application have a page alias, the Report page alias is "EMP001" and the Form page has an alias "EMP002".&lt;br /&gt;The default behaviour will appear in the page like the below screenshot, the cursor will turn into a small hand only when you move your mouse over the icon.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ibFaOm2n1K8/TtSwS6KSqeI/AAAAAAAABJo/Lc-D4VOvoFw/s1600/Edit1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="59" src="http://3.bp.blogspot.com/-ibFaOm2n1K8/TtSwS6KSqeI/AAAAAAAABJo/Lc-D4VOvoFw/s320/Edit1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;But we want to achieve the following: moving the cursor over the whole row changes the cursor to a small hand &amp;nbsp;indicating that the whole row is clickable. And not only that, also that the whole row is in fact clickable.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-srpCkZFkhlU/TtSwgkXX6VI/AAAAAAAABJw/hDzsMMUInTA/s1600/edit2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="63" src="http://3.bp.blogspot.com/-srpCkZFkhlU/TtSwgkXX6VI/AAAAAAAABJw/hDzsMMUInTA/s320/edit2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;To achieve this we would need to "move" (or copy) the anchor to row-level.&lt;br /&gt;&lt;br /&gt;When you look at the generated HTML code, it will look something similar to this:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-BAw3ySDo8Yc/TtSvXI4VJUI/AAAAAAAABJg/ZtVVh6PWEL0/s1600/html.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="78" src="http://2.bp.blogspot.com/-BAw3ySDo8Yc/TtSvXI4VJUI/AAAAAAAABJg/ZtVVh6PWEL0/s400/html.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;The highlighted line in the picture is the icon which is shown in the Report page (alias: EMP001). As you can see in the image, the anchor (the "a" tag) has an href attribute which contains all the information it needs to navigate to the Form page (alias: EMP002). Even the page alias is in there, and this is what we will use to select the correct href.&lt;br /&gt;Because the page alias is in the anchor, we can use the jQuery selector to get the appropriate element&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$('a[href*="EMP002"]')&lt;/pre&gt;&lt;br /&gt;This will result in an array of anchor elements which we want to manipulate. For each of the elements in the array we want to retrieve the href attribute. The href attribute, we will keep in a local variable named "lnk". Now the code will look like:&lt;br /&gt;&lt;pre class="brush: js"&gt;$('a[href*="EMP002"]').each(function(index) {&amp;nbsp;&lt;br /&gt;    lnk = $(this).attr('href');&lt;br /&gt;&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This variable with the href attribute is going to be place at row level as a "data-href". From the current element we need to move up to the row level (tr) and add the attribute.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;   $(this).parent()&lt;br /&gt;          .parent('tr')&lt;br /&gt;    .attr('data-href', lnk)&lt;/pre&gt;&lt;br /&gt;To make it clickable, we also need to add a click event to the row. This is where jQuery really shows its power, we can "chain" the click event to our selector. The code will grow slightly to the following:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt; $(this).parent()&lt;br /&gt;          .parent('tr')&lt;br /&gt;    .attr('data-href', lnk)&lt;br /&gt;    .click(function(){&lt;br /&gt;      window.location=$(this).attr('data-href');&lt;br /&gt;    })&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To give the user the feedback that the row is clickable, we need to change the cursor to a little hand. Again we chain the mouseover event to the selector&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;$(this).parent()&lt;br /&gt;          .parent('tr')&lt;br /&gt;    .attr('data-href', lnk)&lt;br /&gt;    .click(function(){&lt;br /&gt;      window.location=$(this).attr('data-href');&lt;br /&gt;    })&lt;br /&gt;    .mouseover(function(){&lt;br /&gt;      $(this).css('cursor', 'pointer');&lt;br /&gt;    })&lt;/pre&gt;&lt;br /&gt;And because we also want to change the cursor back to default when the user move the cursor away from the rows in the report, the mouseleave event is also required&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: js"&gt;   $(this).parent()&lt;br /&gt;          .parent('tr')&lt;br /&gt;    .attr('data-href', lnk)&lt;br /&gt;    .click(function(){&lt;br /&gt;      window.location=$(this).attr('data-href');&lt;br /&gt;    })&lt;br /&gt;    .mouseover(function(){&lt;br /&gt;      $(this).css('cursor', 'pointer');&lt;br /&gt;    })&lt;br /&gt;    .mouseleave(function(){&lt;br /&gt;      $(this).css('cursor', 'default');&lt;br /&gt;    })&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;Now that the jQuery code is complete, we can add this to the page&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ACLQjet9Eic/TtS1e3-P4fI/AAAAAAAABKE/uXS1_poUrVk/s1600/Pagelevel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-ACLQjet9Eic/TtS1e3-P4fI/AAAAAAAABKE/uXS1_poUrVk/s1600/Pagelevel.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Double click at page level to open the page properties, and paste the jQuery code in the section labelled "Execute when Page Loads" and we are all set.&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-LV9Z0lpDgvA/TtS1ejykJpI/AAAAAAAABKA/jZ2ZEoHsHm4/s1600/jQueryPage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="290" src="http://1.bp.blogspot.com/-LV9Z0lpDgvA/TtS1ejykJpI/AAAAAAAABKA/jZ2ZEoHsHm4/s400/jQueryPage.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;When you run the page you will notice that the whole row is clickable.&lt;br /&gt;Inspecting the HTML after we add all the jQuery code will show that there is an anchor at the row level now, just what we wanted.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-10fcgIQOTBQ/TtSx1pghx5I/AAAAAAAABJ4/jF9CEKBSvWk/s1600/html2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="43" src="http://2.bp.blogspot.com/-10fcgIQOTBQ/TtSx1pghx5I/AAAAAAAABJ4/jF9CEKBSvWk/s400/html2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;To make copy the code for your own use, the completed jQuery code will look like this&lt;br /&gt;&lt;pre class="brush: js"&gt;$('a[href*="EMP002"]').each(function(index) {&lt;br /&gt;   lnk = $(this).attr('href');&lt;br /&gt;   $(this).parent()&lt;br /&gt;          .parent('tr')&lt;br /&gt;    .attr('data-href', lnk)&lt;br /&gt;    .click(function(){&lt;br /&gt;      window.location=$(this).attr('data-href');&lt;br /&gt;    })&lt;br /&gt;    .mouseover(function(){&lt;br /&gt;      $(this).css('cursor', 'pointer');&lt;br /&gt;    })&lt;br /&gt;    .mouseleave(function(){&lt;br /&gt;      $(this).css('cursor', 'default');&lt;br /&gt;    })&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;And to see this in action you can find &lt;a href="http://apex.oracle.com/pls/otn/f?p=47888:2:3653991300354527:::::" target="_blank"&gt;a demo on apex.oracle.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7549616745849305102?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7549616745849305102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/11/apex-make-report-row-clickable-in.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7549616745849305102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7549616745849305102'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/11/apex-make-report-row-clickable-in.html' title='APEX: Make a report row clickable in Report'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-ibFaOm2n1K8/TtSwS6KSqeI/AAAAAAAABJo/Lc-D4VOvoFw/s72-c/Edit1.png' height='72' width='72'/><thr:total>6</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7341793161499882201</id><published>2011-11-07T17:22:00.002+01:00</published><updated>2011-11-08T10:35:08.502+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>Upgrade to APEX 4.1: Invalid Login Credentials</title><content type='html'>Always a good idea to upgrade on a Friday.. not... In the past I did numerous upgrades to the latest APEX release, and never really encountered big problems. It was always very smooth, kudos to the APEX-team. If something went wrong it was mostly my own fault, which I will admit now. Because of the history of going very smoothly, I dare to do an upgrade on Friday morning.And it did go smoothly, the upgrade to APEX 4.1 ... until...&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Until I started up the application, then I got this "Invalid Login Credentials:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-zW8X4HnT588/Trf91eO5HEI/AAAAAAAABGg/fSV2id5IIbY/s1600/1-InvalidLogin.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="121" src="http://4.bp.blogspot.com/-zW8X4HnT588/Trf91eO5HEI/AAAAAAAABGg/fSV2id5IIbY/s320/1-InvalidLogin.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;This had me scratching my head for a while.... For this application we are using a Custom Authentication and it turns out that we ran into bug number: 13045147. At the end of the day, I found a way that worked for me to get around this bug.&lt;br /&gt;Before you start this, I strongly advise you to backup your application and everything else your fond of. This worked for me, hopefully it works for you.&lt;br /&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="color: red;"&gt;Wait!.&lt;/span&gt;&lt;/b&gt;... There is a Patch for this bug:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-SObRalhgiiU/Trj2k317DVI/AAAAAAAABJQ/IJQcU4Cklug/s1600/MOS.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="29" src="http://1.bp.blogspot.com/-SObRalhgiiU/Trj2k317DVI/AAAAAAAABJQ/IJQcU4Cklug/s320/MOS.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;... with a release date of November 7th,... coincidence?... ;)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 1. Copy your original Login Page (very handy to keep around in case you need to copy regions over)&lt;/b&gt;&lt;br /&gt;This can easily be done if you navigate to your Login page, and choose Create &amp;gt; New Page as Copy&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-sz8teDSXPxQ/Trf_QVFV0XI/AAAAAAAABGs/r6OdN0oXx3s/s1600/2-copyloginpage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="230" src="http://4.bp.blogspot.com/-sz8teDSXPxQ/Trf_QVFV0XI/AAAAAAAABGs/r6OdN0oXx3s/s320/2-copyloginpage.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Copy it to you own application&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-F_UpXkN1jlg/Trf_kDs01wI/AAAAAAAABG4/Xk1mitaVsTc/s1600/3-Copy%2BPage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://2.bp.blogspot.com/-F_UpXkN1jlg/Trf_kDs01wI/AAAAAAAABG4/Xk1mitaVsTc/s320/3-Copy%2BPage.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Leave all the defaults in place (or at least that is what I did)&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-YIbltUBjKI0/Trf_xPDSYaI/AAAAAAAABHE/6FZzarGCsyU/s1600/4CopyPage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="90" src="http://4.bp.blogspot.com/-YIbltUBjKI0/Trf_xPDSYaI/AAAAAAAABHE/6FZzarGCsyU/s320/4CopyPage.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-bkhJla9uR1Y/Trf_1z9kCPI/AAAAAAAABHQ/noH72uYbu9k/s1600/5-Copy%2BPage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="97" src="http://1.bp.blogspot.com/-bkhJla9uR1Y/Trf_1z9kCPI/AAAAAAAABHQ/noH72uYbu9k/s320/5-Copy%2BPage.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Step 2: Now you can remove your Login page.&lt;/b&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-bvCS46tR9Bk/TrgAFnZ1zTI/AAAAAAAABHg/9i_XAqFfXBk/s1600/6-DeleteLoginpage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-bvCS46tR9Bk/TrgAFnZ1zTI/AAAAAAAABHg/9i_XAqFfXBk/s320/6-DeleteLoginpage.png" width="226" /&gt;&lt;/a&gt;&lt;/div&gt;When you remove your Login page, you will get a warning:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-_L56m2hilNY/TrgANW4n1fI/AAAAAAAABHo/FXw6Uzn4Zho/s1600/7-Confirm%2BPage%2BDelete.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="81" src="http://3.bp.blogspot.com/-_L56m2hilNY/TrgANW4n1fI/AAAAAAAABHo/FXw6Uzn4Zho/s320/7-Confirm%2BPage%2BDelete.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Choose "Permanently Delete Page".... did I already tell you to make a backup of your application before you start following these steps? This might be your last chance to do so.&lt;br /&gt;&lt;b&gt;Step 3: New authentication and new Login Page&lt;/b&gt;&lt;br /&gt;Navigate to Shared Components&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-PGHOmjDXl2w/TrgA8C6cYFI/AAAAAAAABH0/IM36kcsyaZY/s1600/8-SharedComp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="197" src="http://3.bp.blogspot.com/-PGHOmjDXl2w/TrgA8C6cYFI/AAAAAAAABH0/IM36kcsyaZY/s320/8-SharedComp.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;and choose "Authentication Schemes"&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-wExTXzZ0UJ4/TrgBHDyPnjI/AAAAAAAABIA/h8rHuqC0_Vg/s1600/9-AuthScheme.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="222" src="http://2.bp.blogspot.com/-wExTXzZ0UJ4/TrgBHDyPnjI/AAAAAAAABIA/h8rHuqC0_Vg/s320/9-AuthScheme.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Create a new Authentication Scheme, based on a pre-configured scheme from the gallery&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-FEpbtfqB3-8/TrgBTa5D7xI/AAAAAAAABIM/E3mvkhG-x4o/s1600/10-Create%2BAuthentication%2BScheme%2B-%2BMethod.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="74" src="http://3.bp.blogspot.com/-FEpbtfqB3-8/TrgBTa5D7xI/AAAAAAAABIM/E3mvkhG-x4o/s320/10-Create%2BAuthentication%2BScheme%2B-%2BMethod.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Choose Custom from the select list&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-7dWBnH6L8Fg/TrgBYohGbhI/AAAAAAAABIY/Gm8cbAnZQDg/s1600/11-Edit%2BAuthentication%2BScheme.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="208" src="http://3.bp.blogspot.com/-7dWBnH6L8Fg/TrgBYohGbhI/AAAAAAAABIY/Gm8cbAnZQDg/s320/11-Edit%2BAuthentication%2BScheme.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Then you will see two additional regions on the page, where you specify the functions and procedures you want to use. Currently this application only uses a custom (table based) authentication, so I fill in the packaged function which I use&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-RZyXOZPlZ3A/TrgByApIRWI/AAAAAAAABIk/nJKXe7QV_tc/s1600/12-Edit%2BAuthentication%2BScheme.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="202" src="http://3.bp.blogspot.com/-RZyXOZPlZ3A/TrgByApIRWI/AAAAAAAABIk/nJKXe7QV_tc/s320/12-Edit%2BAuthentication%2BScheme.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;The signature of the function should look like this&lt;br /&gt;&lt;pre style="brush: sql;"&gt;   function validate (p_username in varchar2&lt;br /&gt;                     ,p_password in varchar2&lt;br /&gt;                     )&lt;br /&gt;      return boolean;&lt;br /&gt;&lt;/pre&gt;When you complete this step, not only is the new Authentication Scheme created, but it is also the active one. And as a bonus, you will get a new Login page as well.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-NwaNKauHpYo/TrgDzZBjwOI/AAAAAAAABIw/3i6IHcH4pIw/s1600/13-Authentication%2BSchemes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="36" src="http://3.bp.blogspot.com/-NwaNKauHpYo/TrgDzZBjwOI/AAAAAAAABIw/3i6IHcH4pIw/s320/13-Authentication%2BSchemes.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;You can see it is the active Authentication Scheme, as it is labelled as "Current" in the Authentication Overview&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-uGdiIA1SP2U/TrgEBRuMQrI/AAAAAAAABI8/Q1RDKkwh__w/s1600/14-Authentication%2BSchemes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="133" src="http://4.bp.blogspot.com/-uGdiIA1SP2U/TrgEBRuMQrI/AAAAAAAABI8/Q1RDKkwh__w/s320/14-Authentication%2BSchemes.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;After all these steps the authentication works as before, just restore (copy) the extra regions from the original Login page to the new Login page and we're all set.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-twSfjuIAO_E/TrgEURQVYDI/AAAAAAAABJI/Pjv-6H4t__I/s1600/15-navbar.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="37" src="http://1.bp.blogspot.com/-twSfjuIAO_E/TrgEURQVYDI/AAAAAAAABJI/Pjv-6H4t__I/s320/15-navbar.png" width="244" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7341793161499882201?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7341793161499882201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/11/upgrade-to-apex-41-invalid-login.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7341793161499882201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7341793161499882201'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/11/upgrade-to-apex-41-invalid-login.html' title='Upgrade to APEX 4.1: Invalid Login Credentials'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-zW8X4HnT588/Trf91eO5HEI/AAAAAAAABGg/fSV2id5IIbY/s72-c/1-InvalidLogin.png' height='72' width='72'/><thr:total>2</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-378597086492069755</id><published>2011-10-26T12:08:00.001+02:00</published><updated>2011-10-26T12:10:00.960+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>Using jQuery to determine the checked items</title><content type='html'>In one of my APEX pages, I needed to know in javascript which checkboxes are checked. The checked values are placed in an item as a semi-colon (;) delimited list. To be able to do this I created a Dynamic Action which was quite easy.. what wasn't that easy (at least for me, and that's why I want to keep it here so the next time I need this) was to write the javascript expression to get the values.&lt;a name='more'&gt;&lt;/a&gt;jQuery is simply amazing. Using a "simple" expression it is "easy" to get exactly what was needed. &lt;pre class="brush: js"&gt;&lt;br /&gt;$('input:checkbox:checked').map(function() {&lt;br /&gt;  return $(this).val();&lt;br /&gt;}).get().join(';');&lt;br /&gt;&lt;/pre&gt;And the complete Dynamic Action to see the settings that I used:&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-_3u3QUOGSqw/Tqfayo15ItI/AAAAAAAABEI/9xjp1ZD-9EI/s1600/DynamicAction.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="253" width="320" src="http://1.bp.blogspot.com/-_3u3QUOGSqw/Tqfayo15ItI/AAAAAAAABEI/9xjp1ZD-9EI/s320/DynamicAction.png" /&gt;&lt;/a&gt;&lt;/div&gt;Probably there is an even easier way to get the same results, I'd be more than interested to learn that...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-378597086492069755?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/378597086492069755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/10/using-jquery-to-determine-checked-items.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/378597086492069755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/378597086492069755'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/10/using-jquery-to-determine-checked-items.html' title='Using jQuery to determine the checked items'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-_3u3QUOGSqw/Tqfayo15ItI/AAAAAAAABEI/9xjp1ZD-9EI/s72-c/DynamicAction.png' height='72' width='72'/><thr:total>2</thr:total><georss:featurename>Oosterhout, The Netherlands</georss:featurename><georss:point>51.6410202 4.8616901</georss:point><georss:box>51.6016042 4.7827261 51.6804362 4.9406541</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3466246032078610398</id><published>2011-10-04T19:11:00.000+02:00</published><updated>2011-10-04T19:11:19.077+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='EBR'/><category scheme='http://www.blogger.com/atom/ns#' term='OOW2011'/><title type='text'>Oracle Open World 2011: Oracle Database 11g Features for Developers by Connor McDonald</title><content type='html'>Yesterday I had the pleasure of attending a session by Connor McDonald. I heard a lot about him, his presentation style, and I&lt;a target+"_blank" href="http://technology.amis.nl/blog/578/book-review-mastering-oracle-plsql-practical-solutions"&gt; even did a book review back in 2005. &lt;/a&gt;Everybody I talk to who attended a session by Connor - ever - is always very positive on his presentation style. And it is very impressive, humor, technical knowledge, all the ingredients are there to keep you focused on the content. The opening slide, he put on as you walked in the room invited you to move forward as the presentation has code sample in a fontsize which he showed on the slide. The room filled up, and it was a large room. There was another benefit of being in the front of the room, apart from being able to read the slides, he handed out chocolates - starting from the front. Talking to Connor later in the evening, he told me it takes months to prepare for a presentation, and it really shows. If you get a chance to see Connor do a presentation, attend it and make sure to arrive early!Part of the presentation was on Edition Based Redefinition, and because I presented on that subject before I believed there was a flaw in his presentation. But there was not, I was mistaken. The rest of this blogpost shows you where I was mistaken, and Connor was absolutely right.&lt;a name='more'&gt;&lt;/a&gt;When creating an editioning view, I believed it was "required" to name the columns in your view instead of using the wildcard "*" to select the columns. Shielding the editioning view from table alterations. So let's start with an edition enabled user to setup our table.&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; conn alex/alex@xe11&lt;br /&gt;Connected.&lt;br /&gt;SQL&gt; alter user alex enable editions&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;User altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;---------------------------------------------------------------------&lt;br /&gt;Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production&lt;br /&gt;PL/SQL Release 11.2.0.2.0 - Production&lt;br /&gt;CORE    11.2.0.2.0      Production&lt;br /&gt;TNS for 32-bit Windows: Version 11.2.0.2.0 - Production&lt;br /&gt;NLSRTL Version 11.2.0.2.0 - Production&lt;br /&gt;&lt;br /&gt;5 rows selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table "_EMP"&lt;br /&gt;  2  (empno number&lt;br /&gt;  3  ,ename varchar2(20)&lt;br /&gt;  4  )&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;As you can see in the above code snippet, I run this test on Oracle Database 11g Express Edition. The tablename I picked up from Connor's session. Naming your table something freakish will (hopefully) stop developers from using the table directly in their code. I like that naming convention. Let's continue with two editions&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create edition r1 as child of ora$base&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; create edition r2 as child of r1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition created.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;In each of the editions we will create an editioning view using the wildcard "*" to select the columns&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter session set edition = r1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  editioning view emp&lt;br /&gt;  3  as&lt;br /&gt;  4  select *&lt;br /&gt;  5    from "_EMP"&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;View created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; alter session set edition = r2&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  editioning view emp&lt;br /&gt;  3  as&lt;br /&gt;  4  select *&lt;br /&gt;  5    from "_EMP"&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;View created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;I believed, wrongly, that the wildcard "*" would be dynamic, meaning that if the table definition would change both editioning view would include the new column as well. This is not the case.The editioning views don't even get invalidated...&lt;pre class="brush: sql"&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; alter session set edition = ora$base&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table "_EMP"&lt;br /&gt;  2  add something varchar2(10)&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select owner&lt;br /&gt;  2       , object_name&lt;br /&gt;  3      , status&lt;br /&gt;  4    from all_objects_ae&lt;br /&gt;  5   where object_name = 'EMP'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OWNER                          OBJECT_NAME                    STATUS&lt;br /&gt;------------------------------ ------------------------------ -------&lt;br /&gt;ALEX                           EMP                            VALID&lt;br /&gt;ALEX                           EMP                            VALID&lt;br /&gt;&lt;br /&gt;2 rows selected.&lt;br /&gt;&lt;/pre&gt;Even though they don't get invalidated, the definition of the editioning views surely must be changed then.... &lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; alter session set edition = r1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; desc emp&lt;br /&gt; Name                                                              Null?    Type&lt;br /&gt; ----------------------------------------------------------------- -------- ----------------------&lt;br /&gt;&lt;br /&gt; EMPNO                                                                      NUMBER&lt;br /&gt; ENAME                                                                      VARCHAR2(20)&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; alter session set edition =  r2&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; desc emp&lt;br /&gt; Name                                                              Null?    Type&lt;br /&gt; ----------------------------------------------------------------- -------- ----------------------&lt;br /&gt;&lt;br /&gt; EMPNO                                                                      NUMBER&lt;br /&gt; ENAME                                                                      VARCHAR2(20)&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;No. The definition includes the columns which existed before we altered the table.What is stored in the metadata, you may wonder. Let's go and find out:&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter session set edition = r1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; set long 50000&lt;br /&gt;SQL&gt; select dbms_metadata.get_ddl ('VIEW'&lt;br /&gt;  2                               ,'EMP'&lt;br /&gt;  3                                                      )&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;DBMS_METADATA.GET_DDL('VIEW','EMP')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;  CREATE OR REPLACE FORCE EDITIONING VIEW "ALEX"."EMP" ("EMPNO", "ENAME") AS&lt;br /&gt;  select "EMPNO","ENAME"&lt;br /&gt;  from "_EMP"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; alter session set edition =  r2&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select dbms_metadata.get_ddl ('VIEW'&lt;br /&gt;  2                               ,'EMP'&lt;br /&gt;  3                                                      )&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;DBMS_METADATA.GET_DDL('VIEW','EMP')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;  CREATE OR REPLACE FORCE EDITIONING VIEW "ALEX"."EMP" ("EMPNO", "ENAME") AS&lt;br /&gt;  select "EMPNO","ENAME"&lt;br /&gt;  from "_EMP"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;/pre&gt;As you can see in the output above, the actual column names are in the metadata, shielding changes to the editioning views when the table definition changes.Connor was absolutely right about the mechanics of Edition Based Redefinition. Does this mean you should use the wildcard "*"in your editioning views? I believe not, I believe you should explicitly name the columns that you want to include in a specific edition. This way it is more clear to the developers, and DBA alike. Rerunning the scripts might all of sudden include columns in an edition which weren't suppose to be there. When you explicitly name your columns, you won't have this problem.Just the last bit, cleaning up my environment:&lt;pre class="brush: sql"&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; alter session set edition = ora$base&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; drop edition r2 cascade&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt; drop edition r1 cascade&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt; drop table "_EMP" purge&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3466246032078610398?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3466246032078610398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/10/oracle-open-world-2011-oracle-database.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3466246032078610398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3466246032078610398'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/10/oracle-open-world-2011-oracle-database.html' title='Oracle Open World 2011: Oracle Database 11g Features for Developers by Connor McDonald'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2422379715586040880</id><published>2011-10-02T21:15:00.000+02:00</published><updated>2011-10-02T21:15:28.950+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OOW2011'/><title type='text'>Oracle Open World 2011; Unstructured Data and Multimedia SIG</title><content type='html'>Oracle Open World is underway. My first session, which I didn't plan, was on Unstructured Data. There is a lot of unstructured data going around, as opposed to relational data, and it will only increase in the future. The number of images and videos will only increase in the future.Especially for this, Marcelle Kratochvil is putting together an Oracle Unstructured Data with Multimedia SIG, just to spread the word on unstructured data and multimedia. She even put up a website (to be moved in the future) with white papers, preseentations, and links to other useful information. This site can be found at &lt;a href="https://sites.google.com/site/ommuds/home" target="_blank"&gt;https://sites.google.com/site/ommuds/home.&lt;/a&gt; Head over there and check it out.She also showed some very nice examples of customers using multimedia. Unfortunately I had to leave at this time. Even though I didn't plan on attending this session, it was very interesting - too bad I had to leave for a different session, but I will surely be investigating more on unstructured data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2422379715586040880?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2422379715586040880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/10/oracle-open-world-2011-unstructured.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2422379715586040880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2422379715586040880'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/10/oracle-open-world-2011-unstructured.html' title='Oracle Open World 2011; Unstructured Data and Multimedia SIG'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1239489667012013588</id><published>2011-10-02T01:00:00.000+02:00</published><updated>2011-10-02T01:06:04.220+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OOW2011'/><title type='text'>Where am I, what am I doing.. Oracle Open World 2011</title><content type='html'>This post is more for me than it is for you, at least I will have a place to quickly check where am I supposed to be going next.The rest of this post is basically just my schedule for the upcoming week in San Francisco.Really looking forward to all the sessions, meeting old friends and making new ones. So if you see me walking around, please come up and say hello.&lt;/br&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;iframe width='900' height='600' frameborder='0' src='https://docs.google.com/spreadsheet/pub?hl=nl&amp;hl=nl&amp;key=0AhjfPoaDRiRVdFp1UjByU041UHR6bVdGejZVaC1KakE&amp;output=html&amp;widget=true'&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-1239489667012013588?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/1239489667012013588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/10/where-am-i-what-am-i-doing-oracle-open.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1239489667012013588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1239489667012013588'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/10/where-am-i-what-am-i-doing-oracle-open.html' title='Where am I, what am I doing.. Oracle Open World 2011'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2570316893209980466</id><published>2011-09-09T14:41:00.000+02:00</published><updated>2011-09-09T14:43:00.311+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 9i'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Quick: How long is this string?</title><content type='html'>Riddle me this: how long is the following string:&lt;pre class="brush: sql"&gt;&lt;br /&gt;to_char (10, '0999')&lt;br /&gt;&lt;/pre&gt;&lt;a name='more'&gt;&lt;/a&gt;Did you answer four? Then, sorry to say, you are wrong. The correct answer is five.&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select '['||to_char (10, '0999') ||']'&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;'['||TO&lt;br /&gt;-------&lt;br /&gt;[ 0010]&lt;br /&gt;&lt;/pre&gt;As you can see, there is a space at the beginning of the string. This space is reserved for the sign (either a plus  "+" or a minus  "-" ).If you don't want this in your output, you can do something like this&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select '['||to_char (10, 'fm0999') ||']'&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;'['||TO&lt;br /&gt;-------&lt;br /&gt;[0010]&lt;br /&gt;&lt;/pre&gt;And as you can see, the space is no longer there.What if you do want the sign to appear in the output? Use this format mask&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select '['||to_char (10, 's0999') ||']'&lt;br /&gt;  2    from dual&lt;br /&gt;  3 /&lt;br /&gt;&lt;br /&gt;'['||TO&lt;br /&gt;-------&lt;br /&gt;[+0010]&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;Link&lt;/h3&gt;&lt;a href="http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/sql_elements004.htm#BABIGFBA" target="_blank"&gt;Oracle Documentation on Format Models&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2570316893209980466?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2570316893209980466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/09/quick-how-long-is-this-string.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2570316893209980466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2570316893209980466'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/09/quick-how-long-is-this-string.html' title='Quick: How long is this string?'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5582403922226609109</id><published>2011-09-08T21:22:00.000+02:00</published><updated>2011-09-08T21:31:14.633+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OOW2011'/><title type='text'>Oracle Open World / Java One Preview bij AMIS</title><content type='html'>Op maandag 19 september zal er bij AMIS een Oracle Open World Preview sessie worden gehouden. Een groot deel van de Nederlanders die het dit jaar gelukt is om aanwezig te zijn als spreker op Oracle Open World zullen hun presentaties geven in Nieuwegein. AMIS-ers Lucas Jellema, Alex Nuijten, Peter Ebell en Aino Andriessen zullen in drie parallel sessies presentaties verzorgen. Daarnaast zullen andere Nederlandse presentatoren - zie hieronder de lijst met sprekers - presentaties verzorgen.Heb je dit jaar niet de kans om in San Fransisco aanwezig te zijn, meld je dan aan voor deze preview sessie Oracle Open World 2011!&lt;a name='more'&gt;&lt;/a&gt;&lt;table border ="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Zaal 1&lt;/th&gt;&lt;th&gt;Zaal 2&lt;/th&gt;&lt;th&gt;Zaal 3&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;17:00 - 18:00&lt;/td&gt;&lt;td&gt;Lucas Jellema&lt;/td&gt;&lt;td&gt;Aino Andriessen&lt;/td&gt;&lt;td&gt;Alex Nuijten&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;18:00 - 19:00&lt;/td&gt;&lt;td&gt;Diner&lt;/td&gt;&lt;td&gt;Diner&lt;/td&gt;&lt;td&gt;Diner&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;19:00 - 20:00&lt;/td&gt;&lt;td&gt;Jacco Landlust&lt;/td&gt;&lt;td&gt;Kees Jan Koster&lt;/td&gt;&lt;td&gt;Peter Ebell&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;20:00 - 21:00&lt;/td&gt;&lt;td&gt;Lonneke Dikmans&lt;/td&gt;&lt;td&gt;Kees Jan Koster/ Jeroen Borgers&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h3&gt;Lucas Jellema - Instant Agility in Oracle Fusion Middleware Through Design Time @ Runtime&lt;/h3&gt;This session will discuss quality aspects of an ADF Fusion application. It will demonstrate how to address, manage and measure quality and will show what tools can be used to leverage the development process to create high quality ADF Fusion applications.&lt;h3&gt;Jacco Landlust - Deployment Patterns for Oracle Fusion Middleware 11g&lt;/h3&gt;Oracle Fusion Middleware 11g is a comprehensive software suite that can be installed in a wide range of different topologies. In this session, an Oracle ACE Director (Simon Haslam) and an Oracle ACE (Jacco H. Landlust) combine their real-world experience in a range of organizations (including supermarkets, government departments, and banks) to explain the decisions you have to make in designing a middleware infrastructure.The presentation covers Oracle WebLogic Server, supporting infrastructure such as Oracle Internet Directory, and layered products such as Oracle SOA Suite. Topics include domain planning, clustering options, handling virtualization, patching strategy, and promoting software from test to production.&lt;h3&gt;Lonneke Dikmans - Approach to SOA: Making This a Successful Endeavor for the Whole Organization&lt;/h3&gt;This session starts by explaining key features of SOA. There are several reasons to start with SOA: it can be a strategic choice, it can be started from an IT perspective, or the endeavor can be started from a specific department or business issue. To make the SOA effort successful, it is important to decide where to start. Selecting the right tooling, training, and projects is an important success factor. This session discusses Oracle SOA Suite and Oracle Unified Business Process Management Suite and explains how the suites fit into the roadmap to SOA. At the end of this session, you will know what SOA means, when it is best used, and how you can get there.&lt;h3&gt;Aino Andriessen - Spend some quality time on you ADF Application&lt;/h3&gt;A key factor in succesfull (ADF Fusion) application development is good quality management. And although everybody agrees on that, many projects still struggle with the definition of quality let alone with the implementation, measurement and enforcement of a quality process. Hight quality improves the system's stability and maintainability and allows for better and faster development and is thus beneficial both to the owner as wel as the developer.&lt;h3&gt;Kees Jan Koster - Cache Fundamentals, Dangers, and Tuning&lt;/h3&gt;This presentation is a tour of the surprisingly large number of caches in a typical Java application and the systems it is part of. You'll take home a practical approach to examining the characteristics of a cache and will be able to compare caching solutions.&lt;h3&gt;Kees Jan Koster en Jeroen Borgers	- Java Tuning Puzzlers&lt;/h3&gt;This session looks at some strange and bizarre exceptions, error messages, and performance measurements. It discusses each of them and tries to resolve them. You are invited to chip in, offer solutions, ask questions, and learn about some of the more obscure corners of Java.&lt;h3&gt;Peter Ebell - Bridging the gap between SOA and the Database&lt;/h3&gt;All too often, they are perceived as two different worlds. There’s the realm of the Oracle SOA Suite, where everything revolves around technologies like XML, Web Services and BPEL. And there’s your databases, containing your company’s data and business logic in tables, views and PL/SQL logic. For SOA to be successful, these two worlds must be able to interact closely. This presentation will provide a thorough overview of the many ways in which the SOA Suite can connect to the database (for instance through the Database Adapter, SDO, EDN or Native DBWS), and vice versa: how services and processes in the SOA Suite can be invoked from within the database (using among others AQ, UTL_HTTP and DBMS_EPG). Illustrated by demos, the possibilities of each of these techniques will be made clear. Also, some common solutions and scenarios will be discusses using real life examples.&lt;h3&gt;Alex Nuijten - Who’s afraid of Analytic Functions?&lt;/h3&gt;“I never had the need for Analytic Functions”, is one of the phrases that is heard quite often. Maybe that’s true, maybe it isn’t. Recognizing the need for a particular feature can only occur when you have enough exposure to the feature. In this presentation a number of real life examples will be covered which will show the power of Analytic Functions and the ease of solving the challenge at hand. You can learn the syntax from the Oracle Documentation, but you will learn to recognize “the need for them” by real life examples.&lt;a href="http://www.amis.nl/nieuwsenagenda?eventId=38&amp;amp;task=event_register&amp;amp;type=reg_individual" target="_blank"&gt;Inschrijven kan hier.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5582403922226609109?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5582403922226609109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/09/oracle-open-world-java-one-preview-bij.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5582403922226609109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5582403922226609109'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/09/oracle-open-world-java-one-preview-bij.html' title='Oracle Open World / Java One Preview bij AMIS'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6841888613136125512</id><published>2011-08-24T14:45:00.007+02:00</published><updated>2011-08-30T16:33:16.729+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='Function Based Index'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 9i'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Virtual Column'/><title type='text'>Business Rule: Only One per Day, but keep the time</title><content type='html'>The business rule states:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Only one entry is allowed per ID and per day and the time should be recorded.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;The table involved (simplified for the blog post)&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (id         number&lt;br /&gt;  3  ,inspection_dt date&lt;br /&gt;  4  );&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Wouldn't it be nice if it was possible to do it like this?&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (id         number&lt;br /&gt;  3  ,inspection_dt date&lt;br /&gt;  4  ,constraint one_per_day unique (id, trunc (inspection_dt))&lt;br /&gt;  5  );&lt;br /&gt;,constraint one_per_day unique (id, trunc (inspection_dt))&lt;br /&gt;                                    *&lt;br /&gt;ERROR at line 4:&lt;br /&gt;ORA-00904: : invalid identifier&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This way you still have the complete date information (time is a component of the date column), and only use the TRUNC (inspection_dt) to constrain the data entry.&lt;br /&gt;As you can tell from the error message, this is not allowed.&lt;br /&gt;Oracle 11g Release 1 introduced Virtual Columns which can implement this requirement declaratively.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;A Virtual Column is a column which is based on an expression which can be used in e.g. constraints - just what the doctor ordered.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (id         number&lt;br /&gt;  3  ,inspection_dt date&lt;br /&gt;  4  ,inspection_day as (trunc (inspection_dt))&lt;br /&gt;  5  ,constraint one_per_day unique (id, inspection_day)&lt;br /&gt;  6  );&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The virtual column is defined on line 4 in the above CREATE statement. &lt;br /&gt;If the table already exists, you can also add the Virtual Column with an ALTER statement.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter table test add (&lt;br /&gt;  2    inspection_date date&lt;br /&gt;  3    generated always&lt;br /&gt;  4    as&lt;br /&gt;  5      (trunc (inspection_dt))&lt;br /&gt;  6    virtual&lt;br /&gt;  7  )&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To test the requirement, we simply insert date with SYSDATE into the table - wait a little bit (one second) - and insert again with SYSDATE.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; insert into test (id, inspection_dt) values (1, sysdate)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_lock.sleep (1);&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test (id, inspection_dt) values (1, sysdate)&lt;br /&gt;  2  /&lt;br /&gt;insert into test (id, inspection_dt) values (1, sysdate)&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00001: unique constraint (ALEX.ONE_PER_DAY) violated&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Why the wait? Because I scripted this test (for the blog) SYSDATE has only second-granularity, so the records could be inserted in the same second. Which would have the same result as a unique constraint on the ID and INSPECTION_DT.&lt;br /&gt;&lt;br /&gt;As you can see from the output in the above script, only one record is inserted into the table, the other attempt yields an exception.&lt;br /&gt;Just to prove that the time component of the INSPECTION_DT column is recorded as per requirement:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , to_char (inspection_dt, 'dd-mm-yyyy hh24:mi:ss')&lt;br /&gt;  3    from test&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;        ID TO_CHAR(INSPECTION_&lt;br /&gt;---------- -------------------&lt;br /&gt;         1 24-08-2011 05:43:54&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you don't have Oracle 11g, there is a way to implement this requirement with a Unique Function Based Index (will require at least Oracle 9i - I ran my scripts on an Oracle 10g Release 2).&lt;br /&gt;We start of with the same table:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (id         number&lt;br /&gt;  3  ,inspection_dt date&lt;br /&gt;  4  );&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And add the Unique Function Based Index on it.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create unique index one_per_day on test&lt;br /&gt;  2  (id, trunc (inspection_dt))&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Function Based Index is based on the same expression we used earlier, the TRUNC (inspection_dt). Because this index is created as Unique, it will implement the requirement as stated.&lt;br /&gt;Same testscript as before:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; insert into test values (1, sysdate)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_lock.sleep (1);&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test values (1, sysdate)&lt;br /&gt;  2  /&lt;br /&gt;insert into test values (1, sysdate)&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00001: unique constraint (ALEX.ONE_PER_DAY) violated&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , to_char (inspection_dt, 'dd-mm-yyyy hh24:mi:ss')&lt;br /&gt;  3    from test&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;        ID TO_CHAR(INSPECTION_&lt;br /&gt;---------- -------------------&lt;br /&gt;         1 24-08-2011 05:43:52&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Links&lt;/h2&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/pls/db92/search?remark=docindex&amp;word=function+based&amp;book=a96531&amp;preference=&amp;expand_all=1" target="_blank"&gt;Oracle 9i docs&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.oracle-base.com/articles/11g/VirtualColumns_11gR1.php" target="_blank"&gt;Oracle Base on Virtual Columns&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;UPDATE&lt;/h2&gt;&lt;br /&gt;My colleague Martijn Hoekstra just pointed out that Function Based Indexes were introduced in Oracle 8i, not Oracle 9i.&lt;br /&gt;&lt;a href="http://www.oracle-base.com/articles/8i/FunctionBasedIndexes.php" target="_blank"&gt;Oracle-Base on Function Based Indexes&lt;/a&gt;. Thank you Martijn for this correction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6841888613136125512?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6841888613136125512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/08/business-rule-only-one-per-day-but-keep.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6841888613136125512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6841888613136125512'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/08/business-rule-only-one-per-day-but-keep.html' title='Business Rule: Only One per Day, but keep the time'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6513097111068818530</id><published>2011-08-08T11:19:00.004+02:00</published><updated>2011-08-08T11:39:46.243+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='regexp'/><category scheme='http://www.blogger.com/atom/ns#' term='XE'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Splitting a comma delimited string the RegExp way, Part Two</title><content type='html'>Over two years ago &lt;a href="http://nuijten.blogspot.com/2009/07/splitting-comma-delimited-string-regexp.html" target="_blank"&gt;I wrote about&lt;/a&gt; a way to split a comma delimited string using Regular Expresssions. Just a little while ago someone asked how to split it when you have more records involved than just one (as I used in my example).&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;For this example I use the dataset as AnthonyJ used in the comments. The explanation for the regular expression can be found in the &lt;a href="http://nuijten.blogspot.com/2009/07/splitting-comma-delimited-string-regexp.html" target="_blank"&gt;original post&lt;/a&gt;.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; with test as&lt;br /&gt;  2  (&lt;br /&gt;  3  select 1 id, 'joey,anthony,marvin' str from dual union all&lt;br /&gt;  4  select 5 id, 'tony,glenn' str from dual union all&lt;br /&gt;  5  select 8 id, 'john' str from dual&lt;br /&gt;  6  )&lt;br /&gt;  7  select id&lt;br /&gt;  8       , str&lt;br /&gt;  9       , regexp_substr (str, '[^,]+', 1, rn) split&lt;br /&gt; 10    from test&lt;br /&gt; 11    cross&lt;br /&gt; 12    join (select rownum rn&lt;br /&gt; 13            from (select max (length (regexp_replace (str, '[^,]+'))) + 1 mx&lt;br /&gt; 14                    from test&lt;br /&gt; 15                 )&lt;br /&gt; 16         connect by level &lt;= mx&lt;br /&gt; 17         )&lt;br /&gt; 18   where regexp_substr (str, '[^,]+', 1, rn) is not null&lt;br /&gt; 19   order by id&lt;br /&gt; 20  ;&lt;br /&gt;&lt;br /&gt;        ID STR                 SPLIT&lt;br /&gt;---------- ------------------- -------------------&lt;br /&gt;         1 joey,anthony,marvin joey&lt;br /&gt;         1 joey,anthony,marvin marvin&lt;br /&gt;         1 joey,anthony,marvin anthony&lt;br /&gt;         5 tony,glenn          tony&lt;br /&gt;         5 tony,glenn          glenn&lt;br /&gt;         8 john                john&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The trick here is in lines 11 through 17. The Cross Join is there to create multiple records, but no more than the longest intended individual words (three in this case).&lt;br /&gt;Because each ID would result in three records, which is alright for ID 1, it will also create three records for ID 5 and ID 8. Line 18 removes these extra records.&lt;br /&gt;If you use Oracle 11g, you can also use REGEXP_COUNT instead of the combination of REGEXP_REPLACE and LENGTH, which would look like this:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;  cross &lt;br /&gt;  join (select rownum rn &lt;br /&gt;          from (select max (regexp_count (str, ',') + 1) mx&lt;br /&gt;                  from test&lt;br /&gt;               )&lt;br /&gt;       connect by level &lt;= mx&lt;br /&gt;       )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/functions135.htm" target="_blank"&gt;Regexp_Count&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6513097111068818530?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6513097111068818530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/08/splitting-comma-delimited-string-regexp.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6513097111068818530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6513097111068818530'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/08/splitting-comma-delimited-string-regexp.html' title='Splitting a comma delimited string the RegExp way, Part Two'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-276671397712228675</id><published>2011-07-06T20:09:00.005+02:00</published><updated>2011-07-06T21:29:47.405+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ODTUG KScope 2011'/><title type='text'>ODTUG KScope 2011: looking back</title><content type='html'>Last week the yearly ODTUG KScope conference took place in Long Beach, California. I always need to time to let this wonderful event really sink in. The number of sessions and the wealth of information that you get is really quite extraordinary.&lt;br /&gt;In this blogpost I will highlight some of the sessions that I attended.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Sunday&lt;/h3&gt;&lt;br /&gt;Sunday is always a symposium day, this year I attended the APEX track. The Oracle Application Express Development team filled us in on what to expect from the next APEX release, 4.1. The most exciting, or at least to me, is the improved exception handling. &lt;br /&gt;&lt;h3&gt;Monday&lt;/h3&gt;&lt;br /&gt;The "real" conference starts with the Opening General Session. This session wasn't in the conference center, but in the Long Beach Performing Arts Center and what an opening it was.... &lt;a href="http://www.liberidu.com/blog/" target="_blank"&gt;My colleague Marco&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?feature=player_embedded&amp;v=5gHfoCLx9Z8" target="_blank"&gt;filmed parts&lt;/a&gt; of it, simply hilarious.&lt;br /&gt;After the General Session, I attended Cary Millsap's "My case for Agile Methods". It is always very enjoyable to listen to Cary. One of the other sessions that I look forward to seeing what Patrick Wolf's "Improved Error Handling in Oracle Application Express 4.1". Can't wait for the 4.1 version to be released and use it for real.&lt;br /&gt;In the evening there was the Database Guru Panel, with Cary Millsap, Steven Feuerstein and Tom Kyte. These guys are truly awesome, the knowledge they share is just fabulous. Some very interesting discussions filled the evening.&lt;br /&gt;&lt;h3&gt;Tuesday&lt;/h3&gt;&lt;br /&gt;The first session in the morning was my own, "Who's afraid of Analytic Functions?". Around 60 attended my session and I was very pleased with that. &lt;br /&gt;&lt;a href="http://www.oracle.com/technetwork/community/oracle-ace/index.html" target="_blank"&gt;The Oracle ACE Program&lt;/a&gt; organized a very special lunch, a so called "Lunch and Learn" session. In different rooms, different Oracle ACE's and Oracle ACE Directors had panels. Together with &lt;a href="http://awads.net/wp/" target="_blank"&gt;Eddie Awad&lt;/a&gt; and Steven Feuerstein, I was in the Database Development panel. Sometimes it can be quite a challenge to eat lunch and answer questions at the same time.&lt;br /&gt;&lt;h3&gt;Wednesday&lt;/h3&gt;&lt;br /&gt;Carsten Czarski did a great presentation "It's all about Location: APEX, Spatial, and Maps". Nice demo's and clear explanation.&lt;br /&gt;The other session that I really enjoyed was Eddie Awad's "Five Features you ought to know about the Oracle Scheduler", great examples, great explanation. Great job, Eddie!&lt;br /&gt;Doug Gault's "Creating a Spark Line plugin from Scratch", where he shows how to create a plugin for APEX was a fantastic way to end the day. End the day? Well, not completely...&lt;br /&gt;The Wednesday evening is always reserved for the &lt;a href="http://www.queenmary.com/" target="_blank"&gt;Grand Event - All aboard the Queen Mary&lt;/a&gt;, a magnificent party with fireworks to top it off.&lt;br /&gt;&lt;h3&gt;Thursday&lt;/h3&gt;&lt;br /&gt;The closing session I did attend. What the real shocker was, was that I got the "Best Speaker Award - Database Development Track",.. still can't believe it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-276671397712228675?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/276671397712228675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/07/odtug-kscope-2011-looking-back.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/276671397712228675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/276671397712228675'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/07/odtug-kscope-2011-looking-back.html' title='ODTUG KScope 2011: looking back'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5218631905202761524</id><published>2011-06-26T15:23:00.007+02:00</published><updated>2011-08-12T08:32:33.274+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='XE'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='EBR'/><title type='text'>Oracle 11g Express Edition bug?</title><content type='html'>Of course you know that Oracle has released 11g Express Edition, still in Beta though. That is probably why I ran into something weird -must be a bug- while playing with it. Here is what I did:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; set echo on&lt;br /&gt;SQL&gt; col "Current_Edition" format a20&lt;br /&gt;SQL&gt; col object_name format a20&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select sys_context('userenv'&lt;br /&gt;  2                    ,'current_edition_name'&lt;br /&gt;  3                    ) "Current_Edition"&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Current_Edition                                                                                                         &lt;br /&gt;--------------------                                                                                                    &lt;br /&gt;ORA$BASE                                                                                                                &lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  procedure hello&lt;br /&gt;  3  is&lt;br /&gt;  4  begin&lt;br /&gt;  5     dbms_output.put_line ('Hello World');&lt;br /&gt;  6  end hello;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     hello;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;Hello World                                                                                                             &lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select object_name&lt;br /&gt;  2       , object_type&lt;br /&gt;  3       , edition_name&lt;br /&gt;  4    from user_objects_ae&lt;br /&gt;  5   where object_name = 'HELLO'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OBJECT_NAME          OBJECT_TYPE         EDITION_NAME                                                                   &lt;br /&gt;-------------------- ------------------- ------------------------------                                                 &lt;br /&gt;HELLO                PROCEDURE                                                                                          &lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Notice how the Edition_name column is empty...&lt;br /&gt;That is not right, it should show the name of the edition, which it does in Oracle Enterprise Edition &lt;a href="http://nuijten.blogspot.com/2010/11/edition-based-redefinition-and.html"&gt;as can be seen here.&lt;/a&gt;&lt;br /&gt;&lt;b&gt;UPDATE&lt;/b&gt; How wrong I was,... of course this is correct; the user wasn't Edition Enabled.&lt;br /&gt;Let's continue and see what happens when you introduce a new edition.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; create edition R1 as child of ora$base&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; alter session set edition = R1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  procedure hello&lt;br /&gt;  3  is&lt;br /&gt;  4  begin&lt;br /&gt;  5     dbms_output.put_line ('Hello Universe');&lt;br /&gt;  6  end hello;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     hello;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;Hello Universe                                                                                                          &lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select object_name&lt;br /&gt;  2       , object_type&lt;br /&gt;  3       , edition_name&lt;br /&gt;  4    from user_objects_ae&lt;br /&gt;  5   where object_name = 'HELLO'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OBJECT_NAME          OBJECT_TYPE         EDITION_NAME                                                                   &lt;br /&gt;-------------------- ------------------- ------------------------------                                                 &lt;br /&gt;HELLO                PROCEDURE                                                                                          &lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The procedure works as expected, showing the right information but there is something really weird going on.&lt;br /&gt;There should be two entries in the USER_OBJECTS_AE, one for ORA$BASE and one for R1...&lt;br /&gt;&lt;br /&gt;What if we were to retire the ORA$BASE edition? Let's try that.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; conn sys/oracle@xe11 as sysdba&lt;br /&gt;Connected.&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; ALTER DATABASE DEFAULT EDITION = r1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Database altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; drop edition ora$base cascade&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition dropped.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now for the ORA-00600...&lt;br /&gt;&lt;br /&gt;After a shutdown (of my VM), I get this message when using DBMS_OUTPUT:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     hello;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;begin&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-22303: type "SYS"."DBMSOUTPUT_LINESARRAY" not found&lt;br /&gt;ORA-00600: internal error code, arguments: [kkaegen_get_edition_name_1], [], [], [], [], [], [], [], [], [], [], []&lt;br /&gt;ORA-06508: PL/SQL: could not find program unit being called: "PUBLIC.DBMS_OUTPUT"&lt;br /&gt;ORA-06512: at "ALEX.HELLO", line 4&lt;br /&gt;ORA-06512: at line 2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By looking at it, I get a feeling that it has something to do with EBR...&lt;br /&gt;&lt;b&gt;UPDATE:&lt;/b&gt; After contacting Bryn Llewellyn, PL/SQL product manager, a bug was filled: 12758386.&lt;br /&gt;&lt;h2&gt;Links&lt;/h2&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e17125/adfns_editions.htm#CHDEEIBA"&gt;Retiring Editions&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/technetwork/database/express-edition/11gxe-beta-download-302519.html"&gt;Oracle Database 11.2 Express Edition Beta&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5218631905202761524?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5218631905202761524/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/06/oracle-11g-express-edition-bug.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5218631905202761524'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5218631905202761524'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/06/oracle-11g-express-edition-bug.html' title='Oracle 11g Express Edition bug?'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2292876810425321430</id><published>2011-05-21T09:38:00.005+02:00</published><updated>2011-05-26T09:49:54.032+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='ODTUG KScope 2011'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>ODTUG KScope Preview bij AMIS</title><content type='html'>Ook dit jaar, namelijk op &lt;strong&gt;dinsdag 14 Juni&lt;/strong&gt;, organiseert AMIS de ODTUG Preview. Het jaarlijkse congres van de ODTUG, de Oracle Development Tools Users Group, vind dit jaar plaats in Longbeach, California van 26 tot en met 30 juni. Het is niet voor iedereen weggelegd om daar naar toe te gaan. AMIS biedt, alweer voor het vijfde achtereenvolgende jaar, aan geïnteresseerden de kans om een selectie van de presentaties die daar te zien zijn bij te wonen. Een aantal Europese sprekers zal tijdens de AMIS ODTUG preview presentatie laten zien die ook in de Verenigde Staten worden gehouden. &lt;br /&gt;Tijdens de AMIS ODTUG Preview zullen er drie keer drie parallelle sessies worden gehouden met verschillende onderwerpen zoals  APEX, database development, ADF, JHeadstart en SOA.&lt;br /&gt;&lt;br /&gt;Programma:&lt;br /&gt;&lt;table border="1"&gt;&lt;br /&gt;            &lt;tr&gt;&lt;br /&gt;                &lt;th&gt;&lt;br /&gt;                    Tijd&lt;br /&gt;                &lt;/th&gt;&lt;br /&gt;                &lt;th&gt;&lt;br /&gt;                    Track 1&lt;br /&gt;                &lt;/th&gt;&lt;br /&gt;                &lt;th&gt;&lt;br /&gt;                    Track 2&lt;br /&gt;                &lt;/th&gt;&lt;br /&gt;                &lt;th&gt;&lt;br /&gt;                    Track 3&lt;br /&gt;                &lt;/th&gt;&lt;br /&gt;            &lt;/tr&gt;&lt;br /&gt;            &lt;tr&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    16:30&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td colspan="3"&gt;&lt;br /&gt;                    Welkom en Registratie&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;            &lt;/tr&gt;&lt;br /&gt;            &lt;tr&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    17:00&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    &lt;a href="http://technology.amis.nl/blog/12108/xfiles-apex-community-edition-xace"&gt;XFILES, the APEX 4 Version: The Truth is in There...&lt;/a&gt;&lt;br /&gt;&lt;/br&gt;                    Marco Gralike &amp; Roel Hartman&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    ADF Developers - Make the Database Work for You&lt;br /&gt;&lt;br&gt;                    Lucas Jellema&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    Pipelined Table Functions&lt;br /&gt;     &lt;br&gt;Patrick Barel&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;            &lt;/tr&gt;&lt;br /&gt;            &lt;br /&gt;            &lt;tr&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    18:00&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td colspan="3"&gt;&lt;br /&gt;                    Dinner&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;            &lt;/tr&gt;&lt;br /&gt;            &lt;tr&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    19:00&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    APEX Face/Off - Designing a GUI with APEX Templates and Themes&lt;br /&gt;     &lt;br&gt;Christian Rokitta&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    BPMN: The New Silver Bullet?&lt;br /&gt;     &lt;br&gt;Lonneke Dikmans&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    Oracle JHeadstart: Superior Productivity in Developing Best-practice ADF Web Applications&lt;br /&gt;     &lt;br&gt;Steven Davelaar&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;            &lt;/tr&gt;&lt;br /&gt;            &lt;tr&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    20:15&lt;br /&gt;                &lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    Who's Afraid of Analytic Functions?&lt;br /&gt;                &lt;br&gt;Alex Nuijten&lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    Overview of Eventing in Oracle SOA Suite 11g&lt;br /&gt;                &lt;br&gt;Ronald van Luttikhuizen&lt;/td&gt;&lt;br /&gt;                &lt;td&gt;&lt;br /&gt;                    ...and Thus Your Forms 'Automagically' Disappeared&lt;br /&gt;                &lt;br&gt;Luc Bors&lt;/td&gt;&lt;br /&gt;            &lt;/tr&gt;&lt;br /&gt;                    &lt;/table&gt;&lt;br /&gt;Dit evenement is met name bedoeld voor ontwikkelaars.&lt;br /&gt;Uiteraard zijn er aan dit event geen kosten verbonden, maar het aantal plaatsen voor dit evenement is beperkt, wacht niet te lang. Vol is vol.&lt;br /&gt;Inschrijven via &lt;a href="http://www.amis.nl/evenementen/601-odtug-kscope-preview-2011" target="_blank"&gt;www.amis.nl&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2292876810425321430?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2292876810425321430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/05/odtug-kscope-preview-bij-amis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2292876810425321430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2292876810425321430'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/05/odtug-kscope-preview-bij-amis.html' title='ODTUG KScope Preview bij AMIS'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3213529878816622927</id><published>2011-04-13T20:16:00.002+02:00</published><updated>2011-04-13T20:33:04.854+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>Planboard DBA Symposium: Registration Opened</title><content type='html'>The &lt;a href="http://www.planboard.com/new/index.php?option=com_content&amp;view=article&amp;id=19&amp;Itemid=2" target="_blank"&gt;6th Planboard DBA Symposium&lt;/a&gt; is opened for registration. I'm very pleased to be presenting again at this "For DBA by DBA Symposium". Especially since I'm not really a DBA... :)&lt;br /&gt;Guess the organization committee was pleased with the presentation I did for &lt;a href="http://nuijten.blogspot.com/2009/09/planboard-symposium-registration-open.html" target="_blank"&gt;the fourth Symposium.&lt;/a&gt; That time I was scheduled opposite &lt;a href="http://prutser.wordpress.com/" target="_blank"&gt;Harald van Breederode&lt;/a&gt;, this time it's &lt;a href="http://fritshoogland.wordpress.com/" target="_blank"&gt;Frits Hoogland&lt;/a&gt;. Hopefully someone will show up for my session... or should I say: "Sorry Frits, too bad nobody showed up at your session". :)&lt;br /&gt;This time I'm doing one of my favorite presentations: "SQL Holmes: The Case of the Missing Performance". Looking forward to it. Hope to meet you there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3213529878816622927?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3213529878816622927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2011/04/planboard-dba-symposium-registration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3213529878816622927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3213529878816622927'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2011/04/planboard-dba-symposium-registration.html' title='Planboard DBA Symposium: Registration Opened'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5198397269277119339</id><published>2010-12-23T14:15:00.000+01:00</published><updated>2010-12-23T14:15:00.971+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SOA'/><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><title type='text'>Should you expose a Stored Procedure via XMLType?</title><content type='html'>When you want to expose your Stored Procedure to the "outside world", should you do this using XMLType? As always, the correct answer is "it  depends".&lt;br /&gt;For this example I will use a Package containing two functions, the signature of the package is as follows:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;create or replace package emps_pkg&lt;br /&gt;is&lt;br /&gt;   function get_department_xml (p_department_no in number)&lt;br /&gt;      return xmltype;&lt;br /&gt;&lt;br /&gt;   function get_departement_ot (p_department_no in number)&lt;br /&gt;      return all_departments_ot;&lt;br /&gt;  &lt;br /&gt;end emps_pkg;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;One function returns an XMLType (named get_department_xml) and the other returns an Object Type (named get_department_ot). They both return data from the EMPLOYEES and DEPARTEMENTS tables in the HR schema.&lt;br /&gt;The implementation of the package body is based on &lt;a href="http://technology.amis.nl/blog/9473/creating-an-xmltype-based-on-object-types"&gt;this blogpost.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;With a database adapter in the SOA Suite you can expose each of these functions. To be really useful you will need to add a mediator.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/TRNFDLiwT4I/AAAAAAAAA60/0Wukg5T5jhE/s1600/Mediator.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/TRNFDLiwT4I/AAAAAAAAA60/0Wukg5T5jhE/s320/Mediator.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5553858686418440066" /&gt;&lt;/a&gt;&lt;br /&gt;On the right hand side, in the above image, the database adapters are shown. The purple things are the mediators where the actual mapping takes place (XSLT).&lt;br /&gt;&lt;br /&gt;The biggest difference between the two implementations can be found in the Mapper file.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/TRNFDXN8bdI/AAAAAAAAA7E/3o4Wb5UsLU0/s1600/XmlTypeMapping.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 240px; height: 156px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/TRNFDXN8bdI/AAAAAAAAA7E/3o4Wb5UsLU0/s320/XmlTypeMapping.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5553858689552379346" /&gt;&lt;/a&gt;&lt;br /&gt;As you can see in the image above, the functions returns something. That "something" could basically be anything. Not a whole lot of information.&lt;br /&gt;&lt;br /&gt;Using the Object Type as a returntype from the function makes a big difference.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/TRNFDRPMxPI/AAAAAAAAA68/nZFKi070jiw/s1600/objectType.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 270px; height: 320px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/TRNFDRPMxPI/AAAAAAAAA68/nZFKi070jiw/s320/objectType.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5553858687947031794" /&gt;&lt;/a&gt;&lt;br /&gt;Here you can see the complete structure of the Object Type (which is a quite complex Object Type). Makes Mapping a lot easier.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5198397269277119339?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5198397269277119339/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/12/should-you-expose-stored-procedure-via.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5198397269277119339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5198397269277119339'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/12/should-you-expose-stored-procedure-via.html' title='Should you expose a Stored Procedure via XMLType?'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_tdqazgf19_s/TRNFDLiwT4I/AAAAAAAAA60/0Wukg5T5jhE/s72-c/Mediator.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-8634376087719945994</id><published>2010-12-23T13:44:00.001+01:00</published><updated>2010-12-23T13:54:24.489+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SOA'/><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><title type='text'>Creating an XMLType based on Object Types</title><content type='html'>Sometimes it is necessary to create a Stored Procedure which returns an XMLType, like when you want to expose the Stored Procedure to the "outside world", like via a Mediator. There are several options to create an XMLType. In this blogpost I will show you two ways of doing this. First the "regular" way using XMLElement, XMLForest and XMLAgg. Second using Object Types which are being converted to XMLType.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;For this example I will use the HR schema, and in it are the DEPARTMENTS and EMPLOYEES tables, which has a Master-Detail relation between them. The DEPARTMENTS table &lt;br /&gt;has a MANAGER_ID (the manager of the department). The EMPLOYEES table also has a MANAGER_ID (the person who the employee reports to).&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select d.department_name&lt;br /&gt;  2       , d.manager_id&lt;br /&gt;  3       , e.first_name&lt;br /&gt;  4       , e.last_name&lt;br /&gt;  5       , e.manager_id&lt;br /&gt;  6    from departments d&lt;br /&gt;  7    join employees   e&lt;br /&gt;  8      on d.department_id = e.department_id&lt;br /&gt;  9   where d.department_id = 20&lt;br /&gt; 10  /&lt;br /&gt;&lt;br /&gt;DEPARTMENT_NAME                MANAGER_ID FIRST_NAME           LAST_NAME                 MANAGER_ID&lt;br /&gt;------------------------------ ---------- -------------------- ------------------------- ----------&lt;br /&gt;Marketing                             201 Michael              Hartstein                     100&lt;br /&gt;Marketing                             201 Pat                  Fay                           201&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What I want is an XML structured like this:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;&amp;lt;departments&gt;&lt;br /&gt;   &amp;lt;department&gt;&lt;br /&gt;      &amp;lt;departmentname&gt;Marketing&lt;/departmentname&gt;&lt;br /&gt;      &amp;lt;department_manager&gt;Steven King&lt;/department_manager&gt;&lt;br /&gt;      &amp;lt;employees&gt;&lt;br /&gt;         &amp;lt;employee&gt;&lt;br /&gt;            &amp;lt;name&gt;Michael Hartstein&lt;/name&gt;&lt;br /&gt;         &amp;lt;/employee&gt;&lt;br /&gt;         &amp;lt;employee&gt;&lt;br /&gt;            &amp;lt;name&gt;Pat Fay&lt;/name&gt;&lt;br /&gt;         &amp;lt;/employee&gt;&lt;br /&gt;      &amp;lt;/employees&gt;&lt;br /&gt;   &amp;lt;/department&gt;&lt;br /&gt;&amp;lt;/departments&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In order to get that you can write a SQL statement like this one:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; set long 90000 lines 80&lt;br /&gt;SQL&gt; select xmlelement("departments"&lt;br /&gt;  2                   ,(select xmlagg(xmlelement("department"&lt;br /&gt;  3                                             ,xmlelement ("departmentname"&lt;br /&gt;  4                                                         ,d.department_name&lt;br /&gt;  5                                                         )&lt;br /&gt;  6                                             ,xmlelement ("department_manager"&lt;br /&gt;  7                                                         ,(select m.first_name || ' ' ||&lt;br /&gt;  8                                                                  m.last_name&lt;br /&gt;  9                                                             from employees m&lt;br /&gt; 10                                                            where m.employee_id = d.manager_id&lt;br /&gt; 11                                                           )&lt;br /&gt; 12                                                          )&lt;br /&gt; 13                                             ,xmlforest ((select xmlagg(xmlelement("employee"&lt;br /&gt; 14                                                                                  ,e.first_name || ' ' ||&lt;br /&gt; 15                                                                                   e.last_name&lt;br /&gt; 16                                                                                  )&lt;br /&gt; 17                                                                       )&lt;br /&gt; 18                                                            from employees e&lt;br /&gt; 19                                                           where e.department_id = d.department_id&lt;br /&gt; 20                                                         ) as "employees"&lt;br /&gt; 21                                                        )&lt;br /&gt; 22                                             )&lt;br /&gt; 23                                  )&lt;br /&gt; 24                       from departments d&lt;br /&gt; 25                      where d.department_id = 20&lt;br /&gt; 26                    )&lt;br /&gt; 27                   )&lt;br /&gt; 28    from dual&lt;br /&gt; 29  /&lt;br /&gt;&lt;br /&gt;XMLELEMENT("DEPARTMENTS",(SELECTXMLAGG(XMLELEMENT("DEPARTMENT",XMLELEMENT("DEPAR&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&amp;lt;departments&gt;&lt;department&gt;&lt;departmentname&gt;Marketing&lt;/departmentname&gt;&lt;department_m&lt;br /&gt;anager&gt;Michael Hartstein&lt;/department_manager&gt;&lt;employees&gt;&lt;employee&gt;Michael Hartst&lt;br /&gt;ein&lt;/employee&gt;&lt;employee&gt;Pat Fay&lt;/employee&gt;&lt;/employees&gt;&lt;/department&gt;&lt;/departments&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But the are probably other ways of getting the same results as well. What you can take away from this statement is that it is quite hard to write and maintain.&lt;br /&gt;Look at all those parentheses, enough to drive you crazy...&lt;br /&gt;&lt;br /&gt;There is also the possibility of turning Object Types into XMLType, let's explore this route.&lt;br /&gt;First some Object Types describing our desired XML output&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create type employee_ot as object&lt;br /&gt;  2  (employee_id   number&lt;br /&gt;  3  ,employee_name varchar2 (60)&lt;br /&gt;  4  );&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Type created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create type employees_tt is table of employee_ot&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Type created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; create type department_ot is object&lt;br /&gt;  2  (departmentname    varchar2(30)&lt;br /&gt;  3  ,departmentmanager employee_ot&lt;br /&gt;  4  ,employees         employees_tt&lt;br /&gt;  5  );&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;Type created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create type departments_tt is table of department_ot&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Type created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create type all_departments_ot is object&lt;br /&gt;  2  (departments departments_tt);&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Type created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we can use these Object Types to construct a query like this&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select department_ot (d.department_name&lt;br /&gt;  2                       ,(select employee_ot (m.employee_id&lt;br /&gt;  3                                           , m.first_name||' '||m.last_name&lt;br /&gt;  4                                           )&lt;br /&gt;  5                           from employees m&lt;br /&gt;  6                          where m.employee_id = d.manager_id&lt;br /&gt;  7                         )&lt;br /&gt;  8                      ,cast ( multiset (select employee_ot (e.employee_id&lt;br /&gt;  9                                    ,e.first_name||' '||e.last_name&lt;br /&gt; 10                                    )&lt;br /&gt; 11                                    from employees e where e.department_id = d.department_id&lt;br /&gt; 12                                    )as employees_tt)&lt;br /&gt; 13                        )&lt;br /&gt; 14    from departments d&lt;br /&gt; 15   where d.department_id = 20&lt;br /&gt; 16  /&lt;br /&gt;&lt;br /&gt;DEPARTMENT_OT(D.DEPARTMENT_NAME,(SELECTEMPLOYEE_OT(M.EMPLOYEE_ID,M.FIRST_NAME||'&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;DEPARTMENT_OT('Marketing', EMPLOYEE_OT(201, 'Michael Hartstein'), EMPLOYEES_TT(E&lt;br /&gt;MPLOYEE_OT(201, 'Michael Hartstein'), EMPLOYEE_OT(202, 'Pat Fay')))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So What, you say?&lt;br /&gt;Well, if you do a describe of the XMLTYPE (like in SQL*Plus), you will see this:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; desc xmltype&lt;br /&gt;...&lt;br /&gt;METHOD&lt;br /&gt;------&lt;br /&gt; FINAL CONSTRUCTOR FUNCTION XMLTYPE RETURNS SELF AS RESULT&lt;br /&gt; Argument Name                  Type                    In/Out Default?&lt;br /&gt; ------------------------------ ----------------------- ------ --------&lt;br /&gt; XMLDATA                        UNDEFINED               IN&lt;br /&gt; SCHEMA                         VARCHAR2                IN     DEFAULT&lt;br /&gt; ELEMENT                        VARCHAR2                IN     DEFAULT&lt;br /&gt; VALIDATED                      NUMBER                  IN     DEFAULT&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can use an Object Type as an input to create an XMLTYPE, pretty nifty.&lt;br /&gt;The structure of the Object Types needs to match the structure of the XML.&lt;br /&gt;Just add the XMLTYPE to the query and presto:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select xmltype (&lt;br /&gt;  2        all_departments_ot (cast (&lt;br /&gt;  3         collect (department_ot (d.department_name&lt;br /&gt;  4                                ,(select employee_ot (m.employee_id&lt;br /&gt;  5                                                     ,m.first_name||' '||m.last_name&lt;br /&gt;  6                                                     )&lt;br /&gt;  7                                    from employees m&lt;br /&gt;  8                                   where m.employee_id = d.manager_id&lt;br /&gt;  9                                 )&lt;br /&gt; 10                                ,cast ( multiset (select employee_ot (e.employee_id&lt;br /&gt; 11                                                                     ,e.first_name||' '||e.last_name&lt;br /&gt; 12                                                                     )&lt;br /&gt; 13                                                    from employees e&lt;br /&gt; 14                                                   where e.department_id = d.department_id&lt;br /&gt; 15                                                 )&lt;br /&gt; 16                                        as employees_tt&lt;br /&gt; 17                                       )&lt;br /&gt; 18                                 )&lt;br /&gt; 19                 ) as departments_tt)))&lt;br /&gt; 20    from departments d&lt;br /&gt; 21   where d.department_id = 20&lt;br /&gt; 22  /&lt;br /&gt;&lt;br /&gt;XMLTYPE(ALL_DEPARTMENTS_OT(CAST(COLLECT(DEPARTMENT_OT(D.DEPARTMENT_NAME,(SELECTEMPLOYEE_OT(M.EMPLOYEE_I&lt;br /&gt;&lt;br /&gt;-------------------------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;&amp;lt;ALL_DEPARTMENTS_OT&gt;&lt;br /&gt;  &amp;lt;DEPARTMENTS&gt;&lt;br /&gt;    &amp;lt;DEPARTMENT_OT&gt;&lt;br /&gt;      &amp;lt;DEPARTMENTNAME&gt;Marketing&lt;/DEPARTMENTNAME&gt;&lt;br /&gt;      &amp;lt;DEPARTMENTMANAGER&gt;&lt;br /&gt;        &amp;lt;EMPLOYEE_ID&gt;201&lt;/EMPLOYEE_ID&gt;&lt;br /&gt;        &amp;lt;EMPLOYEE_NAME&gt;Michael Hartstein&lt;/EMPLOYEE_NAME&gt;&lt;br /&gt;      &amp;lt;/DEPARTMENTMANAGER&gt;&lt;br /&gt;      &amp;lt;EMPLOYEES&gt;&lt;br /&gt;        &amp;lt;EMPLOYEE_OT&gt;&lt;br /&gt;          &amp;lt;EMPLOYEE_ID&gt;201&lt;/EMPLOYEE_ID&gt;&lt;br /&gt;          &amp;lt;EMPLOYEE_NAME&gt;Michael Hartstein&lt;/EMPLOYEE_NAME&gt;&lt;br /&gt;        &amp;lt;/EMPLOYEE_OT&gt;&lt;br /&gt;        &amp;lt;EMPLOYEE_OT&gt;&lt;br /&gt;          &amp;lt;EMPLOYEE_ID&gt;202&lt;/EMPLOYEE_ID&gt;&lt;br /&gt;          &amp;lt;EMPLOYEE_NAME&gt;Pat Fay&lt;/EMPLOYEE_NAME&gt;&lt;br /&gt;        &amp;lt;/EMPLOYEE_OT&gt;&lt;br /&gt;      &amp;lt;/EMPLOYEES&gt;&lt;br /&gt;    &amp;lt;/DEPARTMENT_OT&gt;&lt;br /&gt;  &amp;lt;/DEPARTMENTS&gt;&lt;br /&gt;&amp;lt;/ALL_DEPARTMENTS_OT&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-8634376087719945994?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/8634376087719945994/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/12/creating-xmltype-based-on-object-types.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8634376087719945994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8634376087719945994'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/12/creating-xmltype-based-on-object-types.html' title='Creating an XMLType based on Object Types'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2833219126382243203</id><published>2010-12-20T13:52:00.006+01:00</published><updated>2010-12-20T15:46:09.348+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 9i'/><title type='text'>Using DBMS_UTILITY to aggregate strings</title><content type='html'>In my presentation "Analytic Functions Revisited" there is a section on how to string together some columns, string aggregation. Why? There is a new function in Oracle 11g Release 2, called LISTAGG which allows you to do this. This LISTAGG function also has an Analytic counterpart and that's the reason it is in the presentation.&lt;br /&gt;At the UKOUG TEBS conference I did this presentation and someone approached me after wards to talk about these techniques. It boiled down to "Why do it the hard way when there is a built in package that allows you to do this for you?"&lt;br /&gt;The built in package he was referring to is, note the title of this blog entry, DBMS_UTILITY. There are some downsides of using DBMS_UTILITY, more than I initially thought.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;There is a procedure which allows you to change an Associative Array into a comma delimited string. This procedure is called: "TABLE_TO_COMMA".&lt;br /&gt;First the version on which I ran these scripts:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Express Edition Release 10.2.0.1.0 - Product&lt;br /&gt;PL/SQL Release 10.2.0.1.0 - Production&lt;br /&gt;CORE    10.2.0.1.0      Production&lt;br /&gt;TNS for 32-bit Windows: Version 10.2.0.1.0 - Production&lt;br /&gt;NLSRTL Version 10.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now for the proper usage of the TABLE_TO_COMMA procedure:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     names_list dbms_utility.uncl_array;&lt;br /&gt;  3     len  number;&lt;br /&gt;  4     str varchar2(32767);&lt;br /&gt;  5  begin&lt;br /&gt;  6     names_list (1) := 'this';&lt;br /&gt;  7     names_list (2) := 'that';&lt;br /&gt;  8     names_list (3) := 'the other';&lt;br /&gt;  9     dbms_utility.table_to_comma (tab =&gt; names_list&lt;br /&gt; 10                                 ,tablen =&gt; len&lt;br /&gt; 11                                 ,list =&gt; str&lt;br /&gt; 12                                 );&lt;br /&gt; 13     dbms_output.put_line ('The delimited string: '||str);&lt;br /&gt; 14     dbms_output.put_line ('The length of the collection is '||to_char (len));&lt;br /&gt; 15  end;&lt;br /&gt; 16  /&lt;br /&gt;The delimited string: this,that,the other&lt;br /&gt;The length of the collection is 3&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see in the results the collection is converted to a comma separated string. The number of elements in the collection is also shown.&lt;br /&gt;&lt;br /&gt;The first thing that popped into mind was the most notable limitation of DBMS_UTILITY.TABLE_TO_COMMA: you can't pass in a collection of numbers.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     names_list dbms_utility.uncl_array;&lt;br /&gt;  3     len  number;&lt;br /&gt;  4     str varchar2(32767);&lt;br /&gt;  5  begin&lt;br /&gt;  6     names_list (1) := 1;&lt;br /&gt;  7     names_list (2) := 2;&lt;br /&gt;  8     names_list (3) := 3;&lt;br /&gt;  9     dbms_utility.table_to_comma (tab =&gt; names_list&lt;br /&gt; 10                                 ,tablen =&gt; len&lt;br /&gt; 11                                 ,list =&gt; str&lt;br /&gt; 12                                 );&lt;br /&gt; 13     dbms_output.put_line ('The delimited string: '||str);&lt;br /&gt; 14     dbms_output.put_line ('The length of the collection is '||to_char (len));&lt;br /&gt; 15  end;&lt;br /&gt; 16  /&lt;br /&gt;The delimited string: 1,2,3&lt;br /&gt;The length of the collection is 3&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Euh,... well, you can &lt;i&gt;now&lt;/i&gt;.&lt;br /&gt;The signature of TABLE_TO_COMMA in Oracle &lt;i&gt;9i&lt;/i&gt; was:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;DBMS_UTILITY.TABLE_TO_COMMA ( &lt;br /&gt;   tab    IN  UNCL_ARRAY, &lt;br /&gt;   tablen OUT BINARY_INTEGER,&lt;br /&gt;   list   OUT VARCHAR2); &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The definition of UNCL_ARRAY (Oracle 9i) is:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Lists of "USER"."NAME"."COLUMN"@LINK should be stored here.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;And because of this you couldn't pass in numbers. Numbers are not valid identifiers.&lt;br /&gt;apparently things changed, passing in numbers is allowed nowadays.&lt;br /&gt;&lt;br /&gt;Oracle 10&lt;i&gt;g&lt;/i&gt; introduced this overloading:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;DBMS_UTILITY.TABLE_TO_COMMA ( &lt;br /&gt;   tab    IN  lname_array,&lt;br /&gt;   tablen OUT BINARY_INTEGER,&lt;br /&gt;   list   OUT VARCHAR2);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since that limitation is gone, should you use TABLE_TO_COMMA to aggregate strings? I don't think you should (but it depends - as always). If you need data from the database using SQL, you might as well stay within the SQL realm and use SQL techniques to accomplish this goal.&lt;/br&gt;&lt;br /&gt;There is some other oddities I just discovered playing with TABLE_TO_COMMA. The procedure only works with dense collections starting from 1 (one). Using a sparse collection won't show you the complete collection:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     names_list dbms_utility.uncl_array;&lt;br /&gt;  3     len  number;&lt;br /&gt;  4     str varchar2(32767);&lt;br /&gt;  5  begin&lt;br /&gt;  6     names_list (1) := 'this';&lt;br /&gt;  7     names_list (2) := 'that';&lt;br /&gt;  8     names_list (3) := 'the other';&lt;br /&gt;  9     names_list (42) := 'something';&lt;br /&gt; 10     dbms_utility.table_to_comma (tab =&gt; names_list&lt;br /&gt; 11                                 ,tablen =&gt; len&lt;br /&gt; 12                                 ,list =&gt; str&lt;br /&gt; 13                                 );&lt;br /&gt; 14     dbms_output.put_line ('The delimited string: '||str);&lt;br /&gt; 15     dbms_output.put_line ('The length of the collection is '||to_char (len));&lt;br /&gt; 16  end;&lt;br /&gt; 17  /&lt;br /&gt;The delimited string: this,that,the other&lt;br /&gt;The length of the collection is 3&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Starting at a different value won't show you anything&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     names_list dbms_utility.uncl_array;&lt;br /&gt;  3     len  number;&lt;br /&gt;  4     str varchar2(32767);&lt;br /&gt;  5  begin&lt;br /&gt;  6     names_list (11) := 'this';&lt;br /&gt;  7     names_list (12) := 'that';&lt;br /&gt;  8     names_list (13) := 'the other';&lt;br /&gt;  9     names_list (14) := 'something';&lt;br /&gt; 10     dbms_utility.table_to_comma (tab =&gt; names_list&lt;br /&gt; 11                                 ,tablen =&gt; len&lt;br /&gt; 12                                 ,list =&gt; str&lt;br /&gt; 13                                 );&lt;br /&gt; 14     dbms_output.put_line ('The delimited string: '||str);&lt;br /&gt; 15     dbms_output.put_line ('The length of the collection is '||to_char (len));&lt;br /&gt; 16  end;&lt;br /&gt; 17  /&lt;br /&gt;The delimited string:&lt;br /&gt;The length of the collection is 0&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All the entries starting at zero (or lower) won't show up in the output.&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     names_list dbms_utility.uncl_array;&lt;br /&gt;  3     len  number;&lt;br /&gt;  4     str varchar2(32767);&lt;br /&gt;  5  begin&lt;br /&gt;  6     names_list (0) := 'what';&lt;br /&gt;  7     names_list (1) := 'this';&lt;br /&gt;  8     names_list (2) := 'that';&lt;br /&gt;  9     names_list (3) := 'the other';&lt;br /&gt; 10     names_list (4) := 'something';&lt;br /&gt; 11     dbms_utility.table_to_comma (tab =&gt; names_list&lt;br /&gt; 12                                 ,tablen =&gt; len&lt;br /&gt; 13                                 ,list =&gt; str&lt;br /&gt; 14                                 );&lt;br /&gt; 15     dbms_output.put_line ('The delimited string: '||str);&lt;br /&gt; 16     dbms_output.put_line ('The length of the collection is '||to_char (len));&lt;br /&gt; 17  end;&lt;br /&gt; 18  /&lt;br /&gt;The delimited string: this,that,the other,something&lt;br /&gt;The length of the collection is 4&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Because of these oddities I wouldn't use DBMS_UTILITY.TABLE_TO_COMMA to create a comma separated string.&lt;br /&gt;&lt;b&gt;Links&lt;/b&gt;&lt;br /&gt;&lt;a target ="_blank" href="http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions087.htm"&gt;Oracle Documentation on LISTAGG&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_util.htm#i1002844"&gt;Oracle 9i Documentation on DBMS_UTILITY&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_util.htm#i1002844"&gt;Oracle 10g Documentation on DBMS_UTILITY&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/E11882_01/timesten.112/e14000/d_util.htm#i1002844"&gt;Oracle 11g Documentation on DBMS_UTILITY&lt;/a&gt;&lt;br /&gt;&lt;a target ="_blank"  href="http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php"&gt;Tim Hall on String Aggregation Techniques&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2833219126382243203?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2833219126382243203/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/12/using-dbmsutility-to-aggregate-strings.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2833219126382243203'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2833219126382243203'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/12/using-dbmsutility-to-aggregate-strings.html' title='Using DBMS_UTILITY to aggregate strings'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-4713135816064677553</id><published>2010-12-16T09:05:00.004+01:00</published><updated>2010-12-17T11:01:33.026+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='UKOUG'/><title type='text'>My First UKOUG - day two and three</title><content type='html'>Why can I never find the time to write my blogs?... Anyway, long overdue, here is my report from my first UKOUG conference. If you want to know what I did the first day, &lt;a href="http://nuijten.blogspot.com/2010/12/my-first-ukoug-day-one.html"&gt;check out this link&lt;/a&gt;.&lt;br /&gt;As I mentioned before the program is awesome, lots of very good speakers and very interesting subjects. It's quite hard to pick and choose the sessions. So what did I do the second and third day there?&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;On the second I arrived late for the sessions, running into the room at 08:45 when the session was starting,... well starting, not really. The speaker, Martin Widlake on "My SQL is suddenly performing badly and nothing has changed", was even later than I was. All in all, the session was not very good. The content was good, but he kept rushing through it. &lt;a href="http://mwidlake.wordpress.com/2010/11/30/how-not-to-present/"&gt;At least he agrees himself.&lt;/a&gt;&lt;br /&gt;The next session was on APEX and GIS mapping. I like APEX. I like Spatial. The combination would be great. It was a very generic presentation of what is needed to get everything up and running. Nice session by Marin Huljev and Dalibor Kusic.&lt;br /&gt;Sue Harper showed the new features of SQL Developer Data Modeler. It's always very enjoyable to see Sue present. I've seen her present multiple times, and her demo's sometimes do different things than she expects. That doesn't matter, she always recovers really well. If you want to know about the new features - like Subversion support - and listen to Sue explaining them, &lt;a href="http://download.oracle.com/otn_hosted_doc/sqldev/EA1_Overview/EA1_Overview.html"&gt;follow this link.&lt;/a&gt;&lt;br /&gt;Randolf Geist did an &lt;a href="http://oracle-randolf.blogspot.com/2010/12/advanced-oracle-troubleshooting-live.html"&gt;excellent session on Advanced Oracle Troubleshooting&lt;/a&gt;. It's too much information to cover in a small blog like this one.&lt;br /&gt;Even though Hall 5 was very cold, the two sessions I added there were worth while. Tanel Poder did a session on performance tuning and troubleshooting. A lot of the methods and scripts were also covered by Randolf Geist, but that didn't matter. A very good session.&lt;br /&gt;Chris Roderick had a session there called "Oracle Performance Through Understanding". He works for Cern and they deal with a lot of data. And I really mean &lt;i&gt;a lot!&lt;/i&gt;&lt;br /&gt;Therefor getting the design of your application right and optimized for performance is key. He explained the concepts well and gave some pointers.&lt;br /&gt;Edition Based Redefinition is one of the killer features of the Oracle 11g Release 2 database, on which I have &lt;a href="http://nuijten.blogspot.com/2009/11/planboard-symposium-day-after.html"&gt;presented&lt;/a&gt; and &lt;a href="http://nuijten.blogspot.com/2010/04/oracle-11gr2-editions-and-sql-developer.html"&gt;written&lt;/a&gt; a &lt;a href="http://nuijten.blogspot.com/2010/11/edition-based-redefinition-and.html"&gt;few times&lt;/a&gt; before, and I'm always interested to hear what other people cover when they talk about this feature. Melanie Caffrey had a session on this feature, based on a case study. It still amazes me how wonderful the functionality is...&lt;br /&gt;Last session of UKOUG - lots of people were already leaving home because of the weather conditions (and some returned because of the weather conditions) - that I attended was by &lt;a href="http://antognini.ch/blog/"&gt;Christian Antognini &lt;/a&gt;on Transaction Management Internals. Even though I'm a "hardcore" developer I have this fascination with the internal workings of Oracle and I really enjoyed this session. Finally I understand why and how Delayed Block Cleanout works :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-4713135816064677553?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/4713135816064677553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/12/my-first-ukoug-day-two-and-three.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4713135816064677553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4713135816064677553'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/12/my-first-ukoug-day-two-and-three.html' title='My First UKOUG - day two and three'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7535421633250365944</id><published>2010-12-06T15:24:00.002+01:00</published><updated>2010-12-06T15:41:17.029+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='UKOUG'/><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><title type='text'>My First UKOUG - day one</title><content type='html'>Last week the UKOUG-TEBS conference was in Birmingham. For whatever reason I had the idea that this conference was fairly small - no idea why I thought that.&lt;br /&gt;The conference was held in the ICC in Birmingham, a wonderful location.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The line up of speakers was very impressive (amongst others): Tom Kyte, Cary Millsap, Jonathan Lewis, Graham Wood, John Scott, and the list goes on and on and on...&lt;br /&gt;&lt;br /&gt;The first session I went to was by Tom Kyte. Even though I had already seen "What's new in Oracle Database Application Development" during the Oracle Open World, it is still a very good session. My other objective was to check out the room (after lunch on Monday, I had the same room).&lt;br /&gt;Next was "Simplifying your Data Audits with Oracle 11g's Total Recall" by Melanie Caffrey. In this session she described the way she used Total Recall (a.k.a. Flashback Data Archive) for data auditing. After the presentation there was enough room for discussion which was very valuable. Something I forgot, but was reminded of in this session: Total Recall underwent some in Oracle 11g Release 2, &lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17128/chapter1.htm#sthref11"&gt;like the support for DDL.&lt;/a&gt; &lt;br /&gt;Graham Wood did a session on "Features you probably didn't know and are FREE", which he refined to "mostly free". One of the things he mentioned was the "ADVANCED" argument of the Display_Cursor function in the DBMS_XPLAN package. Just a short demo:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; set serveroutput off&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; var empno number&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; exec :empno := 7788&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from emp&lt;br /&gt;  3   where empno = :empno&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO&lt;br /&gt;---------- ---------- --------- ---------- --------- ---------- ---------- ----------&lt;br /&gt;      7788 SCOTT      ANALYST         7566 09-DEC-82       3000                    20&lt;br /&gt;&lt;br /&gt;1 row selected.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from table (dbms_xplan.display_cursor (null, null, 'ADVANCED'))&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;-------------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;SQL_ID  0nb4mtjxa5k10, child number 0&lt;br /&gt;-------------------------------------&lt;br /&gt;select *   from emp  where empno = :empno&lt;br /&gt;&lt;br /&gt;Plan hash value: 3956160932&lt;br /&gt;&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |      |       |       |     2 (100)|          |&lt;br /&gt;|*  1 |  TABLE ACCESS FULL| EMP  |     1 |    37 |     2   (0)| 00:00:01 |&lt;br /&gt;--------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Query Block Name / Object Alias (identified by operation id):&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - SEL$1 / EMP@SEL$1&lt;br /&gt;&lt;br /&gt;Outline Data&lt;br /&gt;-------------&lt;br /&gt;&lt;br /&gt;  /*+&lt;br /&gt;      BEGIN_OUTLINE_DATA&lt;br /&gt;      IGNORE_OPTIM_EMBEDDED_HINTS&lt;br /&gt;      OPTIMIZER_FEATURES_ENABLE('10.2.0.1')&lt;br /&gt;      ALL_ROWS&lt;br /&gt;      OUTLINE_LEAF(@"SEL$1")&lt;br /&gt;      FULL(@"SEL$1" "EMP"@"SEL$1")&lt;br /&gt;      END_OUTLINE_DATA&lt;br /&gt;  */&lt;br /&gt;&lt;br /&gt;Peeked Binds (identified by position):&lt;br /&gt;--------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - :EMPNO (NUMBER): 7788&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - filter("EMPNO"=:EMPNO)&lt;br /&gt;&lt;br /&gt;Column Projection Information (identified by operation id):&lt;br /&gt;-----------------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - "EMPNO"[NUMBER,22], "EMP"."ENAME"[VARCHAR2,10],&lt;br /&gt;       "EMP"."JOB"[VARCHAR2,9], "EMP"."MGR"[NUMBER,22],&lt;br /&gt;       "EMP"."HIREDATE"[DATE,7], "EMP"."SAL"[NUMBER,22],&lt;br /&gt;       "EMP"."COMM"[NUMBER,22], "EMP"."DEPTNO"[NUMBER,22]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;49 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice all the extra information that you get, very cool.&lt;br /&gt;After Lunch I had my own session on Analytic Functions. It took place in Hall One - one of the largest rooms on site (still very impressed with that). Had some nice feedback, &lt;a href="http://boneist-oracle.livejournal.com/"&gt;thank you Boneist&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://debrasoracle.blogspot.com/"&gt;Debra Lilley&lt;/a&gt; taped&lt;a href="http://www.youtube.com/watch?v=9VLUSTg-1o8"&gt; the first two minutes of my session&lt;/a&gt;,... nervous: yes&lt;br /&gt;Unfortunately Pete Finnigan's session was cancelled, but that gave me some room to visit the OTN lounge and talk to some people.&lt;br /&gt;That SQL is a weird and wonderful language I already knew, but in the session of Carl Dudley he put all of it together in a single session. &lt;br /&gt;Last show of the day was &lt;a href="http://www.youtube.com/watch?v=9zW0Jn9bv4Y"&gt;the Panto... &lt;/a&gt;&lt;br /&gt;Later day two and three.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7535421633250365944?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7535421633250365944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/12/my-first-ukoug-day-one.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7535421633250365944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7535421633250365944'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/12/my-first-ukoug-day-one.html' title='My First UKOUG - day one'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7242708360714056654</id><published>2010-11-03T10:44:00.005+01:00</published><updated>2010-11-03T10:56:14.090+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Change SYSDATE for testing</title><content type='html'>This morning I had some free time, so I was playing around with a little APEX 4 plugin. Probably the most simple plugin that you can imagine, but that is not what this post is about, or at least not mainly.&lt;br /&gt;The plugin shows the current date (or I should say: sysdate) as a region on an APEX page.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/TNEv3W4840I/AAAAAAAAA6E/bn8QofYjsfc/s1600/Nov.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 158px; height: 177px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/TNEv3W4840I/AAAAAAAAA6E/bn8QofYjsfc/s320/Nov.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5535258045098812226" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Of course I needed to make sure that it works as SYSDATE changes. Of course I am not refering to the changing of the time component of SYSDATE.&lt;br /&gt;To change SYSDATE you can use this command. Note that it changes SYSDATE for the whole system.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter system set fixed_date ='2011-08-05'&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Querying SYSDATE from SQL*Plus will show the effect:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select sysdate&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;SYSDATE&lt;br /&gt;---------&lt;br /&gt;05-AUG-11&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Also note that SYSDATE is now fixed, not even the time component changes:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter session set nls_date_format = 'dd-mm-yyyy hh24:mi:ss'&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select sysdate&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;SYSDATE&lt;br /&gt;-------------------&lt;br /&gt;05-08-2011 00:00:00&lt;br /&gt;&lt;br /&gt;SQL&gt; /&lt;br /&gt;&lt;br /&gt;SYSDATE&lt;br /&gt;-------------------&lt;br /&gt;05-08-2011 00:00:00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Refreshing the APEX page also reflects the changed SYSDATE.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/TNEw9i69ldI/AAAAAAAAA6M/GUshzi96DCE/s1600/Aug.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 161px; height: 179px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/TNEw9i69ldI/AAAAAAAAA6M/GUshzi96DCE/s320/Aug.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5535259250919314898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To restore the real SYSDATE, issue this command.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter system set fixed_date = none&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;System altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; select sysdate&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;SYSDATE&lt;br /&gt;-------------------&lt;br /&gt;03-11-2010 10:53:40&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I knew about this functionality, but never thought I would actually need it.&lt;br /&gt;If you want the plugin, you can download it by&lt;a href="http://www.apex-plugin.com/oracle-apex-plugins/region-plugin/today's-date.html"&gt; following this link&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7242708360714056654?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7242708360714056654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/11/change-sysdate-for-testing.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7242708360714056654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7242708360714056654'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/11/change-sysdate-for-testing.html' title='Change SYSDATE for testing'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_tdqazgf19_s/TNEv3W4840I/AAAAAAAAA6E/bn8QofYjsfc/s72-c/Nov.png' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2874490570420578847</id><published>2010-11-01T16:14:00.004+01:00</published><updated>2011-12-12T18:13:17.884+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='EBR'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>Edition Based Redefinition and USER_OBJECTS_AE</title><content type='html'>Last week the Oracle PL/SQL Programming (OPP) and APEXposed conference took place in Brussels, Belgium.&lt;br /&gt;&lt;br /&gt;My session was on Edition Based Redefinition, &lt;i&gt;the&lt;/i&gt; killer feature of the Oracle 11&lt;i&gt;g&lt;/i&gt; Release 2 database. One of my demo's showed what a procedure looks like when you override it in a newer edition from the datadictionary standpoint.&lt;br /&gt;In earlier releases of the Oracle database it was not possible to have two procedures (or any other object as a matter of fact) with the same name in the same database schema.&lt;br /&gt;With Edition Based Redefinition you can have two procedures with the same name, as long as they are in different Edtions.&lt;br /&gt;For this demo I will use the new datadictionary view USER_OBJECTS_AE. &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Let's first start within the default edition (the one you get when you upgrade to or install an Oracle 11&lt;i&gt;g&lt;/i&gt; Release 2 database) called ORA$BASE.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select sys_context('userenv'&lt;br /&gt;  2                    ,'current_edition_name'&lt;br /&gt;  3                    ) "Current_Edition"&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Current_Edition&lt;br /&gt;--------------------&lt;br /&gt;ORA$BASE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Within this edition we will create a procedure called "hello" which simple shows "hello world".&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  procedure hello&lt;br /&gt;  3  is&lt;br /&gt;  4  begin&lt;br /&gt;  5     dbms_output.put_line ('Hello World');&lt;br /&gt;  6  end hello;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     hello;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;Hello World&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Querying the USER_OBJECT_AE datadictionary view results in a single record&lt;br /&gt;&lt;pre clas="brush: sql"&gt;&lt;br /&gt;SQL&gt; select object_name&lt;br /&gt;  2       , object_type&lt;br /&gt;  3       , edition_name&lt;br /&gt;  4    from user_objects_ae&lt;br /&gt;  5   where object_name = 'HELLO'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OBJECT_NAME          OBJECT_TYPE         EDITION_NAME&lt;br /&gt;-------------------- ------------------- -----------------&lt;br /&gt;HELLO                PROCEDURE           ORA$BASE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Introducing a new edition as a child of ORA$BASE, in which we will create a "newer" version of the "hello"-procedure.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create edition R1 as child of ora$base&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Edition created.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter session set edition = R1&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Session altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When we call the "hello"-procedure in this new edition (named R1), we get the same result as before. The procedure is inherited from the parent edition (named ORA$BASE).&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     hello;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;Hello World&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The datadictionary view USER_OBJECTS_AE shows just a single version of the "hello"-procedure. This more or less proofs that the procedure is not copied over to the new version (named R1). There is still only one version of the procedure.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select object_name&lt;br /&gt;  2       , object_type&lt;br /&gt;  3       , edition_name&lt;br /&gt;  4    from user_objects_ae&lt;br /&gt;  5   where object_name = 'HELLO'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OBJECT_NAME          OBJECT_TYPE         EDITION_NAME&lt;br /&gt;-------------------- ------------------- ------------&lt;br /&gt;HELLO                PROCEDURE           ORA$BASE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Creating a new version of the "hello"-procedure will -of course- produce a different result when executed.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  procedure hello&lt;br /&gt;  3  is&lt;br /&gt;  4  begin&lt;br /&gt;  5     dbms_output.put_line ('Hello Universe');&lt;br /&gt;  6  end hello;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Procedure created.&lt;br /&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     hello;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;Hello Universe&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This new version of the procedure will override the inherited version of the ORA$BASE edition. This is also reflected in the USER_OBJECTS_AE view:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; select object_name&lt;br /&gt;  2       , object_type&lt;br /&gt;  3       , edition_name&lt;br /&gt;  4    from user_objects_ae&lt;br /&gt;  5   where object_name = 'HELLO'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OBJECT_NAME          OBJECT_TYPE         EDITION_NAME&lt;br /&gt;-------------------- ------------------- ------------------&lt;br /&gt;HELLO                PROCEDURE           ORA$BASE&lt;br /&gt;HELLO                PROCEDURE           R1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you can see there are two versions of the procedure named "hello", one in each edition.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Drop the Procedure&lt;/b&gt;&lt;br /&gt;When you create a new edition, all editionable objects are inherited into the latest edition. If you don't want an object to "be there" in the latest edition, simply drop it. It won't exist anymore in the latest edition. It will exist in the older edition though.&lt;br /&gt;Instead of creating the newer procedure in the latest edition (named R1), dropping it will also lead to an entry in the USER_OBJECTS_AE view.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; drop procedure hello&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Procedure dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt; select object_name&lt;br /&gt;  2       , object_type&lt;br /&gt;  3       , edition_name&lt;br /&gt;  4    from user_objects_ae&lt;br /&gt;  5   where object_name = 'HELLO'&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;OBJECT_NAME          OBJECT_TYPE         EDITION_NAME&lt;br /&gt;-------------------- ------------------- -------------&lt;br /&gt;HELLO                PROCEDURE           ORA$BASE&lt;br /&gt;HELLO                NON-EXISTENT        R1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see from the results, the procedure "hello" doesn't exist any more in the latest edition (named  R1). The object_type "NON-EXISTENT" gives that away.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2874490570420578847?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2874490570420578847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/11/edition-based-redefinition-and.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2874490570420578847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2874490570420578847'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/11/edition-based-redefinition-and.html' title='Edition Based Redefinition and USER_OBJECTS_AE'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2109585288735508500</id><published>2010-10-10T12:01:00.000+02:00</published><updated>2010-10-10T12:01:00.495+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Inline View Check Option</title><content type='html'>Something I didn't know was possible, the "With Check Option" with an inline view. I knew this was possible with a "regular" view , but with an inline view...&lt;br /&gt;The Check Option prohibits changes to the table (through the view) which would produce row that are not included in the view.&lt;br /&gt;Let's start with a sample of how the Check Option works.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;It is possible to perform DML operations using a view (&lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17118/statements_8004.htm#SQLRF01504" target="_blank"&gt;some restrictions apply&lt;/a&gt;), as shown in the next example.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table temp&lt;br /&gt;  2  (x int);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into temp values (1);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into temp values (100);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; create view vw_temp&lt;br /&gt;  2  as&lt;br /&gt;  3  select *&lt;br /&gt;  4    from temp&lt;br /&gt;  5   where x &lt;= 10&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;View created.&lt;br /&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from vw_temp&lt;br /&gt;  3  ;&lt;br /&gt;&lt;br /&gt;         X&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into vw_temp values (2);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into vw_temp values (102);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from vw_temp&lt;br /&gt;  3  ;&lt;br /&gt;&lt;br /&gt;         X&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;         2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What is quite noticable is that it is possible to insert records using the view which are not be visible through the view. To prevent this behaviour you can use the Check Option.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; drop view vw_temp;&lt;br /&gt;&lt;br /&gt;View dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; create view vw_temp&lt;br /&gt;  2  as&lt;br /&gt;  3  select *&lt;br /&gt;  4    from temp&lt;br /&gt;  5   where x &lt;= 10&lt;br /&gt;  6   with check option&lt;br /&gt;  7  ;&lt;br /&gt;&lt;br /&gt;View created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from vw_temp&lt;br /&gt;  3  ;&lt;br /&gt;&lt;br /&gt;         X&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;         2&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; insert into vw_temp values (2);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into vw_temp values (102);&lt;br /&gt;insert into vw_temp values (102)&lt;br /&gt;            *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-01402: view WITH CHECK OPTION where-clause violation&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, with the Check Option in place it is not possible to do DML through the view if the records are not visible through the view in the first place. The value "102" would never show up in the result set when querying the view. Simply because the view has the predicate:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;where x &lt;= 10&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This functionality has been around for a very, very long time.&lt;br /&gt;What I didn't know, until recently, is that the same Check Option is also available with Inline Views.&lt;br /&gt;First I'll show the INSERT using an Inline View, next the INSERT using an Inline View with Check Option.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (x int)&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into (select x&lt;br /&gt;  2                 from test&lt;br /&gt;  3              )&lt;br /&gt;  4  values (100)&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into (select x&lt;br /&gt;  2                 from test&lt;br /&gt;  3                where x &lt;= 10&lt;br /&gt;  4              )&lt;br /&gt;  5  values (100)&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; insert into (select x&lt;br /&gt;  2                 from test&lt;br /&gt;  3                where x &lt;= 10&lt;br /&gt;  4                with check option&lt;br /&gt;  5              )&lt;br /&gt;  6  values (100)&lt;br /&gt;  7  /&lt;br /&gt;               from test&lt;br /&gt;                    *&lt;br /&gt;ERROR at line 2:&lt;br /&gt;ORA-01402: view WITH CHECK OPTION where-clause violation&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This also works well with bind variables, of course:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; var b number&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     :b := 100;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into (select x&lt;br /&gt;  2                 from test&lt;br /&gt;  3                where x &lt;= 10&lt;br /&gt;  4                with check option&lt;br /&gt;  5              )&lt;br /&gt;  6  values (:b)&lt;br /&gt;  7  /&lt;br /&gt;               from test&lt;br /&gt;                    *&lt;br /&gt;ERROR at line 2:&lt;br /&gt;ORA-01402: view WITH CHECK OPTION where-clause violation&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Something similar to the Check Option is available when you use Virtual Private Database (VPD).&lt;br /&gt;When you add a policy to implemnent your VPD, using the dbms_rls.add_policy procedure, one of the parameters is named "UPDATE_CHECK", which is similar to the Check Option.&lt;br /&gt;Records which are not visible when the VPD policy is applied can't be manipulated when you pass in TRUE for this parameter.&lt;br /&gt;&lt;br /&gt;All scripts in this post are executed in this version:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;Personal Oracle Database 11g Release 11.2.0.1.0 - Production&lt;br /&gt;PL/SQL Release 11.2.0.1.0 - Production&lt;br /&gt;CORE    11.2.0.1.0      Production&lt;br /&gt;TNS for 32-bit Windows: Version 11.2.0.1.0 - Production&lt;br /&gt;NLSRTL Version 11.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2109585288735508500?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2109585288735508500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/10/inline-view-check-option.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2109585288735508500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2109585288735508500'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/10/inline-view-check-option.html' title='Inline View Check Option'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3464389436343019749</id><published>2010-09-16T19:04:00.007+02:00</published><updated>2010-09-19T16:18:46.351+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OOW2010'/><title type='text'>Going to OOW2010 - Long Wednesday</title><content type='html'>Wednesday morning at 5 o'clock as the day begins, the alarm clock wakes me. Time to get out, take a shower and get ready to go to Oracle Open World. Not only my first Oracle Open World, but also the first time that I will be invited to the Oracle Ace Directors Briefing. Looking forward to an exciting week and a half in and around San Francisco.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Last time I went to the USA (ODTUG in Washington, DC) the kids had a hard time. Because I had to leave in the middle of night (around 03:00 AM) I had to say goodbye the night before. After that they had a hard time getting to sleep. This time I planned to do things different. They were coming with me to the train station, say goodbye there so at least they could get a good night sleep.&lt;br /&gt;It's been a while that I took the train anywhere, and this time I had the opportunity to go on a high speed train from Rotterdam to Schiphol Airport. "Because of the one year anniversity of Fyra (the train company) you will get a complimentary coffee". Excellent! Too bad I had to get off the train before I encountered the catering. Oh well,... The whole trip only took a little over an hour to get to the airport, nice.&lt;br /&gt;Meeting my colleague Lucas Jellema at the airport, getting some work done for our joint presentation.Meeting Lonneke Dikmans and Mike van Alst at the gate.&lt;br /&gt;The flight was smooth, planned on doing some work but watched movies instead.&lt;br /&gt;The view from the hotel room on the ninth is fantastic, overlooking a lake and Oracle HQ right behind that (I will post a picture later).&lt;br /&gt;Dinner was good, met up with Chris Muir, Richard Foote, Marcel Kratochvil, Lucas Jellema and Mike van Alst.&lt;br /&gt;Because it was only 08:30 PM (local time) I felt it was too early to go to bed so I joined Chris to the bar. Debra Lilley and some other Oracle Ace Directors but I really couldn't keep my eyes open. Time to call defeat and go to bed.&lt;br /&gt;Thursday morning at 5 o'clock (not local time, but time back home... it was only nine local time) I fell asleep.&lt;br /&gt;&lt;br /&gt;Update 18 September:&lt;br /&gt;Finally,... here's the photo:&lt;br /&gt;&lt;div style='text-align:center;margin:0px auto 10px;'&gt;&lt;a href="http://goo.gl/photos/G4Ov" imageanchor="1" style="clear:right;margin-bottom:1em;margin-left:1em"&gt;&lt;img border="0" src="http://lh4.ggpht.com/_tdqazgf19_s/TJVtQ33vcmI/AAAAAAAAAqY/TsMgB8ZhLS4/s512/DSC_0001.JPG"&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3464389436343019749?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3464389436343019749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/09/going-to-oow2010-long-wednesday.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3464389436343019749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3464389436343019749'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/09/going-to-oow2010-long-wednesday.html' title='Going to OOW2010 - Long Wednesday'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_tdqazgf19_s/TJVtQ33vcmI/AAAAAAAAAqY/TsMgB8ZhLS4/s72-c/DSC_0001.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3216438817613535432</id><published>2010-09-08T12:26:00.008+02:00</published><updated>2010-09-10T11:10:27.483+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OOW2010'/><title type='text'>Oracle OpenWorld, the countdown has begun</title><content type='html'>One week from now I am flying to San Francisco. Very excited to go as this will be my first Oracle OpenWorld.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;On Thursday and Friday I am going to attend the Director Briefing at Oracle HQ in Redwood. Another first. I did pass by the Oracle HQ before and looked at it and that’s as close as I got. &lt;br /&gt;&lt;br /&gt;There are so many sessions to choose from during the conference, it's going to be tough to figure out where to go. That’s going to be the chore for this afternoon.&lt;br /&gt;&lt;br /&gt;Some of the social events are already on my calendar.&lt;br /&gt;&lt;table border="1"&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Sunday, September 19&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;07:00 PM&lt;/td&gt;&lt;td&gt;Annual ACE dinner&lt;/td&gt;&lt;td&gt;Commodore Cruises, Pier 40&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Monday, September 20&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;04:00 - 05:00 PM&lt;/td&gt;&lt;td&gt;Ace Director and Java Champions Meet and Greet&lt;/td&gt;&lt;td&gt;Mason Street Tent&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;06:30 - 08:30 PM&lt;/td&gt;&lt;td&gt;Benelux Happy Hour&lt;/td&gt;&lt;td&gt;Restaurant Lulu, Folsom Street&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;07:30 - 10:30 PM&lt;/td&gt;&lt;td&gt;OTN Night Party&lt;/td&gt;&lt;td&gt;Howard Street Tent&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Tuesday, September 21&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;07:30 PM&lt;/td&gt;&lt;td&gt;APEX Meetup&lt;/td&gt;&lt;td&gt;4th Street Bar &amp; Deli&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;09:00 PM - 01:00 AM&lt;/td&gt;&lt;td&gt;Benelux Cocktail Party&lt;/td&gt;&lt;td&gt;Ruby Skye Club, 420 Mason Street&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Wednesday, September 22&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;05:30 PM&lt;/td&gt;&lt;td&gt;Bloggers Meetup&lt;/td&gt;&lt;td&gt;Lower Dining Room, Jillian’s Billiards @ Metreon, 101 Fourth Street&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;07:30 PM - 12:00 AM&lt;/td&gt;&lt;td&gt;Oracle Appreciation Event&lt;/td&gt;&lt;td&gt;Treasure Island&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;It’s going to be a busy week,... Now you know what I will be doing in the "off hours", hopefully we get a chance to meet face to face.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3216438817613535432?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3216438817613535432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/09/oracle-openworld-countdown-has-begun.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3216438817613535432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3216438817613535432'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/09/oracle-openworld-countdown-has-begun.html' title='Oracle OpenWorld, the countdown has begun'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6705062435438423438</id><published>2010-08-06T13:30:00.001+02:00</published><updated>2010-08-08T20:47:49.626+02:00</updated><title type='text'>Birthday Gift from Oracle: Ace Director</title><content type='html'>Yesterday was my birthday which usually starts off by being woken up by my children showering me with drawings they made. Yesterday was no exception.&lt;br /&gt;I also got an email from Oracle, a nice one I might add.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Alex-&lt;br /&gt;I'm happy to inform you that your Oracle ACE Director nomination submitted by ------- has been accepted.  The next step is confirming your understanding of Oracle's expectation as well as confirming the support of your company/management to participate. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;As requested by my nominator in this matter, the name has been left out. Thank you for nominating me - you know who you are. :)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://goo.gl/photos/7cKv" imageanchor="1" style="clear:right;margin-bottom:1em;margin-left:1em"&gt;&lt;img border="0" src="http://lh5.ggpht.com/_tdqazgf19_s/TFu6vHUFmqI/AAAAAAAAAo4/f0mKB3im5K0/s512/tekeningen.jpg"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6705062435438423438?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6705062435438423438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/08/birthday-gift-from-oracle-ace-director.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6705062435438423438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6705062435438423438'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/08/birthday-gift-from-oracle-ace-director.html' title='Birthday Gift from Oracle: Ace Director'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_tdqazgf19_s/TFu6vHUFmqI/AAAAAAAAAo4/f0mKB3im5K0/s72-c/tekeningen.jpg' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3370050182946256688</id><published>2010-08-05T13:00:00.004+02:00</published><updated>2010-08-05T13:50:56.018+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>APEX 4.0: Removing Plugins</title><content type='html'>Just a quick note so I won't forget, and maybe help you along the way.&lt;br /&gt;A little while ago I created my first APEX 4.0 plugin, which was a nice learning experience. Removing a plugin is (or at least to me) not as obvious as you might think.&lt;br /&gt;This blog entry is about removing an installed plugin.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Normally when you want to delete anything from APEX, you need to go in the EDIT mode of the item that you want to remove. At the top of the edit page there will be a few buttons at your disposal. Most of the time labelled something like "Cancel", "Apply Changes" and "Delete".&lt;br /&gt;The logical thing to do when you want to remove a plugin, is to go to the edit page of the plugin and push the "Delete" button. But,... &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/TFqHUI8_8eI/AAAAAAAAAns/5urmt8kVl2E/s1600/no_delete.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 45px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/TFqHUI8_8eI/AAAAAAAAAns/5urmt8kVl2E/s320/no_delete.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501858674856948194" /&gt;&lt;/a&gt;&lt;br /&gt;Where is the "Delete" button?&lt;br /&gt;Turns out that the "Delete" button is hidden from view when you are using the plugin in your application. And that makes sense. Say you are using my Watermark plugin on your page and you delete the plugin. What should happen to the item? Should it be deleted as well? Should it be changed to a text item? You decide, so APEX doesn't make this decision for you.&lt;br /&gt;On the right side of the Edit page (edit page for the plugin) there is a Task List. &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/TFqJTgjVAAI/AAAAAAAAAn0/vidqTxlZRW0/s1600/utilization.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 207px; height: 122px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/TFqJTgjVAAI/AAAAAAAAAn0/vidqTxlZRW0/s320/utilization.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501860863035113474" /&gt;&lt;/a&gt;&lt;br /&gt;In this task list is a link labelled "Utilization". Follow the link to get an overview of where the plugin is being used.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/TFqJsG0zP2I/AAAAAAAAAn8/DLUF3_AKb2g/s1600/overview.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 102px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/TFqJsG0zP2I/AAAAAAAAAn8/DLUF3_AKb2g/s320/overview.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501861285625806690" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now you can start changing the items that use the plugin, until the plugin is no longer used. After you have done this, the "Delete" button will be shown and the plugin can be removed.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/TFqKCYGckMI/AAAAAAAAAoE/iW_cTvHq_yo/s1600/delete.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 312px; height: 46px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/TFqKCYGckMI/AAAAAAAAAoE/iW_cTvHq_yo/s320/delete.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5501861668220342466" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tip:&lt;/b&gt; If you have Utilization Overview, open each reference in a separate Tab (assuming you're using tabbed browsing). After navigating from the overview to the edit page for the item, you are rerouted back to the Shared Components page. Getting back to the overview requires navigating to the Plugin page, the plugin you want to remove, using the Utilization link from the Task list and finally you are back on the overview page.. Lots of clicks. Easier to open an extra tab and refresh the Utilization overview.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3370050182946256688?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3370050182946256688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/08/apex-40-removing-plugins.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3370050182946256688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3370050182946256688'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/08/apex-40-removing-plugins.html' title='APEX 4.0: Removing Plugins'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_tdqazgf19_s/TFqHUI8_8eI/AAAAAAAAAns/5urmt8kVl2E/s72-c/no_delete.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6279993104186717634</id><published>2010-07-23T11:31:00.023+02:00</published><updated>2010-09-10T11:11:06.898+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='APEX'/><title type='text'>APEX 4.0 Plugin: Watermark</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/TEljBF4BCLI/AAAAAAAAAnU/vKt44MR3dTc/s1600/waterrmark.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 115px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/TEljBF4BCLI/AAAAAAAAAnU/vKt44MR3dTc/s320/waterrmark.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5497033690590546098" /&gt;&lt;/a&gt;&lt;br /&gt;This simple plugin for APEX 4.0 shows some text inside an item. Usually a watermark is used to give the user extra information to assist with filling out a Form. Creating this plugin was more a learning experience than anything else. If you just want the plugin than &lt;a href="http://www.apex-plugin.com/oracle-apex-plugins/item-plugin/watermark.html" target="_blank"&gt;follow this link&lt;/a&gt; to download it. If you want to go through the same learning experience I went through while creating this plugin, than keep reading.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Most of the APEX 4 Plugin tutorials that I encountered are structured like "click here", "copy this". To me this doesn't aid in my understanding of the plugin mechanism. Needless to say that plugin tutorials written for APEX 4 EA (Early Adapter) are less effective, since the interface for creating plugin has changed. Currently the only way to learn how to create a plugin is going through someone else's plugin code and try to figure out what it is they did. Which can be quite challenging.&lt;/br&gt;&lt;br /&gt;Enough for the rant. Time to get back to work.&lt;/br&gt;&lt;br /&gt;&lt;b&gt;Step 1. Download the jQuery plugin.&lt;/b&gt;&lt;br /&gt;For this plugin, I used the &lt;a href="http://digitalbush.com/projects/watermark-input-plugin/"&gt;Watermark jQuery plugin&lt;/a&gt;. On this page you can also see how the plugin should be used.&lt;br /&gt;&lt;b&gt;Step 2. Create a new plugin in APEX&lt;/b&gt;&lt;br /&gt;Go to the Shared Components of your application and choose "Plug-ins" in the "User Interface" section.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/TEmAyp-XroI/AAAAAAAAAnc/EVYuJLWSigU/s1600/plugin.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 231px; height: 224px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/TEmAyp-XroI/AAAAAAAAAnc/EVYuJLWSigU/s320/plugin.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5497066427931668098" /&gt;&lt;/a&gt;&lt;br /&gt;On the Plugin overview page, click the CREATE button. Fill out the "Name" and "Internal Name" items, choose "Item" for Type and click the CREATE button. Why click the button now? Well, it turns out that the type of plugin can reveal extra properties on this page. So, if you're follow other "click here - copy this"-tutorials make sure to click the CREATE button after you filled out the name, internal and type.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Name: Watermark&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Internal Name: nl.amis.watermark&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Type: Item&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Step 3: Upload the javascript library&lt;/b&gt;&lt;br /&gt;To upload the javascript, go to the files section of the plugin edit/create form, and follow the wizard to upload the javascript file. As easy as that. Why do you need to do this? The plugin contains all necessary information to be exported into other applications. As this plugin needs the javascript library, include it with the plugin.&lt;br /&gt;&lt;b&gt;Step 4: Custom Attributes&lt;/b&gt;&lt;br /&gt;What do you need to define the watermark? First of all some text, because that is the text that will be shown as the watermark. Second, the plugin allows you to define your own color instead of the standard light grey.&lt;br /&gt;Go to the section labelled "Custom Attributes" and create two attributes:&lt;br /&gt;First: &lt;ul&gt;&lt;br /&gt;&lt;li&gt;Scope: component&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Display sequence: 10&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Label: Watermark&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Type: Text&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Required: Yes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Help Text: The text that you want to appear as a Watermark in the item.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Second:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Scope: component&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Display sequence: 20&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Label: Color&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Type: Text&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Required: No&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Default Value: #369&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Help Text: The color of the Watermark. Default is #369, which is a light grey.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Why do you need to do this? You need to create some attributes to define the appearance of the plugin, in this case the text and the color of the text, giving some flexibility to the plugin.&lt;br /&gt;&lt;b&gt;Step 5: Create a Render function&lt;/b&gt;&lt;br /&gt;The Render function is the code that generates the plugin on the form. What it needs to render is determined by the type of plugin, in this case an Input item. The signature of the render function looks like this&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;function &amp;lt;name of function&gt; (&lt;br /&gt;    p_item                in apex_plugin.t_page_item,&lt;br /&gt;    p_plugin              in apex_plugin.t_plugin,&lt;br /&gt;    p_value               in varchar2,&lt;br /&gt;    p_is_readonly         in boolean,&lt;br /&gt;    p_is_printer_friendly in boolean )&lt;br /&gt;    return apex_plugin.t_page_item_render_result&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;Tip: To get the signature of the render function, click on the "Render Function Name" in the Callback section of the Plugin create/edit page.&lt;/i&gt;&lt;br /&gt;In order for the plugin to render correctly, you need to adhere to this signature.&lt;br /&gt;&lt;br /&gt;Using the HTP package, it's fairly easy to create an Input item.&lt;br /&gt;What you need to construct is something like this&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;sys.htp.p ('&amp;lt;input type="text" name="'||l_name&lt;br /&gt;           ||'" id="'||p_item.name||'" '&lt;br /&gt;           ||'value="'||l_value&lt;br /&gt;           ||'" size="'||p_item.element_width&lt;br /&gt;           ||'" '||'maxlength="'||p_item.element_max_length&lt;br /&gt;           ||'" '||p_item.element_attributes||' /&gt;');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Watch for the quotes and double quotes!&lt;br /&gt;&lt;br /&gt;In the code above there is a reference to a local variable, named "l_name". In order to get the name attribute of the input item, you need to call the Apex_Plugin package:&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;l_name := apex_plugin.get_input_name_for_page_item (false);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The APEX_PLUGIN is actually a synonym for the WWV_FLOW_PLUGIN package. Reading the note from the package specification shows that you need this function before using the HTP package:&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;--==============================================================================&lt;br /&gt;-- Returns the name attribute which has to be used for a HTML input element if&lt;br /&gt;-- you want that the value of the element is stored in session state when the&lt;br /&gt;-- page is submitted. If you have a HTML input element which returns multiple&lt;br /&gt;-- values (eg. select list with multiple="multiple") you have&lt;br /&gt;-- to set p_is_multi_value.&lt;br /&gt;-- Note: This function has to be called before you write something to the&lt;br /&gt;--       HTTP buffer with HTP.P(RN)&lt;br /&gt;--==============================================================================&lt;br /&gt;function get_input_name_for_page_item (&lt;br /&gt;    p_is_multi_value in boolean )&lt;br /&gt;    return varchar2;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Two more things to do in constructing the rendering function, include the javascript library and write the jQuery code to "attach" the watermark to the item.&lt;br /&gt;Including the javascript library is as easy as calling a package,... &lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;apex_javascript.add_library (p_name      =&gt; 'jquery.watermarkinput'&lt;br /&gt;                            ,p_directory =&gt; p_plugin.file_prefix&lt;br /&gt;                            ,p_version   =&gt; null&lt;br /&gt;                            );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;Note: Do not include the extension of the javascript library. In other words: omit the ".js" extension.&lt;/i&gt;&lt;br /&gt;"Attaching" (by lack of better term) the watermark to the item, we need to refer to the documentation that goes with the Watermark plugin. This is what the render function needs to generate as "on Load" code:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;jQuery(function($){&lt;br /&gt;   $("#suffix").Watermark("Suffix","#369");&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using the Apex_Javascript package allows you to add this code to the "document ready" function. If you are not familiar with this term, forget about it. Just remember that the jQuery code is executed after the page is shown and all good things happen there. Want to know more? Google: "jQuery document ready" and you will find lots of tutorials on using this function.&lt;br /&gt;The only thing we need to generate is the line which starts with the dollar ($) sign.&lt;br /&gt;Something like this:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;apex_javascript.add_onload_code (p_code =&gt;&lt;br /&gt;      '$("#'||p_item.name||'").Watermark("'||l_watermark||'","'||l_color||'");'&lt;br /&gt;      );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Watch those quotes and double quotation marks again.&lt;br /&gt;&lt;br /&gt;Now the complete rendering function looks like this, including some debug information. Notice the use of the Custom Attributes (defined as p_item.attribute_01 and p_item.attribute_02):&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;function render_watermark (p_item                in apex_plugin.t_page_item&lt;br /&gt;                          ,p_plugin              in apex_plugin.t_plugin&lt;br /&gt;                          ,p_value               in varchar2&lt;br /&gt;                          ,p_is_readonly         in boolean&lt;br /&gt;                          ,p_is_printer_friendly in boolean&lt;br /&gt;                          )&lt;br /&gt;   return apex_plugin.t_page_item_render_result&lt;br /&gt;is&lt;br /&gt;   l_value varchar2(32767) := sys.htf.escape_sc (p_value);&lt;br /&gt;   l_name  varchar2(30);&lt;br /&gt;   l_watermark apex_application_page_items.attribute_01%type := p_item.attribute_01;&lt;br /&gt;   l_color     apex_application_page_items.attribute_02%type := coalesce (p_item.attribute_02, '#369');&lt;br /&gt;   retval apex_plugin.t_page_item_render_result;&lt;br /&gt;begin&lt;br /&gt;   if apex_application.g_debug&lt;br /&gt;   then&lt;br /&gt;      apex_plugin_util.debug_page_item (p_plugin    =&gt; p_plugin&lt;br /&gt;                                       ,p_page_item =&gt; p_item&lt;br /&gt;                                       );&lt;br /&gt;   end if;&lt;br /&gt;   l_name := apex_plugin.get_input_name_for_page_item (false);&lt;br /&gt;   if p_is_readonly or p_is_printer_friendly&lt;br /&gt;   then&lt;br /&gt;      if p_is_readonly and not p_is_printer_friendly&lt;br /&gt;      then         &lt;br /&gt;         sys.htp.p ('&amp;lt;input type="hidden" name="'||l_name||'" '||&lt;br /&gt;                    'id="'||p_item.name||'" value="'||l_value||'" /&gt;'&lt;br /&gt;                   );&lt;br /&gt;      end if;&lt;br /&gt;      sys.htp.p ('&amp;lt;span id="'||p_item.name||'_DISPLAY" '||&lt;br /&gt;                 coalesce (p_item.element_attributes, 'class="display_only"')||&lt;br /&gt;                 '&gt;'||l_value||'&lt;/span&gt;'&lt;br /&gt;                );&lt;br /&gt;   else&lt;br /&gt;      sys.htp.p ('&amp;lt;input type="text" name="'||l_name||'" id="'||p_item.name||'" '||&lt;br /&gt;                  'value="'||l_value||'" size="'||p_item.element_width||'" '||&lt;br /&gt;                  'maxlength="'||p_item.element_max_length||'" '||&lt;br /&gt;                  p_item.element_attributes||' /&gt;');&lt;br /&gt;      apex_javascript.add_library (p_name      =&gt; 'jquery.watermarkinput'&lt;br /&gt;                                  ,p_directory =&gt; p_plugin.file_prefix&lt;br /&gt;                                  ,p_version   =&gt; null&lt;br /&gt;                                  );&lt;br /&gt;      apex_javascript.add_onload_code (p_code =&gt;&lt;br /&gt;      '$("#'||p_item.name||'").Watermark("'||l_watermark||'","'||l_color||'");'&lt;br /&gt;      );&lt;br /&gt;   end if;&lt;br /&gt;   return retval;&lt;br /&gt;end render_watermark;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I also set some standard attributes as depicted below.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/TEmvWLO1cyI/AAAAAAAAAnk/qBD7UblVJIs/s1600/standard_attr.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 86px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/TEmvWLO1cyI/AAAAAAAAAnk/qBD7UblVJIs/s320/standard_attr.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5497117615689397026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Step 6: Validation&lt;/b&gt;&lt;br /&gt;Now we are almost done. You could try it out now. But there is a catch,... What do you think gets stored in the database if you don't enter a value for the item? Exactly, the watermark text that you provide. In order to prevent that from happening, I searched for ways to use the functionality that the Watermark plugin provides.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;function UseData(){&lt;br /&gt;   $.Watermark.HideAll();&lt;br /&gt;   //Do Stuff&lt;br /&gt;   $.Watermark.ShowAll();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But was unable to find the correct location where to add this. So I decided to implement a "validation" function.&lt;br /&gt;In the Callback section, where you also specify the render function, add a validation function - in this case call it "validate_watermark".&lt;br /&gt;The function is not really a validation functionality, the only thing that it does is set the session state for the item to NULL - only when the value of the item is equal to the watermark text.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;function validate_watermark (p_item   in apex_plugin.t_page_item&lt;br /&gt;                            ,p_plugin in apex_plugin.t_plugin&lt;br /&gt;                            ,p_value  in varchar2&lt;br /&gt;                            )&lt;br /&gt;   return apex_plugin.t_page_item_validation_result&lt;br /&gt;is&lt;br /&gt;   retval apex_plugin.t_page_item_validation_result;&lt;br /&gt;   l_watermark    apex_application_page_items.attribute_01%type := p_item.attribute_01;   &lt;br /&gt;begin&lt;br /&gt;   if apex_application.g_debug&lt;br /&gt;   then&lt;br /&gt;      apex_plugin_util.debug_page_item (p_plugin    =&gt; p_plugin&lt;br /&gt;                                       ,p_page_item =&gt; p_item&lt;br /&gt;                                       );&lt;br /&gt;   end if;&lt;br /&gt;   if p_value = l_watermark&lt;br /&gt;   then&lt;br /&gt;      -- Set session state to NULL for the item&lt;br /&gt;      apex_util.set_session_state(p_item.name, null);&lt;br /&gt;   end if;&lt;br /&gt;   return retval;&lt;br /&gt;end validate_watermark;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Creating a simple plugin like this one is not very hard. Writing this blog took a lot more time than creating the actual plugin. The hardest part was trying to figure out what exactly is needed to render the item and how to fit the jQuery samples in the PL/SQL code.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Links&lt;/b&gt;&lt;br /&gt;&lt;a href="http://digitalbush.com/projects/watermark-input-plugin/" target="_blank"&gt;Watermark by Josh Bush (Digital Bush)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://html-color-codes.info/" target="_blank"&gt;Easy reference for HTML Color Codes&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.apex-plugin.com/" target="_blank"&gt;A lot more plugins for APEX&lt;/a&gt;&lt;br /&gt;&lt;a href="http://jquery.com/" target="_blank"&gt;jQuery homepage&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6279993104186717634?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6279993104186717634/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/07/apex-40-plugin-watermark.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6279993104186717634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6279993104186717634'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/07/apex-40-plugin-watermark.html' title='APEX 4.0 Plugin: Watermark'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_tdqazgf19_s/TEljBF4BCLI/AAAAAAAAAnU/vKt44MR3dTc/s72-c/waterrmark.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6319746848058899282</id><published>2010-07-11T12:00:00.001+02:00</published><updated>2010-07-12T20:49:22.399+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Connecting Without TNSNames With EZConnect</title><content type='html'>On my laptop I run two databases, Oracle 10g Release 2 and Oracle 11g Release 2. Next to my regular consulting work, I also give &lt;a href="http://www.amis.nl/oracle-7up" target="_blank"&gt;an Oracle Advanced SQL and PL/SQL training called "7Up"&lt;/a&gt;. This training covers "all" new features since Oracle7.&lt;br /&gt;I tried using VM for having multiple versions of the database, but my laptop would grind to a halt.&lt;br /&gt;Anyway, usually I use SQL*Plus for all my demo's - SQL*Plus Windows that is. As you may know, SQL*Plus Windows vanished from the Oracle 11g database.&lt;br /&gt;Until now I was using SQL*Plus commandline instead, and that was fine.&lt;br /&gt;Last week I received the latest Oracle Magazine (July/August 2010) at home and in it was the AskTom column on connecting without TNSNames.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Just having a single shortcut on my desktop to connect to either the Oracle 10g database or the Oracle 11g database would make it easier to switch databases and show the differences between the two. I like the SQL*Plus Windows a little bit better over the SQL*Plus commandline version, it just feels more natural. I tried using SQL Developer for the demo's, but found zooming in and out to show snippets of SQL too distracting from the actual statement.&lt;br /&gt;Because I probably forget what to use as the connect string, I had to write this down somewhere. And since this is my "Things I got to remember" blog,.. here it is:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; conn alex/alex@//localhost/orcl11&lt;br /&gt;Connected.&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;------------------------------------------------------------&lt;br /&gt;Personal Oracle Database 11g Release 11.2.0.1.0 - Production&lt;br /&gt;PL/SQL Release 11.2.0.1.0 - Production&lt;br /&gt;CORE    11.2.0.1.0      Production&lt;br /&gt;TNS for 32-bit Windows: Version 11.2.0.1.0 - Production&lt;br /&gt;NLSRTL Version 11.2.0.1.0 - Production&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This works because I have EZCONNECT in my SQLNet.Ora:&lt;br /&gt;&lt;pre  class="brush: sql"&gt;&lt;br /&gt;NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Credit goes to Tom Kyte, of course.&lt;br /&gt;&lt;b&gt;Links&lt;/b&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/technology/oramag/oracle/10-jul/o40asktom.html" target="_blank"&gt;Oracle Magazine: On Connecting, Pivoting, and Learning New Tricks&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6319746848058899282?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6319746848058899282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/07/connecting-without-tnsnames.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6319746848058899282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6319746848058899282'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/07/connecting-without-tnsnames.html' title='Connecting &lt;strike&gt;Without TNSNames&lt;/strike&gt; With EZConnect'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1530429550167774467</id><published>2010-07-05T16:11:00.002+02:00</published><updated>2010-07-05T16:46:21.859+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='ODTUG Kaleidoscope 2010'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>Looking back at ODTUG Kaleidoscope 2010</title><content type='html'>In case you missed it (how is that possible?) last week was the annual ODTUG conference, Kaleidoscope in Washington DC. In this post I will share some of, what I consider, the highlights of this event. &lt;br /&gt;I always enjoy going to this conference, it's nice to see old friends and make some new ones.&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;During the Sunday symposium I attended "APEX: From A to Z". During this full day several presentations showed each step in creating an Application with APEX. They (the presentors) used the same application each time, continuing where the previous speaker stopped and introducing their own subject.&lt;br /&gt;All aspects of application development were covered this day, from Modelling to Globalization.&lt;/br&gt;&lt;br /&gt;&lt;/br&gt;&lt;br /&gt;&lt;b&gt;Monday&lt;/b&gt;&lt;br /&gt;On Monday, the regular sessions started. During the General Session the yearly awards were announced, I am proud to say that I was nominated for "Best Paper" and Cary Millsap won the award. Congratulations, Cary!&lt;/br&gt;&lt;br /&gt;The APEX keynote was slightly disappointing, it was more &lt;a href="http://www.goenglish.com/preachingtothechoir.asp" target="_blank"&gt;"preaching to the choir"&lt;/a&gt;. Paul Broughton did a session called "APEX through the eyes of a beginner" in which he told his first experience with APEX, he was quite nervous. He did a good job in sharing his story.&lt;/br&gt;&lt;br /&gt;&lt;a href="http://c2anton.blogspot.com/" target="_blank"&gt;Anton Nielsen &lt;/a&gt;at the end did a session called "APEX: A Window into the Oracle database" which was more about the Oracle database than it was about APEX. I really enjoyed this session, probably because it was different than other APEX sessions. Most APEX sessions focus on the user interface of APEX, this session showed how to leverage Oracle Database functionality with APEX. Or was it because of his plug for my own session? ;)&lt;/br&gt;&lt;br /&gt;&lt;/br&gt;&lt;br /&gt;&lt;b&gt;Tuesday&lt;/b&gt;&lt;br /&gt;&lt;a href="http://www.optimizermagic.blogspot.com/"&gt;Maria Colgan&lt;/a&gt; talked for an hour about the Explain Plan. This session gave a nice introduction into reading the Explain Plan and how to make sense of it. She also gave some pointers as where to start to look in case you run into trouble.&lt;/br&gt;&lt;br /&gt;The vendor session that I attended,... we shall speak of it no more... very disappointing. Instead of showing what the product was about, it only stated some general remarks...&lt;/br&gt;&lt;br /&gt;"Using Collections in APEX: The Definitive Intro" by Raj Mattamal was very informative. The demo that Raj did showed the power of collections. He could have explained some of the concepts a little better though.&lt;/br&gt;&lt;br /&gt;Another session by Maria Colgan which was also very good. This time it was on "Finally Plan Stability During Database Upgrade with SQL Plan Management". During this session she explained the concepts of SQL Plan Management.&lt;br /&gt;The last session of the day was by &lt;a href="http://jonathanlewis.wordpress.com/"&gt;Jonathan Lewis&lt;/a&gt; "Co-operating with the Database". In this session he explained the "good and bad" on bind variables and the limitations of "what Oracle can do for you" and how you (as a developer) can help Oracle to respond efficiently.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Wednesday&lt;/b&gt;&lt;br /&gt;After my own session, I attended "Thinking Clearly About Performance" by &lt;a href="http://carymillsap.blogspot.com/"&gt;Cary Millsap&lt;/a&gt;. I like the way Cary does his presentations, even though I've heard this presentation before, I go there for the "show". &lt;/br&gt;&lt;br /&gt;The next one was "Performance Instrumentation" by Karen Morton. This session was mainly about the &lt;a href="http://sourceforge.net/projects/ilo/" target="_blank"&gt;ILO tool&lt;/a&gt; and Instrumentation in general. Previously the tool was called Hotsos-ILO and I've &lt;a href="http://technology.amis.nl/blog/2178/odtug-kaleidoscope-hotsos-instrumentation-library-for-oracle" target="_blank"&gt;written about this on a previous occasion.&lt;/a&gt;&lt;/br&gt;&lt;br /&gt;Next up was &lt;a href="http://wphilltech.com/"&gt;Tim St. Hilaire&lt;/a&gt; with "APEX and AJAX - where to start" which was a nice introduction into using AJAX within an APEX environment. Even though APEX 4.0 has recently been released, where a lot of AJAX-like things can be hidden from the developer, this overview makes it clear what is involved with AJAX calls.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Thursday&lt;/b&gt;&lt;br /&gt;On Thursday I planned to go to "Oracle Development Sins and how Hackers (Can) Abuse Them" - 3 parts.. unfortunately this serie got cancelled. I knew it ahead of time, and was struggling hard to find a good alternative. It boiled down to not being able to make my mind up and eventually not going to any session at all. Looking back, I should have attended "Adding Statistics to Oracle's Application Express" by Rory McClean... &lt;br /&gt;As this was the last day of the Kaleidoscope conference, &lt;a href="http://one-size-doesnt-fit-all.blogspot.com/"&gt;Chris (Muir)&lt;/a&gt; and myself went to the Mall to do the necessary shopping. Around 6 pm we met up with &lt;a href="http://debrasoracle.blogspot.com/"&gt;Debra Lilley &lt;/a&gt;and &lt;a href="http://www.wtfistheacevest.com/"&gt;Stanley&lt;/a&gt; to have a nice dinner at a Thai restaurant followed by an evening site seeing on the National Mall. Very nice.&lt;br /&gt;&lt;b&gt;Friday&lt;/b&gt;&lt;br /&gt;Getting ready to fly back home. But first the Soccergame which put us in a good mood to fly back: Netherlands - Brazil... &lt;br /&gt;&lt;/br&gt;&lt;br /&gt;To conclude: This was an excellent ODTUG Kaleidoscope. Hope to be there &lt;a href="http://www.shawmarketing.org/K11/landingpage.html"&gt;next year&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-1530429550167774467?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/1530429550167774467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/07/looking-back-at-odtug-kaleidoscope-2010.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1530429550167774467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1530429550167774467'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/07/looking-back-at-odtug-kaleidoscope-2010.html' title='Looking back at ODTUG Kaleidoscope 2010'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2833761888652110076</id><published>2010-06-14T11:41:00.006+02:00</published><updated>2010-06-14T12:07:13.730+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><title type='text'>Analytic Function bug and National Championship</title><content type='html'>Yesterday there were the "NL Masters" in my hometown Oosterhout, The Netherlands. This two day track and field event was the National Championship for Athletes in the Master Class. &lt;br /&gt;While volunteering (I was running around all day as a courier) I noticed a familiar name on the list. &lt;a href="http://toinevanbeckhoven.wordpress.com/" target="_blank"&gt;Toine van Beckhoven&lt;/a&gt;, currently ranked first in the &lt;a href="http://www.plsqlchallenge.com/" target="_blank"&gt;PL/SQL Challenge&lt;/a&gt;. Small world. Toine finished first on the 400m hurdles in his category and is now the official Dutch National Champion. Congratulations, Toine.&lt;br /&gt;Being involved in this event triggered a question regarding ranking. And we're back to analytic functions... ;)&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;In the Oracle Documentation it says that the DENSE_RANK is the Olympic Ranking:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;... or the maximum (LAST) dense rank (also called olympic rank).&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;The major difference between the RANK and the DENSE_RANK function is the way the hand out numbers after a draw is encountered.&lt;br /&gt;Taking Scott's EMP table as an example:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select ename&lt;br /&gt;  2       , sal&lt;br /&gt;  3       , rank () over (order by sal desc) rk&lt;br /&gt;  4       , dense_rank () over (order by sal desc) dr&lt;br /&gt;  5    from emp&lt;br /&gt;  6   where deptno = 20&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;ENAME             SAL         RK         DR&lt;br /&gt;---------- ---------- ---------- ----------&lt;br /&gt;SCOTT            3000          1          1&lt;br /&gt;FORD             3000          1          1&lt;br /&gt;JONES            2975          3          2&lt;br /&gt;ADAMS            1100          4          3&lt;br /&gt;SMITH             800          5          4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice how Scott and Ford have the same salary, and notice how the ranking in the last two columns differ. Jones with a salary of 2975 is either ranked third or second - depending on the use of RANK or DENSE_RANK.&lt;br /&gt;&lt;br /&gt;According to the officials I spoke to yesterday, if there is a draw and you have a split first place there will be no Silver medal. This would mean that the Olympic Ranking would be RANK and not DENSE_RANK.&lt;br /&gt;&lt;br /&gt;Probably I'm mistaken &lt;a href="http://nuijten.blogspot.com/2010/06/last-tuesday-we-had-odtug-preview-mini.html" target="_blank"&gt;(again)&lt;/a&gt; so would appreciate your comments and corrections... :)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/functions063.htm#SQLRF00641" target="_blank"&gt;Oracle Documentation&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2833761888652110076?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2833761888652110076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/06/analytic-function-bug-and-national.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2833761888652110076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2833761888652110076'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/06/analytic-function-bug-and-national.html' title='Analytic Function bug and National Championship'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7654009380651485629</id><published>2010-06-10T20:01:00.005+02:00</published><updated>2010-06-10T21:31:14.558+02:00</updated><title type='text'>Analytic Function bug?</title><content type='html'>Last Tuesday we had an ODTUG Preview mini-conference at our office in Nieuwegein, The Netherlands. Nine presentations with nine fine speakers. Too bad the session were in parallel, and thus you had to pick and choose which session to attend. Oh well, that's always the case with conferences, even tiny ones like this one.&lt;br /&gt;Because I'm doing a presentation during the &lt;a href="http://www.odtugkaleidoscope.com/" target="_blank"&gt;ODTUG Kaleidoscope 2010&lt;/a&gt;, I also did my presentation last Tuesday.&lt;br /&gt;The feedback I got was good, and I also got some pointers to make the presentation better.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;While changing one of the demo's, I rediscovered of what I believe to be a documentation bug. The demo is about showing the Windowing Clause with Analytic Functions. If you are not familiar with Analytic Functions, attend my session and you will know all about it... ;)&lt;br /&gt;Anyway, to create a running total using the EMP table use the SUM with the default Windowing Clause and you're good to go. You would have to know what the default Windowing Clause is in that case.&lt;br /&gt;The documentation states&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;If you omit the windowing_clause entirely, then the default is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Let's go back to the example, a running total on Scott's EMP table&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select ename&lt;br /&gt;  2       , sal&lt;br /&gt;  3       , sum (sal) over (order by ename) running_total&lt;br /&gt;  4    from emp&lt;br /&gt;  5   where deptno = 20&lt;br /&gt;  6   order by ename&lt;br /&gt;  7  ;&lt;br /&gt; &lt;br /&gt;ENAME            SAL RUNNING_TOTAL&lt;br /&gt;---------- --------- -------------&lt;br /&gt;ADAMS           1100          1100&lt;br /&gt;FORD            3000          4100&lt;br /&gt;JONES           2975          7075&lt;br /&gt;SCOTT           3000         10075&lt;br /&gt;SMITH            800         10875&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Because we omitted the Windowing Clause, we get the Default - range between unbounded preceding and current row.&lt;br /&gt;Being explicit about the Windowing Clause, we get the same results - just as we expect.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select ename&lt;br /&gt;  2       , sal&lt;br /&gt;  3       , sum (sal) over (order by ename&lt;br /&gt;  4                         range between unbounded preceding&lt;br /&gt;  5                                   and current row&lt;br /&gt;  6                        ) running_total&lt;br /&gt;  7    from emp&lt;br /&gt;  8   where deptno = 20&lt;br /&gt;  9   order by ename&lt;br /&gt; 10  ;&lt;br /&gt; &lt;br /&gt;ENAME            SAL RUNNING_TOTAL&lt;br /&gt;---------- --------- -------------&lt;br /&gt;ADAMS           1100          1100&lt;br /&gt;FORD            3000          4100&lt;br /&gt;JONES           2975          7075&lt;br /&gt;SCOTT           3000         10075&lt;br /&gt;SMITH            800         10875&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the short notation of the Default Windowing Clause also yields the same results&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select ename&lt;br /&gt;  2       , sal&lt;br /&gt;  3       , sum (sal) over (order by ename&lt;br /&gt;  4                         range unbounded preceding&lt;br /&gt;  5                        ) running_total&lt;br /&gt;  6    from emp&lt;br /&gt;  7   where deptno = 20&lt;br /&gt;  8   order by ename&lt;br /&gt;  9  ;&lt;br /&gt; &lt;br /&gt;ENAME            SAL RUNNING_TOTAL&lt;br /&gt;---------- --------- -------------&lt;br /&gt;ADAMS           1100          1100&lt;br /&gt;FORD            3000          4100&lt;br /&gt;JONES           2975          7075&lt;br /&gt;SCOTT           3000         10075&lt;br /&gt;SMITH            800         10875&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Why do I believe this to be a bug because of the definition of a Range Window.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;RANGE specifies the window as a logical offset.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;and this part:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;If value_expr evaluates to a numeric value, then the ORDER BY expr must be a numeric or DATE data type.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If value_expr evaluates to an interval value, then the ORDER BY expr must be a DATE data type&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Look back at the examples and notice that I didn't use a Numeric or DATE data type in the ORDER BY expression - I used a VARCHAR2 (ename is after all a VARCHAR2). And this is in contradiction with the documentation quotes from above.&lt;br /&gt;Using the Range Window - not the Default one - with the ename column raises an exception&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select ename&lt;br /&gt;  2       , sal&lt;br /&gt;  3       , sum (sal) over (order by ename&lt;br /&gt;  4                         range between 400 preceding&lt;br /&gt;  5                                   and 400 following&lt;br /&gt;  6                         ) running_total&lt;br /&gt;  7    from emp&lt;br /&gt;  8   where deptno = 20&lt;br /&gt;  9   order by sal&lt;br /&gt; 10  /&lt;br /&gt; &lt;br /&gt;select ename&lt;br /&gt;     , sal&lt;br /&gt;     , sum (sal) over (order by ename&lt;br /&gt;                       range between 400 preceding&lt;br /&gt;                                 and 400 following&lt;br /&gt;                       ) running_total&lt;br /&gt;  from emp&lt;br /&gt; where deptno = 20&lt;br /&gt; order by sal&lt;br /&gt; &lt;br /&gt;ORA-00902: invalid datatype&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I believe the Default Windowing Clause should be&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;rows between unbounded preceding and current row&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And to use the Default Windowing explicitly would show this:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select ename&lt;br /&gt;  2       , sal&lt;br /&gt;  3       , sum (sal) over (order by ename&lt;br /&gt;  4                         rows between unbounded preceding&lt;br /&gt;  5                                   and current row -- Should be the default Window Clause&lt;br /&gt;  6                        ) running_total&lt;br /&gt;  7    from emp&lt;br /&gt;  8   where deptno = 20&lt;br /&gt;  9   order by ename&lt;br /&gt; 10  ;&lt;br /&gt; &lt;br /&gt;ENAME            SAL RUNNING_TOTAL&lt;br /&gt;---------- --------- -------------&lt;br /&gt;ADAMS           1100          1100&lt;br /&gt;FORD            3000          4100&lt;br /&gt;JONES           2975          7075&lt;br /&gt;SCOTT           3000         10075&lt;br /&gt;SMITH            800         10875&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Maybe, probably, I misinterpret the Oracle Documentation. If you believe the Oracle Documentation is correct, can you try and explain it to me?&lt;br /&gt;I did make a note of it in the Oracle Documentation, you can add user comments, but it never made it past the moderators... I guess they don't agree with me. ;)&lt;br /&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/functions004.htm#SQLRF06174" target="_blank"&gt;Oracle Documentation on Analytic Functions&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7654009380651485629?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7654009380651485629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/06/last-tuesday-we-had-odtug-preview-mini.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7654009380651485629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7654009380651485629'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/06/last-tuesday-we-had-odtug-preview-mini.html' title='Analytic Function bug?'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2110438389602386316</id><published>2010-06-04T11:15:00.007+02:00</published><updated>2010-06-04T12:23:42.048+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Check your Datatype, also in Check Constraints</title><content type='html'>A question came up at the Oracle discussion forum on SQL and PL/SQL regarding not being able to see any data even though the table did contain records. My lesson from this question was to pay close attention to the datatypes and implicit conversions taking place. What still puzzles me is a way to detect the mistake made apart from looking closely.&lt;br /&gt;Anyway these queries didn't return any records:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SELECT * FROM problem WHERE solved = '0';&lt;br /&gt;SELECT * FROM problem WHERE solved = 0;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;As you can see in the query different predicates are used. One compares the "solved" column to a NUMBER and the other compares it to a CHAR.&lt;br /&gt;&lt;br /&gt;Without seeing the table definition a lot of things could be the cause of not being able to see any data. A reproducible test case would be nice.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; CREATE TABLE  "PROBLEM" &lt;br /&gt;  2     (   "PROBLEM_ID" NUMBER NOT NULL ENABLE, &lt;br /&gt;  3     "PROBLEM_DESCRIPTION" VARCHAR2(4000), &lt;br /&gt;  4     "ACTION_TAKEN" VARCHAR2(4000), &lt;br /&gt;  5     "TRANSACTION_ID" NUMBER NOT NULL ENABLE, &lt;br /&gt;  6     "SOLVED" NUMBER NOT NULL ENABLE, &lt;br /&gt;  7      CONSTRAINT "PROBLEM_PK" PRIMARY KEY ("PROBLEM_ID") ENABLE, &lt;br /&gt;  8      CONSTRAINT "SOLVED_VALUE" CHECK ( "SOLVED" IN ('1', '0')) enable&lt;br /&gt;  9     )&lt;br /&gt; 10  / &lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt;  &lt;br /&gt;SQL&gt;  &lt;br /&gt;SQL&gt; CREATE INDEX  "PROBLEM_IDX1" ON  "PROBLEM" ("TRANSACTION_ID")&lt;br /&gt;  2  / &lt;br /&gt;&lt;br /&gt;Index created.&lt;br /&gt;&lt;br /&gt;SQL&gt;  &lt;br /&gt;SQL&gt;  CREATE SEQUENCE   "PROBLEM_SEQ"  MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 41 CACHE 20 NOORDER  NOCYCLE&lt;br /&gt;  2  / &lt;br /&gt;&lt;br /&gt;Sequence created.&lt;br /&gt;&lt;br /&gt;SQL&gt;  &lt;br /&gt;SQL&gt; CREATE OR REPLACE TRIGGER  "BI_PROBLEM" &lt;br /&gt;  2    before insert on "PROBLEM"               &lt;br /&gt;  3    for each row  &lt;br /&gt;  4  begin   &lt;br /&gt;  5      select "PROBLEM_SEQ".nextval into :NEW.PROBLEM_ID from dual; &lt;br /&gt;  6  end; &lt;br /&gt;  7   &lt;br /&gt;  8  / &lt;br /&gt;&lt;br /&gt;Trigger created.&lt;br /&gt;&lt;br /&gt;SQL&gt; ALTER TRIGGER  "BI_PROBLEM" ENABLE&lt;br /&gt;  2  / &lt;br /&gt;&lt;br /&gt;Trigger altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into problem&lt;br /&gt;  2     (problem_id&lt;br /&gt;  3     ,problem_description&lt;br /&gt;  4     ,transaction_id&lt;br /&gt;  5     ,solved&lt;br /&gt;  6     )&lt;br /&gt;  7  values&lt;br /&gt;  8     (22&lt;br /&gt;  9     ,'the problem'&lt;br /&gt; 10     ,81&lt;br /&gt; 11     ,0&lt;br /&gt; 12     );&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt;    &lt;br /&gt;SQL&gt; insert into problem&lt;br /&gt;  2     (problem_id&lt;br /&gt;  3     ,problem_description&lt;br /&gt;  4     ,transaction_id&lt;br /&gt;  5     ,solved&lt;br /&gt;  6     )&lt;br /&gt;  7  values&lt;br /&gt;  8     (2&lt;br /&gt;  9     ,'There are'&lt;br /&gt; 10     ,81&lt;br /&gt; 11     ,0&lt;br /&gt; 12     );&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; SELECT * &lt;br /&gt;  2    FROM problem &lt;br /&gt;  3   WHERE solved = '0';&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT *&lt;br /&gt;  2    FROM problem&lt;br /&gt;  3   WHERE solved = 0;&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you have it, even though we created two records each with a 0 (zero) for the "solved" column, no rows were selected by either of the SELECT statements issued.&lt;br /&gt;How could this be possible?&lt;br /&gt;Let's have a look at the Explain plan for one of the statements:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from problem&lt;br /&gt;  3   where solved = 0&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;no rows selected&lt;br /&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from table (dbms_xplan.display_cursor)&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;SQL_ID  dz2r64h4m6f2z, child number 0&lt;br /&gt;-------------------------------------&lt;br /&gt;select *   from problem  where solved = 0&lt;br /&gt;&lt;br /&gt;Plan hash value: 4173894327&lt;br /&gt;&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation          | Name    | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT   |         |       |       |     1 (100)|          |&lt;br /&gt;|*  1 |  FILTER            |         |       |       |            |          |&lt;br /&gt;|*  2 |   TABLE ACCESS FULL| PROBLEM |     2 |  8086 |     3   (0)| 00:00:01 |&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Predicate Information (identified by operation id):&lt;br /&gt;---------------------------------------------------&lt;br /&gt;&lt;br /&gt;   1 - filter(NULL IS NOT NULL)&lt;br /&gt;   2 - filter("SOLVED"=0)&lt;br /&gt;&lt;br /&gt;Note&lt;br /&gt;-----&lt;br /&gt;   - dynamic sampling used for this statement&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The filter with operation id 1 in the Explain Plan shows "NULL IS NOT NULL". As this predicate is always false no data is shown. According to the &lt;a href="http://optimizermagic.blogspot.com/2008/06/why-are-some-of-tables-in-my-query.html" target="_blank"&gt;"Inside the Oracle Optimizer"-blog&lt;/a&gt; this effectively removes the table from the query.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;The "NULL IS NOT NULL" filter is a false constant predicate, that will prevent the table scan from even taking place.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;So because of the filter the data isn't shown, but why is the filter introduced? There must be something wrong here. In the past I've used similar queries on similar tables which all seemed to be working well. Why is this scenario different?&lt;br /&gt;Let's have a closer look at the table definition&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;CREATE TABLE  "PROBLEM" &lt;br /&gt;   (   "PROBLEM_ID" NUMBER NOT NULL ENABLE, &lt;br /&gt;   "PROBLEM_DESCRIPTION" VARCHAR2(4000), &lt;br /&gt;   "ACTION_TAKEN" VARCHAR2(4000), &lt;br /&gt;   "TRANSACTION_ID" NUMBER NOT NULL ENABLE, &lt;br /&gt;   "SOLVED" NUMBER NOT NULL ENABLE, &lt;br /&gt;    CONSTRAINT "PROBLEM_PK" PRIMARY KEY ("PROBLEM_ID") ENABLE, &lt;br /&gt;    CONSTRAINT "SOLVED_VALUE" CHECK ( "SOLVED" IN ('1', '0')) enable&lt;br /&gt;   )&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice anything unusual?&lt;br /&gt;No?...&lt;br /&gt;How about now&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;...&lt;br /&gt;   "SOLVED" NUMBER&lt;br /&gt;...&lt;br /&gt;  "SOLVED_VALUE" CHECK ( "SOLVED" IN ('1', '0'))&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice the quotes in the Check Constraint definition? The datatype of the column is NUMBER and the Check in the Check constraint uses CHAR.&lt;br /&gt;Could this be the cause? Let's verify&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; CREATE TABLE  "PROBLEM" &lt;br /&gt;  2     (   "PROBLEM_ID" NUMBER NOT NULL ENABLE, &lt;br /&gt;  3     "PROBLEM_DESCRIPTION" VARCHAR2(4000), &lt;br /&gt;  4     "ACTION_TAKEN" VARCHAR2(4000), &lt;br /&gt;  5     "TRANSACTION_ID" NUMBER NOT NULL ENABLE, &lt;br /&gt;  6     "SOLVED" NUMBER NOT NULL ENABLE, &lt;br /&gt;  7      CONSTRAINT "PROBLEM_PK" PRIMARY KEY ("PROBLEM_ID") ENABLE, &lt;br /&gt;  8      CONSTRAINT "SOLVED_VALUE" CHECK ( "SOLVED" IN (1, 0)) enable&lt;br /&gt;  9     )&lt;br /&gt; 10  / &lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into problem&lt;br /&gt;  2     (problem_id&lt;br /&gt;  3     ,problem_description&lt;br /&gt;  4     ,transaction_id&lt;br /&gt;  5     ,solved&lt;br /&gt;  6     )&lt;br /&gt;  7  values&lt;br /&gt;  8     (22&lt;br /&gt;  9     ,'the problem'&lt;br /&gt; 10     ,81&lt;br /&gt; 11     ,0&lt;br /&gt; 12     );&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select problem_description&lt;br /&gt;  2    from problem&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;PROBLEM_DESCRIPTION&lt;br /&gt;----------------------------------------------------------------------------&lt;br /&gt;the problem&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the filter is eliminated from the Explain Plan&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from table (dbms_xplan.display_cursor)&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;PLAN_TABLE_OUTPUT&lt;br /&gt;------------------------------------------------------------------------------&lt;br /&gt;SQL_ID  18wkx6g72p35j, child number 0&lt;br /&gt;-------------------------------------&lt;br /&gt;select problem_description   from problem&lt;br /&gt;&lt;br /&gt;Plan hash value: 3149918165&lt;br /&gt;&lt;br /&gt;-----------------------------------------------------------------------------&lt;br /&gt;| Id  | Operation         | Name    | Rows  | Bytes | Cost (%CPU)| Time     |&lt;br /&gt;-----------------------------------------------------------------------------&lt;br /&gt;|   0 | SELECT STATEMENT  |         |       |       |     3 (100)|          |&lt;br /&gt;|   1 |  TABLE ACCESS FULL| PROBLEM |     1 |  2002 |     3   (0)| 00:00:01 |&lt;br /&gt;-----------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Note&lt;br /&gt;-----&lt;br /&gt;   - dynamic sampling used for this statement&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Puzzling&lt;/strong&gt;&lt;br /&gt;Why does these queries give output, even with the original Check Constraint in place? The only difference is using bind variable instead of hard coded values... &lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; var b number&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     :b := 0;&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; select problem_description&lt;br /&gt;  2    from problem&lt;br /&gt;  3   where solved = :b&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PROBLEM_DESCRIPTION&lt;br /&gt;----------------------------------------&lt;br /&gt;the problem&lt;br /&gt;There are&lt;br /&gt;&lt;br /&gt;SQL&gt; select problem_description&lt;br /&gt;  2    from problem&lt;br /&gt;  3   where solved = to_char (:b)&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PROBLEM_DESCRIPTION&lt;br /&gt;----------------------------------------&lt;br /&gt;the problem&lt;br /&gt;There are&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Any suggestions?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Oracle 11g Release 2&lt;/strong&gt;&lt;br /&gt;As always, things change with different versions. Until now this code has been run on Oracle 10g:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; conn alex/alex@orcl&lt;br /&gt;Connected.&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;-------------------------------------------------------------&lt;br /&gt;Personal Oracle Database 10g Release 10.2.0.1.0 - Production&lt;br /&gt;PL/SQL Release 10.2.0.1.0 - Production&lt;br /&gt;CORE    10.2.0.1.0      Production&lt;br /&gt;TNS for 32-bit Windows: Version 10.2.0.1.0 - Production&lt;br /&gt;NLSRTL Version 10.2.0.1.0 - Production&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With Oracle 11g Release 2 the original queries (and original table definition) do return records:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;Personal Oracle Database 11g Release 11.2.0.1.0 - Production&lt;br /&gt;PL/SQL Release 11.2.0.1.0 - Production&lt;br /&gt;CORE    11.2.0.1.0      Production&lt;br /&gt;TNS for 32-bit Windows: Version 11.2.0.1.0 - Production&lt;br /&gt;NLSRTL Version 11.2.0.1.0 - Production&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT problem_description&lt;br /&gt;  2    FROM problem&lt;br /&gt;  3   WHERE solved = '0';&lt;br /&gt;&lt;br /&gt;PROBLEM_DESCRIPTION&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;the problem&lt;br /&gt;There are&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT problem_description&lt;br /&gt;  2    FROM problem&lt;br /&gt;  3   WHERE solved = 0;&lt;br /&gt;&lt;br /&gt;PROBLEM_DESCRIPTION&lt;br /&gt;--------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;the problem&lt;br /&gt;There are&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br /&gt;Read the whole thread &lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=1081762&amp;tstart=0" target="_blank"&gt; here&lt;/a&gt;.&lt;br /&gt;&lt;a href="http://optimizermagic.blogspot.com/2008/06/why-are-some-of-tables-in-my-query.html" target="_blank"&gt;Inside the Oracle Optimizer&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2110438389602386316?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2110438389602386316/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/06/check-your-datatype-also-in-check.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2110438389602386316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2110438389602386316'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/06/check-your-datatype-also-in-check.html' title='Check your Datatype, also in Check Constraints'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5640784827924329734</id><published>2010-04-07T09:20:00.017+02:00</published><updated>2010-11-01T16:40:18.080+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Developer'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='EBR'/><title type='text'>Oracle 11gR2: Editions and SQL Developer</title><content type='html'>I know the correct term is Edition Based Redefinition, but that would make the title of this blog post a bit too long.&lt;br /&gt;Over the last weekend Oracle 11gR2 was released on the Windows platform, good news for me. On my laptop I run Windows so I could finally upgrade my demo database from Release 1 to Release 2. I had been doing demo's with a virtual machine, which was ok. Like the one I did last year during the &lt;a href="http://nuijten.blogspot.com/2009/11/planboard-symposium-day-after.html"&gt;Planboard Symposium&lt;/a&gt;&lt;br /&gt;While I was preparing for that presentation, the support for Edition Based Redefinition was added to SQL Developer 2.1, at least that's what the &lt;a href="http://www.oracle.com/technology/products/database/sql_developer/files/NewFeatureList21.htm"&gt;New Features stated&lt;/a&gt;. But since March 1st there is SQL Developer 2.1.1 which does have support for Edition Based Redefinition.&lt;br /&gt;This blogpost is not going to explain Edition Based Redefinition in detail, but will show you the support that SQL Developer has for Edition Based Redefinition.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;When you connect with SQL Developer to an Oracle 11g Release 2 database, there are a number of extra folders in the browser.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/S7w17nJryZI/AAAAAAAAAFI/K4CZitTtEgY/s1600/browser.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 114px; height: 320px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/S7w17nJryZI/AAAAAAAAAFI/K4CZitTtEgY/s320/browser.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457296146704353682" /&gt;&lt;/a&gt;&lt;br /&gt;Following the demo I used for the Planboard Symposium, I created a few Editions, which would result in a tree structure.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/S7w3awt0_4I/AAAAAAAAAFQ/StFauUBw0uM/s1600/Editions.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 182px; height: 88px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/S7w3awt0_4I/AAAAAAAAAFQ/StFauUBw0uM/s320/Editions.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457297781359443842" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;With a simple Right-click on the Edition of your choice, you can change your current Edition, like changing to the Base Edition.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/S7w3vIK1trI/AAAAAAAAAFY/oXo5PvBjrOY/s1600/3change_to_base.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 252px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/S7w3vIK1trI/AAAAAAAAAFY/oXo5PvBjrOY/s320/3change_to_base.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457298131252524722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And changing the Editions also changes the Editioning Views. Note that the underlying tables are none editionable objects. &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/S7xB6qyuMeI/AAAAAAAAAFg/7OdvlAfePNQ/s1600/4EditioningView.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 117px; height: 320px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/S7xB6qyuMeI/AAAAAAAAAFg/7OdvlAfePNQ/s320/4EditioningView.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457309324641448418" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;While playing around with the Editions I noticed something weird though.&lt;br /&gt;First change to another edition named "Release_1":&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/S7xCqaB-8MI/AAAAAAAAAFo/daAgLWjWqXc/s1600/5change_to_release1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 252px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/S7xCqaB-8MI/AAAAAAAAAFo/daAgLWjWqXc/s320/5change_to_release1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457310144775778498" /&gt;&lt;/a&gt;&lt;br /&gt;And create a new Editioning View, using SQL*Plus:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/S7xC2fBndII/AAAAAAAAAFw/LQON8oZIuq8/s1600/6new_editioning_view.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 203px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/S7xC2fBndII/AAAAAAAAAFw/LQON8oZIuq8/s320/6new_editioning_view.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457310352274846850" /&gt;&lt;/a&gt;&lt;br /&gt;And because this Editioning View includes a new column, Language, we also need a Forward Cross Edition Trigger to be compatible with older Editions:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/S7xDXReZ97I/AAAAAAAAAF4/iozqfvcwKPE/s1600/7fwd_xed_trg.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 162px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/S7xDXReZ97I/AAAAAAAAAF4/iozqfvcwKPE/s320/7fwd_xed_trg.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457310915573184434" /&gt;&lt;/a&gt;&lt;br /&gt;The SQL Developer browser has a folder for the Cross Edition Triggers which now includes the newly created trigger:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/S7xDs1aFywI/AAAAAAAAAGA/7zJ5SO8O-NU/s1600/8xed_trg.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 165px; height: 36px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/S7xDs1aFywI/AAAAAAAAAGA/7zJ5SO8O-NU/s320/8xed_trg.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457311285996014338" /&gt;&lt;/a&gt;&lt;br /&gt;But now the strange bit; when you try to edit the Cross Edition Trigger code in SQL Developer, it doesn't show any code.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/S7xD_XeFq-I/AAAAAAAAAGI/fsp4_MBmw0A/s1600/9no_code.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 117px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/S7xD_XeFq-I/AAAAAAAAAGI/fsp4_MBmw0A/s320/9no_code.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457311604377234402" /&gt;&lt;/a&gt;&lt;br /&gt;At least not in the place where you would expect it, the Cross Edition Trigger code does show up in context of the underlying Base Table, named EMP_BASE:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/S7xEQNu1n6I/AAAAAAAAAGQ/MAUGUG1ZOKo/s1600/10_trg.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 190px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/S7xEQNu1n6I/AAAAAAAAAGQ/MAUGUG1ZOKo/s320/10_trg.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5457311893820907426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And here's the testscript that I used:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;conn alex/alex@orcl11&lt;br /&gt;&lt;br /&gt;set echo on&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;select *&lt;br /&gt;  from v$version&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Setup ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create table emp&lt;br /&gt;as&lt;br /&gt;select *&lt;br /&gt;  from scott.emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Editions ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create edition base as child of ora$base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;create edition release_1 as child of base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;create edition release_2 as child of release_1&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Rename Base Table ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter table emp rename to emp_base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Change Session Edition ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter session set edition = base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select sys_context('userenv', 'current_edition_name') "Current_Edition"&lt;br /&gt;  from dual&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Editioning View Base Release ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create or replace editioning view emp&lt;br /&gt;as&lt;br /&gt;select empno&lt;br /&gt;     , ename&lt;br /&gt;     , deptno&lt;br /&gt;     , job&lt;br /&gt;     , hiredate&lt;br /&gt;     , sal&lt;br /&gt;     , comm&lt;br /&gt;     , mgr&lt;br /&gt;  from emp_base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;desc emp&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select *&lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;cl scr&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Change Session Edition ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter session set edition = release_1&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select sys_context('userenv', 'current_edition_name') "Current_Edition" &lt;br /&gt;from dual&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Add additional column to Table ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter table emp_base&lt;br /&gt;add (language varchar2(2) null)&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;desc emp_base&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Editioning View Emp inherited ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;desc emp&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Editioning View Release 1 ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create or replace editioning view emp&lt;br /&gt;as&lt;br /&gt;select empno&lt;br /&gt;     , ename&lt;br /&gt;     , deptno&lt;br /&gt;     , job&lt;br /&gt;     , language&lt;br /&gt;     , hiredate&lt;br /&gt;     , sal&lt;br /&gt;     , comm&lt;br /&gt;     , mgr&lt;br /&gt;  from emp_base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;desc emp&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select *&lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Function Derive Language Value ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create or replace&lt;br /&gt;function get_language (p_job in varchar2)&lt;br /&gt;   return varchar2&lt;br /&gt;is&lt;br /&gt;begin&lt;br /&gt;  return case p_job &lt;br /&gt;         when 'MANAGER' then 'fr' &lt;br /&gt;         else 'en'&lt;br /&gt;         end;&lt;br /&gt;end get_language;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Forward Cross Edition Trigger        ==]&lt;br /&gt;prompt [== DML on earlier editions fire trigger ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;create or replace trigger EMP_1_2_Fwd_Xed&lt;br /&gt;before insert or update on emp_base&lt;br /&gt;for each row&lt;br /&gt;forward crossedition&lt;br /&gt;disable&lt;br /&gt;begin &lt;br /&gt;   :new.language := get_language(:new.job);&lt;br /&gt;end EMP_1_2_Fwd_Xed; &lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Enable Trigger ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter trigger EMP_1_2_Fwd_Xed enable&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Test Trigger from Base Edition ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter session set edition = base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;update emp&lt;br /&gt;   set job = 'PA'&lt;br /&gt; where job = 'CLERK'&lt;br /&gt;   and deptno = 10&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select *  &lt;br /&gt;  from emp&lt;br /&gt; where job = 'PA'&lt;br /&gt;   and deptno = 10&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select *&lt;br /&gt;  from emp_base&lt;br /&gt; where job = 'PA'&lt;br /&gt;   and deptno = 10&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;commit&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Change to Release_1 Edition ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter session set edition = release_1&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;select ename&lt;br /&gt;     , job&lt;br /&gt;     , language&lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Use Forward Crossedition Trigger     ==]&lt;br /&gt;prompt [== to upgrade existing records in table ==]&lt;br /&gt;prompt [== Consider: DBMS_PARALLEL_EXECUTE      ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;declare &lt;br /&gt;  c number := dbms_sql.open_cursor(); &lt;br /&gt;  x number; &lt;br /&gt;begin &lt;br /&gt;   dbms_sql.parse&lt;br /&gt;   ( c =&gt; c&lt;br /&gt;   , Language_Flag =&gt; dbms_sql.native&lt;br /&gt;   , Statement =&gt; 'UPDATE EMP_BASE&lt;br /&gt;                   SET EMPNO = EMPNO'&lt;br /&gt;   , Apply_Crossedition_Trigger =&gt; 'EMP_1_2_Fwd_Xed'&lt;br /&gt;   );&lt;br /&gt;   x := dbms_sql.execute(c); &lt;br /&gt;   dbms_sql.close_cursor(c); &lt;br /&gt;   commit; &lt;br /&gt;end; &lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select ename&lt;br /&gt;     , job&lt;br /&gt;     , language&lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Change to Release_2 Edition ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter session set edition = release_2&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Change to Mandatory Column ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter table emp_base&lt;br /&gt;modify &lt;br /&gt;( language varchar2(2)  not null &lt;br /&gt;)&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Add extra columns ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter table emp_base&lt;br /&gt;add &lt;br /&gt;( first_name varchar2(25)&lt;br /&gt;, last_name  varchar2(25)&lt;br /&gt;)&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Editioning View Emp Release_2 ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create or replace editioning view emp&lt;br /&gt;as&lt;br /&gt;select empno&lt;br /&gt;     , first_name&lt;br /&gt;     , last_name&lt;br /&gt;     , deptno&lt;br /&gt;     , job&lt;br /&gt;     , language&lt;br /&gt;     , hiredate&lt;br /&gt;     , sal&lt;br /&gt;     , comm&lt;br /&gt;     , mgr&lt;br /&gt;  from emp_base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;set lines 140&lt;br /&gt;&lt;br /&gt;select *&lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Forward Crossedition Trigger ==]&lt;br /&gt;prompt [== Derive First and Last name   ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create or replace trigger EMP_1_3_Fwd_Xed&lt;br /&gt;before insert or update of ename on emp_base&lt;br /&gt;for each row&lt;br /&gt;forward crossedition&lt;br /&gt;disable&lt;br /&gt;begin &lt;br /&gt;  :new.last_name := :new.ename;&lt;br /&gt;end EMP_1_3_Fwd_Xed; &lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;alter trigger EMP_1_3_Fwd_Xed enable&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Apply Forward Crossedition Trigger ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;declare &lt;br /&gt;  c number := dbms_sql.open_cursor(); &lt;br /&gt;  x number; &lt;br /&gt;begin &lt;br /&gt;  dbms_sql.parse&lt;br /&gt;  ( c =&gt; c&lt;br /&gt;  , Language_Flag =&gt; dbms_sql.native&lt;br /&gt;  , Statement =&gt; 'UPDATE EMP_BASE&lt;br /&gt;                  SET ENAME = ENAME'&lt;br /&gt;  , Apply_Crossedition_Trigger =&gt; 'EMP_1_3_Fwd_Xed'&lt;br /&gt;  );&lt;br /&gt;  x := dbms_sql.execute(c); &lt;br /&gt;  dbms_sql.close_cursor(c); &lt;br /&gt;  commit; &lt;br /&gt;end; &lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select * &lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;cl scr&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Reverse Crossedition Trigger ==]&lt;br /&gt;prompt [== Set ename based on LastName  ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;create or replace trigger EMP_3_2_Rve_Xed&lt;br /&gt;before insert or update of  last_name on emp_base&lt;br /&gt;for each row&lt;br /&gt;reverse crossedition&lt;br /&gt;disable&lt;br /&gt;begin &lt;br /&gt;  :new.ename := substr(:new.last_name, 1, 10);&lt;br /&gt;end EMP_3_2_Rve_Xed; &lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;alter trigger EMP_3_2_Rve_Xed enable&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select sys_context ('userenv', 'current_edition_name') "Current_Edition"&lt;br /&gt;  from dual&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt &lt;br /&gt;prompt [== Change Lastname from Release_2 Edition ==]&lt;br /&gt;prompt &lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;update emp&lt;br /&gt;   set last_name = 'MARTINA'&lt;br /&gt; where last_name = 'MARTIN'&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;commit&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select ename&lt;br /&gt;     , last_name&lt;br /&gt;  from emp_base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;alter session set edition = release_1&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;select ename&lt;br /&gt;  from emp&lt;br /&gt;/&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;update emp&lt;br /&gt;   set ename = 'MARTIN'&lt;br /&gt; where ename = 'MARTINA'&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;select ename&lt;br /&gt;     , last_name&lt;br /&gt;  from emp_base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;commit&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;cl scr&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt&lt;br /&gt;prompt [== Clean up==]&lt;br /&gt;prompt&lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;alter session set edition = ora$base&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt&lt;br /&gt;prompt [== Make Editions Unusable ==]&lt;br /&gt;prompt&lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;alter edition release_2 unusable&lt;br /&gt;/&lt;br /&gt;alter edition release_1 unusable&lt;br /&gt;/&lt;br /&gt;alter edition base unusable&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt&lt;br /&gt;prompt [== Drop Editions ==]&lt;br /&gt;prompt&lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;drop edition release_2 cascade&lt;br /&gt;/&lt;br /&gt;drop edition release_1 cascade&lt;br /&gt;/&lt;br /&gt;drop edition base cascade&lt;br /&gt;/&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;set echo off&lt;br /&gt;prompt&lt;br /&gt;prompt [== Remove Base Table ==]&lt;br /&gt;prompt&lt;br /&gt;set echo on&lt;br /&gt;&lt;br /&gt;drop table emp_base purge&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;drop view emp&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5640784827924329734?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5640784827924329734/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/04/oracle-11gr2-editions-and-sql-developer.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5640784827924329734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5640784827924329734'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/04/oracle-11gr2-editions-and-sql-developer.html' title='Oracle 11gR2: Editions and SQL Developer'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_tdqazgf19_s/S7w17nJryZI/AAAAAAAAAFI/K4CZitTtEgY/s72-c/browser.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7249401654398632552</id><published>2010-03-29T09:00:00.000+02:00</published><updated>2010-03-29T09:00:09.404+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><title type='text'>Change the XML Prolog using XMLROOT</title><content type='html'>About a month ago I wrote about using XMLType and RefCursor to rename the ROW and ROWSET tags. You can find that article by &lt;a href="http://nuijten.blogspot.com/2010/02/xmltype-refcursor-and-renaming-row-and.html" target="_blank"&gt;clicking here.&lt;/a&gt;&lt;br /&gt;For the same project described in that article the XML prolog needed to include the encoding as well. Just in case you are unfamiliar with the XML Prolog, this is it:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In Oracle 10&lt;i&gt;g&lt;/i&gt; there is an XMLROOT function which creates the XML Prolog for you, but it doesn't allow you to add encoding to it. At least not by specifying parameters, but there is a way to do this, read on.. &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;With the XMLRoot function you can create the XML Prolog. Besides the XML value, you need to specify the Version as well. This can be any value that you want.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select xmlroot (xmltype ('&lt;this&gt;that&lt;/this&gt;')&lt;br /&gt;  2                 ,version '24.5'&lt;br /&gt;  3                 ) "XMLRoot"&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;XMLRoot&lt;br /&gt;---------------------------------------------------------&lt;br /&gt;&amp;lt;?xml version="24.5"?&gt;&lt;br /&gt;&amp;lt;this&gt;that&amp;lt;/this&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Quite strangly, you can also specify that you want NO VALUE for the Version. Why strange? Look at this&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select xmlroot (xmltype ('&lt;this&gt;that&lt;/this&gt;')&lt;br /&gt;  2                 ,version no value&lt;br /&gt;  3                 ) "XMLRoot"&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;XMLRoot&lt;br /&gt;----------------------------------------------------&lt;br /&gt;&amp;lt;?xml version="1.0"?&gt;&lt;br /&gt;&amp;lt;this&gt;that&amp;lt;/this&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It seems a bit odd to me, specifying NO VALUE but you get a value anyway. This behavior is documented to do so, but why not call it DEFAULT?&lt;br /&gt;The last argument is optional and allows you to create a STANDALONE clause. The exact meaning of it is not clear to me, one day I will find out what it means.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select xmlroot (xmltype ('&lt;this&gt;that&lt;/this&gt;')&lt;br /&gt;  2                 ,version '1.0'&lt;br /&gt;  3                 ,standalone YES&lt;br /&gt;  4                 ) "XMLRoot"&lt;br /&gt;  5    from dual&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;XMLRoot&lt;br /&gt;--------------------------------------------------&lt;br /&gt;&amp;lt;?xml version="1.0" standalone="yes"?&gt;&lt;br /&gt;&amp;lt;this&gt;that&amp;lt;/this&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see there is no argument where you can specify the encoding. After searching for a long-long time, I gave my search and decided to concatenate the XML Prolog to the XML (after having extracting it as a CLOB). Not a nice solution, but it works.&lt;br /&gt;And than suddenly last week on the Oracle Technology Network SQL and PL/SQL forum someone asked how to specify the encoding in the XML Prolog... Now why didn't I think of that? I spend way too much time on that forum, and I never thought of it of posting a question there. Anyway, because you can put whatever you want in the Version argument,&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select xmlroot (xmltype ('&lt;this&gt;that&lt;/this&gt;')&lt;br /&gt;  2                 ,version 'whatever'&lt;br /&gt;  3                 ) "XMLRoot"&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;XMLRoot&lt;br /&gt;-----------------------------------------------------&lt;br /&gt;&amp;lt;?xml version="whatever"?&gt;&lt;br /&gt;&amp;lt;this&gt;that&amp;lt;/this&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;you can also place the encoding that you want in there.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select xmlroot (xmltype ('&lt;this&gt;that&lt;/this&gt;')&lt;br /&gt;  2                 , version '1.0" encoding="utf-8'&lt;br /&gt;  3                 ) "XMLRoot"&lt;br /&gt;  4    from dual&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;XMLRoot&lt;br /&gt;----------------------------------------------------&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;br /&gt;&amp;lt;this&gt;that&amp;lt;/this&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just mind the quotes and double quotes in the Version argument.&lt;br /&gt;&lt;h2&gt;Links&lt;/h2&gt;&lt;br /&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?messageID=4189125&amp;#4189125" target="_blank"&gt;OTN SQL and PL/SQL Discussion forum thread&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions225.htm#SQLRF06230" target="_blank"&gt;Oracle Documentation on XMLRoot&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7249401654398632552?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7249401654398632552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/03/change-xml-prolog-using-xmlroot.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7249401654398632552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7249401654398632552'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/03/change-xml-prolog-using-xmlroot.html' title='Change the XML Prolog using XMLROOT'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2092921424563887192</id><published>2010-02-25T10:00:00.000+01:00</published><updated>2010-02-25T10:00:05.007+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><title type='text'>Identifying the "last" record using the LEAD function</title><content type='html'>Of course there is no such thing as the "last" record in a relational database. Unless you have an ORDER BY in your query.&lt;br /&gt;For the current project we needed to determine the last record in a set, because this record needs special treatment. One of my colleagues came up with a CASE expression combined with a LEAD function to determine this record.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;First let's create a sample table - a simple one with just one column.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  as&lt;br /&gt;  3  select rownum id&lt;br /&gt;  4    from dual&lt;br /&gt;  5   connect by level &lt;= 5&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The content of this table are just 5 consecutive numbers, ranging from 1 through 5.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2    from test&lt;br /&gt;  3   order by id&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;        ID&lt;br /&gt;----------&lt;br /&gt;         1&lt;br /&gt;         2&lt;br /&gt;         3&lt;br /&gt;         4&lt;br /&gt;         5&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since we are going to use the LEAD function, it's important to understand what the LEAD function does. With the LEAD function it is possible to access values from others rows in the resultset. The value that is returned is the next one in line, therefore the ORDER BY in the analytic function is important.&lt;br /&gt;Below is the effect shown of the LEAD function. For the first line of the resultset, the value of the second line is shown. For the second line, the third one is shown etcetera. For the last line in the resultset there is no "next line", a NULL is shown.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , lead (id) over (order by id) ind_last_record&lt;br /&gt;  3    from test&lt;br /&gt;  4   order by id&lt;br /&gt;  5  ;&lt;br /&gt;&lt;br /&gt;        ID IND_LAST_RECORD&lt;br /&gt;---------- ---------------&lt;br /&gt;         1               2&lt;br /&gt;         2               3&lt;br /&gt;         3               4&lt;br /&gt;         4               5&lt;br /&gt;         5&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Because we want to identify the "last" record, based on the sorted ID, a CASE expression is used where we look at the results of the LEAD:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , case&lt;br /&gt;  3         when lead (id) over (order by id) is null&lt;br /&gt;  4         then 'Yes'&lt;br /&gt;  5         end ind_last_record&lt;br /&gt;  6    from test&lt;br /&gt;  7   order by id&lt;br /&gt;  8  ;&lt;br /&gt;&lt;br /&gt;        ID IND&lt;br /&gt;---------- ---&lt;br /&gt;         1&lt;br /&gt;         2&lt;br /&gt;         3&lt;br /&gt;         4&lt;br /&gt;         5 Yes&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there it is, the last record has the indicator "Yes".&lt;/ br&gt;&lt;br /&gt;&lt;br /&gt;The LEAD function can take up to three arguments. The first argument is the value that you want returned. The second argument is the number of rows that you want to look ahead - the default is 1. The third argument is the value you want to see in case the LEAD function returns NULL - the default is NULL.&lt;br /&gt;Because of the optional arguments we can simplify the query to use "Yes" as the last argument when the LEAD would return NULL.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , lead (to_char (id), 1, 'Yes') over (order by id)&lt;br /&gt;  3         ind_last_record&lt;br /&gt;  4    from test&lt;br /&gt;  5   order by id&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;        ID IND_LAST_RECORD&lt;br /&gt;---------- ----------------------------------------&lt;br /&gt;         1 2&lt;br /&gt;         2 3&lt;br /&gt;         3 4&lt;br /&gt;         4 5&lt;br /&gt;         5 Yes&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For the first argument a TO_CHAR is required, otherwise the exception ORA-1722 (Invalid Number) is raised - After all, "Yes" is not a NUMBER.&lt;br /&gt;Last step to take: only the indicator for the last record is required, let's change the first argument to NULL instead of TO_CHAR (id)&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , lead (null, 1, 'Yes') over (order by id)&lt;br /&gt;  3         ind_last_record&lt;br /&gt;  4    from test&lt;br /&gt;  5   order by id&lt;br /&gt;  6  ;&lt;br /&gt;&lt;br /&gt;        ID IND&lt;br /&gt;---------- ---&lt;br /&gt;         1&lt;br /&gt;         2&lt;br /&gt;         3&lt;br /&gt;         4&lt;br /&gt;         5 Yes&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pretty nifty trick, if I say so myself.... :)&lt;br /&gt;... and don't forget to cleanup the testset.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; drop table test purge&lt;br /&gt;  2  ;&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Oracle documentation&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions074.htm#SQLRF00656"&gt;Lead Analytic Function&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2092921424563887192?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2092921424563887192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/02/identifying-last-record-using-lead.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2092921424563887192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2092921424563887192'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/02/identifying-last-record-using-lead.html' title='Identifying the &quot;last&quot; record using the LEAD function'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3019185023886569989</id><published>2010-02-23T10:00:00.003+01:00</published><updated>2010-02-23T10:20:43.004+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><title type='text'>XMLType, RefCursor and renaming ROW and ROWSET tags</title><content type='html'>The project that I'm currently involved in uses XML to send data to a reporting engine which parses the XML and creates a pretty PDF report.&lt;br /&gt;In order to do this some "generic" packages were created to create the XML based on the content of a RefCursor. Works like a charm. Just call the function with a RefCursor and get an XML back.&lt;br /&gt;The problem they initially had was that the XML returned uses the ROWSET and ROW tags, while they wanted different tags. No problem, some REPLACE function would do the job, and that worked fine. Until last week that was...&lt;br /&gt; &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;First let me show you how the initial function works and what the XML result was they returned.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create function rc_to_xml (p_rc in sys_refcursor)&lt;br /&gt;  2     return xmltype&lt;br /&gt;  3  is&lt;br /&gt;  4  begin&lt;br /&gt;  5     return xmltype (p_rc);&lt;br /&gt;  6  end rc_to_xml;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The function has an input parameter which is a Ref Cursor and returns an XMLType. The XMLType constructor can take a Ref Cursor and transform that into an XMLType, pretty cool functionality. In order to test this functionality, we need two local variables - one for the Ref Cursor and one for the XMLType.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     refC sys_refcursor;&lt;br /&gt;  3     x    xmltype;&lt;br /&gt;  4  begin&lt;br /&gt;  5     open refC for&lt;br /&gt;  6     select ename&lt;br /&gt;  7          , job&lt;br /&gt;  8          , sal&lt;br /&gt;  9       from emp&lt;br /&gt; 10      where rownum &lt;= 2&lt;br /&gt; 11     ;&lt;br /&gt; 12     x := rc_to_xml (refC);&lt;br /&gt; 13     dbms_output.put_line (x.extract ('/*').getClobVal());&lt;br /&gt; 14  end;&lt;br /&gt; 15  /&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using DBMS_OUTPUT to see the actual content of the XMLType, we will get this result:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;&amp;lt;ROWSET&gt;&lt;br /&gt;  &lt;ROW&gt;&lt;br /&gt;    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;&lt;br /&gt;    &lt;JOB&gt;CLERK&lt;/JOB&gt;&lt;br /&gt;    &lt;SAL&gt;800&lt;/SAL&gt;&lt;br /&gt;  &lt;/ROW&gt;&lt;br /&gt;  &lt;ROW&gt;&lt;br /&gt;    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;&lt;br /&gt;    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;&lt;br /&gt;    &lt;SAL&gt;1600&lt;/SAL&gt;&lt;br /&gt;  &lt;/ROW&gt;&lt;br /&gt;&amp;lt;/ROWSET&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A nice XMLType is the result. The only "problem" is the standard tags used by the XMLType. In this project we didn't want to have the standard ROWSET and ROW tags, we wanted to have more meaningful names.&lt;br /&gt;Changing the function to change these tags on the fly would work&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  function rc_to_xml (p_rc     in sys_refcursor&lt;br /&gt;  3                     ,p_rowset in varchar2&lt;br /&gt;  4                     ,p_row    in varchar2&lt;br /&gt;  5                     )&lt;br /&gt;  6     return xmltype&lt;br /&gt;  7  is&lt;br /&gt;  8     retval xmltype;&lt;br /&gt;  9     c      clob;&lt;br /&gt; 10  begin&lt;br /&gt; 11     retval := xmltype (p_rc);&lt;br /&gt; 12     c := retval.getClobVal();&lt;br /&gt; 13     c := replace (c, '&lt;ROWSET&gt;', '&lt;'||p_rowset||'&gt;');&lt;br /&gt; 14     c := replace (c, '&lt;/ROWSET&gt;', '&lt;/'||p_rowset||'&gt;');&lt;br /&gt; 15     c := replace (c, '&lt;ROW&gt;', '&lt;'||p_row||'&gt;');&lt;br /&gt; 16     c := replace (c, '&lt;/ROW&gt;', '&lt;/'||p_row||'&gt;');&lt;br /&gt; 17     retval := xmltype (c);&lt;br /&gt; 18     return retval;&lt;br /&gt; 19  end rc_to_xml;&lt;br /&gt; 20  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just to make sure it works as expected, a small testcase:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     refC sys_refcursor;&lt;br /&gt;  3     x    xmltype;&lt;br /&gt;  4  begin&lt;br /&gt;  5     open refC for&lt;br /&gt;  6     select ename&lt;br /&gt;  7          , job&lt;br /&gt;  8          , sal&lt;br /&gt;  9       from emp&lt;br /&gt; 10      where rownum &lt;= 2&lt;br /&gt; 11     ;&lt;br /&gt; 12     x := rc_to_xml (refC&lt;br /&gt; 13                    ,'DEPT'&lt;br /&gt; 14                    ,'EMP'&lt;br /&gt; 15                    );&lt;br /&gt; 16     dbms_output.put_line (x.extract ('/*').getClobVal());&lt;br /&gt; 17  end;&lt;br /&gt; 18  /&lt;br /&gt;&amp;lt;DEPT&gt;&lt;br /&gt;  &lt;EMP&gt;&lt;br /&gt;    &lt;ENAME&gt;SMITH&lt;/ENAME&gt;&lt;br /&gt;    &lt;JOB&gt;CLERK&lt;/JOB&gt;&lt;br /&gt;    &lt;SAL&gt;800&lt;/SAL&gt;&lt;br /&gt;  &lt;/EMP&gt;&lt;br /&gt;  &lt;EMP&gt;&lt;br /&gt;    &lt;ENAME&gt;ALLEN&lt;/ENAME&gt;&lt;br /&gt;    &lt;JOB&gt;SALESMAN&lt;/JOB&gt;&lt;br /&gt;    &lt;SAL&gt;1600&lt;/SAL&gt;&lt;br /&gt;  &lt;/EMP&gt;&lt;br /&gt;&amp;lt;/DEPT&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that did the trick for quite some time (years actually).&lt;br /&gt;&lt;br /&gt;This technique was used quite heavily for reporting purposes, it even works fine with nesting one XMLType in another:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  function all_emps (p_deptno in dept.deptno%type)&lt;br /&gt;  3     return xmltype&lt;br /&gt;  4  is&lt;br /&gt;  5     rc sys_refcursor;&lt;br /&gt;  6  begin&lt;br /&gt;  7     open rc for select ename&lt;br /&gt;  8                      , job&lt;br /&gt;  9                      , sal&lt;br /&gt; 10                   from emp&lt;br /&gt; 11                  where deptno = p_deptno;&lt;br /&gt; 12     return rc_to_xml (rc&lt;br /&gt; 13                      ,'EMPS'&lt;br /&gt; 14                      ,'EMP'&lt;br /&gt; 15                      );&lt;br /&gt; 16  end all_emps;&lt;br /&gt; 17  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the below code sample you can see the above function being used in the Ref Cursor on line 8.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     refC sys_refcursor;&lt;br /&gt;  3     x    xmltype;&lt;br /&gt;  4  begin&lt;br /&gt;  5     open refC for&lt;br /&gt;  6     select deptno&lt;br /&gt;  7          , dname&lt;br /&gt;  8          , all_emps (deptno) employees&lt;br /&gt;  9       from dept&lt;br /&gt; 10      where deptno = 10&lt;br /&gt; 11     ;&lt;br /&gt; 12     x := rc_to_xml (refC&lt;br /&gt; 13                    ,'DEPTS'&lt;br /&gt; 14                    ,'DEPT'&lt;br /&gt; 15                    );&lt;br /&gt; 16     dbms_output.put_line (x.extract ('/*').getClobVal());&lt;br /&gt; 17  end;&lt;br /&gt; 18  /&lt;br /&gt;&amp;lt;DEPTS&gt;&lt;br /&gt;  &lt;DEPT&gt;&lt;br /&gt;    &lt;DEPTNO&gt;10&lt;/DEPTNO&gt;&lt;br /&gt;    &lt;DNAME&gt;ACCOUNTING&lt;/DNAME&gt;&lt;br /&gt;    &lt;EMPLOYEES&gt;&lt;br /&gt;      &lt;EMPS&gt;&lt;br /&gt;        &lt;EMP&gt;&lt;br /&gt;&lt;br /&gt;    &lt;ENAME&gt;CLARK&lt;/ENAME&gt;&lt;br /&gt;          &lt;JOB&gt;MANAGER&lt;/JOB&gt;&lt;br /&gt;          &lt;SAL&gt;2450&lt;/SAL&gt;&lt;br /&gt;        &lt;/EMP&gt;&lt;br /&gt;        &lt;EMP&gt;&lt;br /&gt;          &lt;ENAME&gt;KING&lt;/ENAME&gt;&lt;br /&gt;          &lt;JOB&gt;PRESIDENT&lt;/JOB&gt;&lt;br /&gt;          &lt;SAL&gt;5000&lt;/SAL&gt;&lt;br /&gt;        &lt;/EMP&gt;&lt;br /&gt;        &lt;EMP&gt;&lt;br /&gt;          &lt;ENAME&gt;MILLER&lt;/ENAME&gt;&lt;br /&gt;          &lt;JOB&gt;CLERK&lt;/JOB&gt;&lt;br /&gt;          &lt;SAL&gt;1300&lt;/SAL&gt;&lt;br /&gt;        &lt;/EMP&gt;&lt;br /&gt;      &lt;/EMPS&gt;&lt;br /&gt;    &lt;/EMPLOYEES&gt;&lt;br /&gt;  &lt;/DEPT&gt;&lt;br /&gt;&amp;lt;/DEPTS&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Very flexible, used extensively throughout the reporting packages.&lt;br /&gt;&lt;br /&gt;Last week problems started to occur with these packages however. XMLTypes were not being created correctly, strange characters started to appear causing the XML to be invalid. But not with all reports, not all the time. It appeared that if the reports became to large, this problem would occur.&lt;br /&gt;After looking for the cause for quite some time, we had this feeling that it had to do with the datatype conversions going on between the XMLType and the Clob. This transformation is done in the REPLACE part of the function where the ROWSET and ROW tags are replaced.&lt;br /&gt;First we started looking into creating a "REPLACE function for CLOB", which is according to the documentation not necessary... what a waste of time...&lt;br /&gt;I couldn't help it, but I was getting the feeling that I wasn't the first one with this "problem". Surely there must be a way to rename the ROWSET and ROW tags when you create an XML based on a Ref Cursor?&lt;br /&gt;Probably I was looking in the wrong direction, instead of treating the XML as a string, I should stay in the "XML Domain".&lt;br /&gt;After more searching, finally I came across this procedure: setRowTag in the dbms_xmlgen package - just what the doctor ordered. All the convoluted code we used earlier was no longer necessary.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;create or replace &lt;br /&gt;function rc_to_xml (p_rc     in sys_refcursor&lt;br /&gt;                   ,p_rowset in varchar2&lt;br /&gt;                   ,p_row    in varchar2&lt;br /&gt;                   ) &lt;br /&gt;   return xmltype&lt;br /&gt;is&lt;br /&gt;   retval xmltype;&lt;br /&gt;   ctx dbms_xmlgen.ctxHandle;&lt;br /&gt;begin&lt;br /&gt;   ctx := dbms_xmlgen.newContext (p_rc);&lt;br /&gt;   dbms_xmlgen.setRowTag (ctx, p_row_name);&lt;br /&gt;   dbms_xmlgen.setRowSetTag (ctx, p_rowset_name);&lt;br /&gt;   retval := dbms_xmlgen.getXMLType (ctx);&lt;br /&gt;   dbms_xmlgen.closeContext (ctx);&lt;br /&gt;   return retval;&lt;br /&gt;end rc_to_xml;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's always good to realize that when you are trying to solve a certain problem someone must have come across your problem before and maybe even solved it. Hope my story will save you some time when you are trying to rename the ROWSET and ROW tags.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Oracle Documentation&lt;/span&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions134.htm#SQLRF00697"&gt;REPLACE; it also works with CLOB&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_xmlgen.htm#CHEJCICB"&gt;DBMS_XMLGEN - all subprograms&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3019185023886569989?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3019185023886569989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/02/xmltype-refcursor-and-renaming-row-and.html#comment-form' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3019185023886569989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3019185023886569989'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/02/xmltype-refcursor-and-renaming-row-and.html' title='XMLType, RefCursor and renaming ROW and ROWSET tags'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-9204876804697220980</id><published>2010-01-16T12:05:00.000+01:00</published><updated>2010-01-16T12:08:23.545+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 9i'/><category scheme='http://www.blogger.com/atom/ns#' term='exceptions'/><title type='text'>The Case for the Case Statement</title><content type='html'>Oracle 9i introduced the CASE Statement and CASE Expressions.&lt;br /&gt;Some say that the CASE statement is a drop-in replacement of the IF statement, but there is a subtle difference between these two.&lt;br /&gt;In this post I will explain the difference between the Case statement and the IF statement. Sometimes it's "better" to change the IF statement to a CASE statement.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The CASE statement, like the CASE expression, comes in two flavors: Simple and Searched. To illustrate, this is the Simple CASE&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     selector varchar2(1);&lt;br /&gt;  3  begin&lt;br /&gt;  4     selector := 'Y';&lt;br /&gt;  5     case selector&lt;br /&gt;  6     when 'Y' then&lt;br /&gt;  7        dbms_output.put_line ('Yes!');&lt;br /&gt;  8     when 'N' then&lt;br /&gt;  9        dbms_output.put_line ('No..');&lt;br /&gt; 10     end case;&lt;br /&gt; 11  end;&lt;br /&gt; 12  /&lt;br /&gt;Yes!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and this is the Searched Case&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     selector varchar2(1);&lt;br /&gt;  3  begin&lt;br /&gt;  4     selector := 'Y';&lt;br /&gt;  5     case &lt;br /&gt;  6     when selector = 'Y' then&lt;br /&gt;  7        dbms_output.put_line ('Yes!');&lt;br /&gt;  8     when selector = 'N' then&lt;br /&gt;  9        dbms_output.put_line ('No..');&lt;br /&gt; 10     end case;&lt;br /&gt; 11  end;&lt;br /&gt; 12  /&lt;br /&gt;Yes!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice the subtle difference in lines 5 and 6.&lt;br /&gt;The difference is the type of comparison that you do. With the Simple Case, you do an equality comparison and with the Searched Case, you can do whatever you feel like - in this case also an equality comparison. When you want to use a comparison with NULL, than your only option is to use a Searched Case. An equality comparison with NULL is always False.&lt;br /&gt;Also note that the Case Statement ends with an "END CASE", just like the IF statement ends with an "END IF".&lt;br /&gt;&lt;br /&gt;Can you replace an IF statement with a CASE statement? Well, yes and no. Let me explain.&lt;br /&gt;The IF statement and the CASE statement look very much alike, the difference is in whether or not a match is found.&lt;br /&gt;Say you want to create a function which return a Yes or No depending on your input value. Of course this is a completely stupid example, but it's the idea that counts.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  function fun (p_in in varchar2)&lt;br /&gt;  3     return varchar2&lt;br /&gt;  4  is&lt;br /&gt;  5     l_in   varchar2 (1);&lt;br /&gt;  6     retval varchar2 (20);&lt;br /&gt;  7  begin&lt;br /&gt;  8     l_in := upper (p_in);&lt;br /&gt;  9     if l_in = 'Y'&lt;br /&gt; 10     then&lt;br /&gt; 11        retval := 'Yes!';&lt;br /&gt; 12     elsif l_in = 'N'&lt;br /&gt; 13     then&lt;br /&gt; 14        retval := 'No...';&lt;br /&gt; 15     end if;&lt;br /&gt; 16     return retval;&lt;br /&gt; 17  end fun;&lt;br /&gt; 18  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So far, so good. Let's see if it works as expected.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_output.put_line ('Input Y: '||fun ('Y'));&lt;br /&gt;  3     dbms_output.put_line ('Input N: '||fun ('N'));&lt;br /&gt;  4  end;&lt;br /&gt;  5  /&lt;br /&gt;Input Y: Yes!&lt;br /&gt;Input N: No...&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Looks good to me, but what if someone would call the function with a different input value?&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_output.put_line ('Input Z: '||fun ('Z'));&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;Input Z:&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Hmmmm,... the function is successfully completed, but the return value is NULL. If this alright, than you're done. If the Business Rule says it is not allowed, than you would need to change your code. For instance with an ELSE in the IF statement, which raises an exception. Or use the CASE statement and see how you can benefit from it.&lt;br /&gt;First let's rewrite the function:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create or replace&lt;br /&gt;  2  function fun (p_in in varchar2)&lt;br /&gt;  3     return varchar2&lt;br /&gt;  4  is&lt;br /&gt;  5     l_in   varchar2 (1);&lt;br /&gt;  6     retval varchar2 (20);&lt;br /&gt;  7  begin&lt;br /&gt;  8     l_in := upper (p_in);&lt;br /&gt;  9     case l_in&lt;br /&gt; 10     when 'Y' then&lt;br /&gt; 11        retval := 'Yes!';&lt;br /&gt; 12     when 'N' then&lt;br /&gt; 13        retval := 'No...';&lt;br /&gt; 14     end case;&lt;br /&gt; 15     return retval;&lt;br /&gt; 16  end fun;&lt;br /&gt; 17  /&lt;br /&gt;&lt;br /&gt;Function created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and do the same test we did before&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_output.put_line ('Input Y: '||fun ('Y'));&lt;br /&gt;  3     dbms_output.put_line ('Input N: '||fun ('N'));&lt;br /&gt;  4  end;&lt;br /&gt;  5  /&lt;br /&gt;Input Y: Yes!&lt;br /&gt;Input N: No...&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So far, so good. Now for the not allowed value:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_output.put_line ('Input Z: '||fun ('Z'));&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;begin&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-06592: CASE not found while executing CASE statement&lt;br /&gt;ORA-06512: at "ALEX.FUN", line 8&lt;br /&gt;ORA-06512: at line 2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, it blows up in your face. Because there is not a match for the incoming value, the Case statement raises an exception - something that the IF statement doesn't do for you out-of-the-box.&lt;br /&gt;This is just what the Business Rule ordered. No need for extra coding to raise an exception.&lt;br /&gt;Can this exception be handled gracefully? Yes, it can. Oracle gives us the "CASE_NOT_FOUND" exception.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_output.put_line ('Input Z: '||fun ('Z'));&lt;br /&gt;  3  exception&lt;br /&gt;  4     when case_not_found&lt;br /&gt;  5     then&lt;br /&gt;  6        dbms_output.put_line ('Gracefully Handled');&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;Gracefully Handled&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, can the IF statement be replaced: yes. Can it be done without thinking about the consequences: no.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-9204876804697220980?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/9204876804697220980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2010/01/case-for-case-statement.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/9204876804697220980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/9204876804697220980'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2010/01/case-for-case-statement.html' title='The Case for the Case Statement'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-8529417175580458732</id><published>2009-12-22T10:00:00.002+01:00</published><updated>2009-12-23T10:19:43.319+01:00</updated><title type='text'>Best Wishes</title><content type='html'>Let SQL do the talking:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;select decode&lt;br /&gt;      (sign (floor (maxwidth/2) - rownum)&lt;br /&gt;      ,1, lpad (' ', floor (maxwidth/2) - (rownum -1))&lt;br /&gt;         ||rpad ('*', 2 * (rownum -1), ' *')&lt;br /&gt;      ,lpad ('* * *', floor (maxwidth/2) + 3)) "Merry Christmas"&lt;br /&gt;from all_objects&lt;br /&gt;,    (select 40 as maxwidth from dual)&lt;br /&gt;where rownum &lt; floor (maxwidth/2) + 5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;... and in case your wondering what the result would be, I added a screenshot.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/Sy9VZW2khTI/AAAAAAAAAE0/lTYsj8VpLOc/s1600-h/SQLChristmasTree.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 269px; height: 320px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/Sy9VZW2khTI/AAAAAAAAAE0/lTYsj8VpLOc/s320/SQLChristmasTree.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5417642770869552434" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Best wishes and a Happy New Year!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-8529417175580458732?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/8529417175580458732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/12/best-wishes.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8529417175580458732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8529417175580458732'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/12/best-wishes.html' title='Best Wishes'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_tdqazgf19_s/Sy9VZW2khTI/AAAAAAAAAE0/lTYsj8VpLOc/s72-c/SQLChristmasTree.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-4124176744608097266</id><published>2009-12-14T10:00:00.000+01:00</published><updated>2009-12-14T10:00:00.220+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ODTUG Kaleidoscope 2010'/><title type='text'>ODTUG Kaleidoscope 2010: Accepted Abstract</title><content type='html'>My presentation on Analytic Functions is accepted for the ODTUG Kaleidoscope 2010 conference. The other abstract that I sent in is an alternate, standby if you will. As you can see on the right side, I have attended (and presented) at the ODTUG yearly conference since 2005.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The yearly &lt;a href="http://www.odtugkaleidoscope.com/"&gt;ODTUG Kaleidoscope&lt;/a&gt; is one of my favorite conferences to go to. Besides the really good sessions, there is always lots of time for networking.&lt;br /&gt;This year it will be held in Washington, D.C..&lt;br /&gt;The Abstracts:&lt;br /&gt;&lt;b&gt;Analytic Functions Revisited&lt;/b&gt;&lt;br /&gt;&lt;blockquote&gt;Even though Oracle has enriched SQL with Analytic Functions a long time ago, they are still relatively unknown. Analytic Functions add inter-row calculations, aggregates over multiple dimensions, or rank assignment based on values within a group of values - all without a GROUP BY clause. They can make your life as a developer a lot easier once you get Analytic Functions in your fingertips. Even though the syntax may seem daunting at first, it becomes second nature very quickly. Oracle 11gR2 brings you new and exciting Analytic Functions.&lt;br /&gt;This presentation will cover the syntax and is packed with real life examples of how to apply them on a daily basis.See the performance increase with just a few lines of code and bring you sheer development joy.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;SQL Holmes - The Revisited Case of the Missing Performance&lt;/b&gt;&lt;br /&gt;&lt;blockquote&gt;During Kaleidoscope 2009 a case study was unfolded to investigate the cause of a slow performing query. In this presentation we will revisit this case and investigate further possibly leading to a solution for the Missing Performance.&lt;/br&gt;&lt;br /&gt;Did the database just "have a bad day"? Was the evil DBA to blame? The PL/SQL developer who didn't get enough coffee? Or was it the application sending the "wrong" query in the first place?&lt;/br&gt;&lt;br /&gt;In this classic "whodunnit" you will take a tour past the crime scene. Investigate the query, use the tools of the trade and collect all the relevant information. Follow the trail to uncover the truth and nothing but the truth...&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;We'll see if I have to prepare for one or two presentations when the time comes....&lt;br /&gt;Hope to see you there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-4124176744608097266?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/4124176744608097266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/12/odtug-kaleidoscope-2010-accepted.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4124176744608097266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4124176744608097266'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/12/odtug-kaleidoscope-2010-accepted.html' title='ODTUG Kaleidoscope 2010: Accepted Abstract'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5742974442650489432</id><published>2009-11-23T10:00:00.000+01:00</published><updated>2009-11-25T08:09:35.890+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='Diacritic'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Revenge of the Multibyte Characters</title><content type='html'>In The Netherlands, where I'm from, we do have Multibyte characters. Names with diacritic characters occur frequently.&lt;br /&gt;In &lt;a href="http://nuijten.blogspot.com/2009/03/oracle-text-diacritic-search.html" target="_blank"&gt;a prior post&lt;/a&gt; I've written about using Oracle text to perform diacritic searches.&lt;br /&gt;&lt;br /&gt;This post is about the table definition. Every now and then this problem rears its ugly head, that's why I decided to write this little note on it. &lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Let's look at the problem at hand. First we'll create a table:&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; create table test&lt;br /&gt;  2  (name varchar2 (7))&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;No problem so far.. but what happens when we try to insert a name with diacritics into this table?&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; insert into test values ('Alèx')&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;/pre&gt;No problem. Or so it seems... what is the length of the name that I just inserted into the table?&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; select length (name)&lt;br /&gt;  2    from test&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;LENGTH(NAME)&lt;br /&gt;------------&lt;br /&gt;           4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even though the name contains a diacritic character, the length is four. But what if we insert a name with length seven with diacritic characters?&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; rollback&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Rollback complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into test values ('Thérèse')&lt;br /&gt;  2  /&lt;br /&gt;insert into test values ('Thérèse')&lt;br /&gt;                         *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-12899: value too large for column "ALEX"."TEST"."NAME" (actual: 9, maximum: 7)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The error message is not quite clear. It says the name is too long, but when you count them like&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;T h é r è s e &lt;br /&gt;1 2 3 4 5 6 7&lt;br /&gt;&lt;/pre&gt;it should match.... Of course it has to do with the diacritic characters in the name.&lt;/br&gt;&lt;br /&gt;We can check this by using the LENGTHB function (notice the "B" at the end of Length)&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; with local_name&lt;br /&gt;  2  as&lt;br /&gt;  3  (select 'Thérèse' name&lt;br /&gt;  4     from dual&lt;br /&gt;  5  )&lt;br /&gt;  6  select length (name)&lt;br /&gt;  7       , lengthb (name)&lt;br /&gt;  8    from local_name&lt;br /&gt;  9  /&lt;br /&gt;&lt;br /&gt;LENGTH(NAME) LENGTHB(NAME)&lt;br /&gt;------------ -------------&lt;br /&gt;           7             9&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When we query the length in Bytes, it becomes obvious. The last name we inserted was too long in Bytes. How to fix this? Either increase the length of the column, or change it from BYTE to CHAR (and I don't mean the datatype CHAR)&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; drop table test&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (name varchar2 (7 char))&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;Now the column will allow up to seven characters in it regardless of the byte size of the data. Or at least that's how I interpret the documentation.&lt;br /&gt;The name which we couldn't insert with a prior INSERT statement, now comfortably fits into the column:&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; insert into test values ('Thérèse')&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, when you use Multibyte characters, check you Length Semantics!&lt;/br&gt;&lt;br /&gt;You can check your setting with this statement:&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; show parameter nls_length_semantics&lt;br /&gt;&lt;br /&gt;NAME                                 TYPE        VALUE&lt;br /&gt;------------------------------------ ----------- -----&lt;br /&gt;nls_length_semantics                 string      BYTE&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And just to clean up the test table.&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; drop table test&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Links&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#sthref3787" target="_blank"&gt;Oracle Documentation&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5742974442650489432?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5742974442650489432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/11/revenge-of-multibyte-characters.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5742974442650489432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5742974442650489432'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/11/revenge-of-multibyte-characters.html' title='Revenge of the Multibyte Characters'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-4735641740545130480</id><published>2009-11-23T09:41:00.004+01:00</published><updated>2009-11-23T09:57:54.582+01:00</updated><title type='text'>(Non Oracle): Shocking Truth</title><content type='html'>Do you know what it looks like when a couple of guys are trying out how much electricity runs through an electric fence?&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/2DCIMah-_i0&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/2DCIMah-_i0&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;The guys in this clip are my nephews Michiel, Randy, and Jesper. During a family reunion gettaway they couldn't resist trying out the electirc fence across the street... My brother just happened to be filming...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-4735641740545130480?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/4735641740545130480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/11/non-oracle-shocking-truth.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4735641740545130480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4735641740545130480'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/11/non-oracle-shocking-truth.html' title='(Non Oracle): Shocking Truth'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-4273068036774961957</id><published>2009-11-18T16:28:00.005+01:00</published><updated>2010-11-01T16:40:05.728+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EBR'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>Planboard Symposium, the day after.</title><content type='html'>Yesterday the &lt;a target="_blank" href="http://www.planboard.com/"&gt;Planboard Symposium&lt;/a&gt;, a symposium for DBA presented by DBA, took place in Driebergen, The Netherlands.&lt;br /&gt;The setup of this conference is a little different from what I'm used to. Only two parallel sessions to choose from with lots of time in between the sessions, which is nice for networking.&lt;br /&gt;For everybody who attended my session, a big "Thank you". For everybody who decided to attend &lt;a  target="_blank" href="http://prutser.wordpress.com/"&gt;Harald's session&lt;/a&gt;, you missed out on something good, and I finished on time ;)&lt;br /&gt;As a consolodation price for those of you that didn't attend the Planboard Symposium, here is my presentation.&lt;br /&gt;&lt;div style="width:425px;text-align:left" id="__ss_2526636"&gt;&lt;a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/AlexNuijten/edition-based-redefinition" title="Edition Based Redefinition"&gt;Edition Based Redefinition&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=reviewebr-091118044730-phpapp01&amp;stripped_title=edition-based-redefinition" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=reviewebr-091118044730-phpapp01&amp;stripped_title=edition-based-redefinition" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;"&gt;View more &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/AlexNuijten"&gt;AlexNuijten&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-4273068036774961957?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/4273068036774961957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/11/planboard-symposium-day-after.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4273068036774961957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4273068036774961957'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/11/planboard-symposium-day-after.html' title='Planboard Symposium, the day after.'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6514352380668692010</id><published>2009-11-13T08:00:00.001+01:00</published><updated>2009-11-13T08:00:03.357+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>An Empty Clob is not NULL, it's NOT NULL</title><content type='html'>Oracle recommends using CLOB instead of LONG for columns in the database. We all know this, right? Using CLOB is a lot easier than trying to manipulate LONG. Makes our life a lot easier.&lt;br /&gt;But there is something about using CLOBs that I didn't know. As you might have guessed from the title it has to do with NULL...&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;First let's set up a table for testing:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test&lt;br /&gt;  2  (id   number&lt;br /&gt;  3  ,text clob&lt;br /&gt;  4  );&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Along with some sample data:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; insert into test values (1, null);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into test values (2, 'some data is here');&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into test values (3, empty_clob());&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; commit;&lt;br /&gt;&lt;br /&gt;Commit complete.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At first I expected that this query would show the records with the ID 1 and 3, but this is not the case.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from test&lt;br /&gt;  3   where text is null&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;        ID TEXT&lt;br /&gt;---------- -------------&lt;br /&gt;         1&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;An Empty_Clob is not NULL. According to the documentation: &lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;EMPTY means that the LOB is initialized, but not populated with data.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;When you run the following query, you can see this&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from test&lt;br /&gt;  3   where text is not null&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;        ID TEXT&lt;br /&gt;---------- -----------------&lt;br /&gt;         2 some data is here&lt;br /&gt;         3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately we can't use the DUMP function to see what the Empty_Clob is made up of.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select dump (text)&lt;br /&gt;  2    from test&lt;br /&gt;  3  /&lt;br /&gt;select dump (text)&lt;br /&gt;             *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00932: inconsistent datatypes: expected - got CLOB&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So how can you execute a SQL statement to find the NULL and the Empty_Clob?&lt;br /&gt;&lt;br /&gt;One way would be to look at the length of the CLOB column, just like&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , text&lt;br /&gt;  3    from test&lt;br /&gt;  4   where length (text) = 0&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;        ID TEXT&lt;br /&gt;---------- ------------------&lt;br /&gt;         3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But than you would only select the Empty_Clob, not the NULL. Adding an extra predicate could solve this.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , text&lt;br /&gt;  3    from test&lt;br /&gt;  4   where length (text) = 0&lt;br /&gt;  5      or text is null&lt;br /&gt;  6  /&lt;br /&gt;&lt;br /&gt;        ID TEXT&lt;br /&gt;---------- ------------------&lt;br /&gt;         1&lt;br /&gt;         3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another way to do this, is using NULLIF. NULLIF is one of those "obscure" functions that are rarely used, at least I hardly ever see this function used.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , text&lt;br /&gt;  3    from test&lt;br /&gt;  4   where nullif (length (text), 0) is null&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;        ID TEXT&lt;br /&gt;---------- ----------------------------------&lt;br /&gt;         1&lt;br /&gt;         3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Personally I like this use of NULLIF. What do you think?&lt;br /&gt;&lt;br /&gt;Cleanup:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; drop table test&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Table dropped.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Links to the Oracle Documentation:&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions046.htm#SQLRF00635"&gt;DUMP&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions047.htm#SQLRF00636"&gt;EMPTY_CLOB&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions102.htm#SQLRF00681"&gt;NULLIF&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6514352380668692010?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6514352380668692010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/11/empty-clob-is-not-null-its-not-null.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6514352380668692010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6514352380668692010'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/11/empty-clob-is-not-null-its-not-null.html' title='An Empty Clob is not NULL, it&apos;s NOT NULL'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-9053926721105934202</id><published>2009-11-11T08:00:00.005+01:00</published><updated>2009-11-11T15:29:16.281+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Create Users with DBMS_METADATA</title><content type='html'>Not too long ago I wrote a blog on using DBMS_METADATA to extract DDL for tables, so when I got an assignment to migrate Users I immediately thought of using DBMS_METADATA to do this.&lt;br /&gt;&lt;br /&gt;The User Migration would consist of all the users in the database being renamed following a new convention. Don't ask why they wanted this, they had some very good reasons to want so.&lt;br /&gt;All the users had to be recreated along with all their privileges to their new name.&lt;br /&gt;At first I thought of writing all sorts of queries against the datadictionary which sounded like a daunting task. On second thought it dawned to me that a CREATE USER statement is DDL and DDL can be extracted using DBMS_METADATA.&lt;br /&gt;Google is your friend at times like that. Turned out you can use DBMS_METADATA to generate the CREATE USER statement, very easily.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The way we did it was create a table with the "old" and the "new" usernames, and join this to the appropriate DBA_* view. In the script below the name of this table is "USER_MIGRATIE" containing two columns: "old_name" and "new_name"&lt;br /&gt;Instead of boring you with the details, here is the script we used to generate the scripts&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;--create table user_migratie&lt;br /&gt;--(old_name varchar2(30)&lt;br /&gt;--,new_name varchar2(30)&lt;br /&gt;--);&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;   dbms_metadata.set_transform_param (dbms_metadata.session_transform, 'SQLTERMINATOR', true);&lt;br /&gt;   dbms_metadata.set_transform_param (dbms_metadata.session_transform, 'PRETTY', true);&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;set long 2000000000&lt;br /&gt;set head off&lt;br /&gt;set pages 0&lt;br /&gt;set feedback off&lt;br /&gt;set termout off&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;select replace (&lt;br /&gt;          dbms_metadata.get_ddl ('USER', upper (mig.old_name))&lt;br /&gt;               , upper (mig.old_name), upper (mig.new_name)&lt;br /&gt;       )&lt;br /&gt;  from user_migratie mig&lt;br /&gt;  join dba_users     usr&lt;br /&gt;    on usr.username = upper (old_name)&lt;br /&gt; union all&lt;br /&gt;select replace (&lt;br /&gt;          dbms_metadata.get_granted_ddl( 'TABLESPACE_QUOTA', upper (old_name))&lt;br /&gt;               , upper (old_name), upper (new_name)&lt;br /&gt;       )&lt;br /&gt;  from user_migratie mig&lt;br /&gt;  join dba_ts_quotas tq&lt;br /&gt;    on tq.username = upper (mig.old_name)&lt;br /&gt; union all &lt;br /&gt; select replace (&lt;br /&gt;          dbms_metadata.get_granted_ddl ('ROLE_GRANT', upper (old_name))&lt;br /&gt;                       , upper (old_name), upper (new_name)&lt;br /&gt;       )&lt;br /&gt;  from user_migratie mig&lt;br /&gt;  join dba_role_privs rpv&lt;br /&gt;    on rpv.grantee = upper (mig.old_name)&lt;br /&gt; union all&lt;br /&gt;select replace (&lt;br /&gt;          dbms_metadata.get_granted_ddl ('SYSTEM_GRANT', upper (old_name))&lt;br /&gt;               , upper (old_name), upper (new_name)&lt;br /&gt;       )&lt;br /&gt;  from user_migratie mig&lt;br /&gt;  join dba_sys_privs spv&lt;br /&gt;    on spv.grantee = upper (mig.old_name)&lt;br /&gt; union all&lt;br /&gt;select replace (&lt;br /&gt;          dbms_metadata.get_granted_ddl ('OBJECT_GRANT', upper (old_name))&lt;br /&gt;               , upper (old_name), upper (new_name)&lt;br /&gt;       )&lt;br /&gt;  from user_migratie mig&lt;br /&gt;  join dba_tab_privs tpv&lt;br /&gt;    on tpv.grantee = upper (mig.old_name)&lt;br /&gt; union all&lt;br /&gt;select case&lt;br /&gt;        when ((select count(*)&lt;br /&gt;                 from dba_role_privs&lt;br /&gt;                where grantee = upper (old_name)&lt;br /&gt;                  and default_role = 'YES'&lt;br /&gt;                  and rownum = 1&lt;br /&gt;               ) &gt; 0&lt;br /&gt;             )&lt;br /&gt;       then replace (&lt;br /&gt;            dbms_metadata.get_granted_ddl ('DEFAULT_ROLE', upper (old_name))&lt;br /&gt;               , upper (old_name), upper (new_name))&lt;br /&gt;       end&lt;br /&gt;  from user_migratie&lt;br /&gt; union all&lt;br /&gt;select replace (&lt;br /&gt;         dbms_metadata.get_ddl('PROFILE', profile)&lt;br /&gt;        , old_name, new_name)&lt;br /&gt;  from user_migratie mig&lt;br /&gt;  join dba_users     usr&lt;br /&gt;    on usr.username = upper (mig.old_name)&lt;br /&gt; where usr.profile &lt;&gt; 'DEFAULT'&lt;br /&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;spool create_user_like.sql&lt;br /&gt;/&lt;br /&gt;spool off&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ed create_user_like.sql&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Links I used for creating this script:&lt;br /&gt;&lt;a target="_blank" href="http://www.oraclealchemist.com/news/trick-1-copying-users-the-right-way/"&gt;Oracle Alchemist&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:494205100346718343"&gt;askTom&lt;/a&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_metada.htm#ARPLS640"&gt;Oracle Documentation&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-9053926721105934202?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/9053926721105934202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/11/dbmsmetadata-create-users.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/9053926721105934202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/9053926721105934202'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/11/dbmsmetadata-create-users.html' title='Create Users with DBMS_METADATA'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-7050088918348423731</id><published>2009-10-26T10:24:00.014+01:00</published><updated>2009-10-28T16:20:24.113+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Developer'/><title type='text'>Importing an Excel into an Oracle database</title><content type='html'>There are several ways to do this, but I will only show you how to do this using SQL Developer. Why? Because I just came across this functionality the other day.&lt;br /&gt;However there are some things you have to pay attention to, you might get frustrated with it otherwise.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Let's start with an Excel file.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/SuVrWcwtl9I/AAAAAAAAADE/kG9znQ5qgVM/s1600-h/Excel.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 310px; height: 320px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/SuVrWcwtl9I/AAAAAAAAADE/kG9znQ5qgVM/s320/Excel.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396837761895208914" /&gt;&lt;/a&gt;&lt;br /&gt;This (very) simple Excel sheet shows three columns. The "A" column is a DATE; the "B" column is a number with a Financial Format; the "C" column is a string.&lt;br /&gt;&lt;br /&gt;In the navigator of SQL Developer, when you right click on the Tables tab, you will get a context menu. One of the options is "Import Data ..."&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/SuVrWqiOUqI/AAAAAAAAADM/61I8nvCkZXs/s1600-h/import1.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 201px; height: 167px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/SuVrWqiOUqI/AAAAAAAAADM/61I8nvCkZXs/s320/import1.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396837765592535714" /&gt;&lt;/a&gt;&lt;br /&gt;An dialog is shown to locate your file.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/SuVrWdvWe7I/AAAAAAAAAC8/JU-HoT9XtiQ/s1600-h/Dialog.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 203px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/SuVrWdvWe7I/AAAAAAAAAC8/JU-HoT9XtiQ/s320/Dialog.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396837762157935538" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After you locate you file, the Data Import Wizard is shown. In this Wizard, you can specify which page in your Excel you want to impot. Remember that our first column was a DATE?&lt;br /&gt;Excel has it's own definition of what a DATE is. It's a numeric representation.&lt;br /&gt;As you can see in the screenshot below, the Import Wizard doesn't recognize this Date format. Let's see the effect of this, for now let's continue.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/SuVrW3x7twI/AAAAAAAAADU/zK0_ksYyCEE/s1600-h/ImportWizard1.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/SuVrW3x7twI/AAAAAAAAADU/zK0_ksYyCEE/s320/ImportWizard1.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396837769148086018" /&gt;&lt;/a&gt;&lt;br /&gt;Notice that you can also choose to have a script generated for you to create an External Table or to have a SQL*Loader file.&lt;br /&gt;&lt;br /&gt;In the second step of the Import Wizard you can choose which columns you want to include in the destination table. For this simple test, we will select all of the columns.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/SuVrW8owlsI/AAAAAAAAADc/NnELvlTLI8g/s1600-h/ImportWizard2.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/SuVrW8owlsI/AAAAAAAAADc/NnELvlTLI8g/s320/ImportWizard2.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396837770451785410" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Step three of the Wizard is to define the destination table. Notice two things here:&lt;br /&gt;&lt;ol&gt; &lt;li&gt;we changed the datatype of the first column to DATE&lt;/li&gt;&lt;br /&gt;&lt;li&gt;we changed the column name of the first colunmn to "date_col " in lowercase&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/SuVs87jvI4I/AAAAAAAAADk/uAVT1b0pSwU/s1600-h/ImportWizard3.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/SuVs87jvI4I/AAAAAAAAADk/uAVT1b0pSwU/s320/ImportWizard3.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839522508940162" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The fourth and last step includes a verification step&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/SuVs9F9MMbI/AAAAAAAAADs/XsdFu9N9y70/s1600-h/ImportWizard4.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/SuVs9F9MMbI/AAAAAAAAADs/XsdFu9N9y70/s320/ImportWizard4.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839525300056498" /&gt;&lt;/a&gt;&lt;br /&gt;Using the "Verify" button will do a sanity check of the data which is going to be imported. As you can see in the screenshot above, it does signal a mismatch between the data in the Excel sheet and the datatype of the destination column.&lt;br /&gt;Also notice that the "Voltooien" (Dutch for "Finish") is greyed out. You can reactivate this button by navigating back to the third page and forward to the fourth page in the wizard.&lt;br /&gt;&lt;br /&gt;We'll continue with "Voltooien" (="Finish"). The Message log will show you:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/SuVtESMjyLI/AAAAAAAAAEM/CqigXf3TGoQ/s1600-h/MessageLog.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 201px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/SuVtESMjyLI/AAAAAAAAAEM/CqigXf3TGoQ/s320/MessageLog.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839648844826802" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The table that we wanted is created. Inserting data into the didn't succeed though. If you look closely at the generated INSERT statement, you will notice that the column name is in lowercase. Hmmm...&lt;br /&gt;The table definition changed the lowercase to an uppercase columnname - and rightfully so: "Thou shalt never create case sensitive objects"&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/SuVtE_OmPDI/AAAAAAAAAEc/l1bNPNLqh40/s1600-h/TableDef.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 303px; height: 149px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/SuVtE_OmPDI/AAAAAAAAAEc/l1bNPNLqh40/s320/TableDef.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839660932971570" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Let's correct the situation and use a CSV instead of a native Excel format.&lt;br /&gt;First save the Excel sheet as a CSV. But... first check your delimiter and change it if necessary to a comma.&lt;br /&gt;To change this you need to Customize the Regional Settings. As you can see in the screenshot below, the default list separator is a semicolon. Change this to a comma.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/SuVwrFAU8nI/AAAAAAAAAEk/5bZSooI2RmY/s1600-h/regionalsetting.bmp"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 266px; height: 320px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/SuVwrFAU8nI/AAAAAAAAAEk/5bZSooI2RmY/s320/regionalsetting.bmp" border="0" alt=""id="BLOGGER_PHOTO_ID_5396843613853643378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;After this step save your Excel file as a CSV.&lt;br /&gt;Use the Data Import Wizard as before now selecting the newly saved CSV file. The first column will now show you a "real" date instead of a number.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/SuVs9aYtLGI/AAAAAAAAAD0/CvfkC7mPEGA/s1600-h/ImportWizard5.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/SuVs9aYtLGI/AAAAAAAAAD0/CvfkC7mPEGA/s320/ImportWizard5.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839530784173154" /&gt;&lt;/a&gt;&lt;br /&gt;Continuing with the Import Wizard, we now will use an uppercase column name, use the correct datatype and provide the format mask. For the number column in the CSV you will need to provide the appropriate Scale and Precision in order to load the data correctly. This is not shown in the screenshot.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/SuVs9j4jZGI/AAAAAAAAAD8/3l8VP1P3alI/s1600-h/ImportWizard6.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/SuVs9j4jZGI/AAAAAAAAAD8/3l8VP1P3alI/s320/ImportWizard6.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839533333668962" /&gt;&lt;/a&gt;&lt;br /&gt;When we verify the data in the next tab, we will get a "SUCCES" on every check. "Voltooien" (= "Finish") and we're set.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/SuVs-M4HeOI/AAAAAAAAAEE/jE2k6RJavKU/s1600-h/ImportWizard7.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/SuVs-M4HeOI/AAAAAAAAAEE/jE2k6RJavKU/s320/ImportWizard7.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839544337692898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The table is created, the data is loaded into the table.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/SuVtEibaCVI/AAAAAAAAAEU/AXKUEziClHM/s1600-h/TableDef2.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 277px; height: 127px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/SuVtEibaCVI/AAAAAAAAAEU/AXKUEziClHM/s320/TableDef2.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5396839653202069842" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One last catch: The transaction is not automatically ended. You need to explicitly end it by either a COMMIT or a ROLLBACK.&lt;br /&gt;&lt;br /&gt;Just to recap:&lt;br /&gt;Next time to need to import data from an Excel sheet into an Oracle database, pay attention to the following points&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Verify you delimiter, change if necessary: Control panel --&gt; Regional setting --&gt; Customize --&gt; List Separator&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Save the Excel as a CSV&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Import the Data using the newly created CSV&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Rename columns if needed and enter these in Uppercase&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Provide the correct format mask when importing dates&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Provide the correct format mask when importing numbers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;End your transaction (COMMIT or ROLLBACK)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPDATE 28-10-2009&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/Suhgp8-CA4I/AAAAAAAAAEs/-AyfW8-HUUY/s1600-h/18+Oct.+28+16.15.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 205px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/Suhgp8-CA4I/AAAAAAAAAEs/-AyfW8-HUUY/s320/18+Oct.+28+16.15.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5397670427260748674" /&gt;&lt;/a&gt;&lt;br /&gt;A colleague of mine just pointed out that you can supply the delimiter when you import a CSV file. For whatever reason I completely missed this, first signs of aging? In the screenshot above I've circled this setting. Thank you, Peter for pointing this out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-7050088918348423731?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/7050088918348423731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/10/importing-excel-into-oracle-database.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7050088918348423731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/7050088918348423731'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/10/importing-excel-into-oracle-database.html' title='Importing an Excel into an Oracle database'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_tdqazgf19_s/SuVrWcwtl9I/AAAAAAAAADE/kG9znQ5qgVM/s72-c/Excel.gif' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-8340280610950358642</id><published>2009-10-13T16:56:00.006+02:00</published><updated>2009-10-14T08:01:58.878+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><title type='text'>Oracle PL/SQL Programming Preview</title><content type='html'>This post will be mainly in Dutch. It's about a Preview we are doing in our office in Nieuwegein, The Netherlands.&lt;br /&gt;The sessions will probably be in Dutch, but can be changed on the fly to English if desired.&lt;br /&gt;If you are not able to make it to our office, you still have a chance to see Patrick do his presentations in Atlanta, Georgia during the Oracle PL/SQL Programming Conference, November 10 and 11.&lt;br /&gt;You can register by following &lt;a href="http://www.amis.nl/aanmelden_evenement.php?aanmelden_voor=AMIS%20Query:%20Oracle%20PL/SQL%20Programming%20conference%20Preview.%20Three%20Gems%20for%20Oracle%20Database%20Developers&amp;datum=2009-10-20&amp;preview=&amp;draft=" target="_blank"&gt;this link.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Our Event in Nieuwegein will be on &lt;span style="font-weight:bold;"&gt;October 20&lt;/span&gt; (Thanks Erik for pointing out the date was missing)&lt;br /&gt;&lt;br /&gt;Here is the agenda for this evening:&lt;br /&gt;&lt;br /&gt;De Oracle PL/SQL Programming conference vind dit jaar in Atlanta, Georgia plaats. Patrick Barel is daarbij om een drietal presentaties te geven. Voor degene die niet aanwezig zullen zijn in Atlanta vind deze OPP-Preview plaats.&lt;br /&gt;Tijdens deze KC zal Patrick twee van deze presentaties geven.&lt;br /&gt;Na het diner zal Alex een re-run geven van zijn ODTUG presentatie: "SQL Holmes". &lt;br /&gt; &lt;br /&gt;Het programma voor deze avond:&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;16:30&lt;br /&gt;"Pipelined table functions" - Patrick Barel&lt;br /&gt;&lt;/span&gt;Pipelined table functions offer an ideal convergence of the elegance and simplicity of PL/SQL with the performance of SQL. Complex data transformations are effortless to develop and support with PL/SQL, yet to achieve high-performance data processing, we often resort to set-based SQL solutions. Pipelined functions bridge the gap between the two methods effortlessly, but they also have some unique performance features of their own, making them a superb performance optimization tool.&lt;br /&gt; &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;18:00&lt;br /&gt;diner: Chinees&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Na diner (rond 19:00): &lt;br /&gt;"SQL Holmes: The Case of the Missing Performance" - Alex Nuijten&lt;br /&gt;&lt;/span&gt;During this presentation, a case study is unfolded to reveal the true cause of a slow performing query. Did the database just "have a bad day"? Was the evil DBA to blame? The PL/SQL developer who didn't get enough coffee? Or was it the application sending the "wrong" query in the first place?&lt;br /&gt;In this classic "whodunnit" you will take a tour past the crime scene. Investigate the query, use the tools of the trade and collect all the relevant information. Follow the trail to uncover the truth and nothing but the truth...&lt;br /&gt; &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;20:00&lt;br /&gt;Ter afsluiting:&lt;br /&gt;"Optimizing SQL with Collections" - Patrick Barel&lt;br /&gt;&lt;/span&gt;Collections (array-like structures in PL/SQL) are used in two of the most important performance features of PL/SQL: FORALL and BULK COLLECT. This session demonstrates the power of these features and offers in-depth guidance on how to apply them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-8340280610950358642?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/8340280610950358642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/10/oracle-plsql-programming-preview.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8340280610950358642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8340280610950358642'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/10/oracle-plsql-programming-preview.html' title='Oracle PL/SQL Programming Preview'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5852156740315211203</id><published>2009-10-09T07:30:00.001+02:00</published><updated>2009-10-09T07:30:00.732+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Just the plain DDL, please.. DBMS_METADATA</title><content type='html'>The other day I needed to get some DDL statements from the datadictionary. In the old days you could write your own queries to extract all this. Nowadays you can use the built in package DBMS_METADATA to get the DDL for you.&lt;br /&gt;Let's take a look at how you can use this. The DDL that I want to extract is the infamous EMP table, normally in the SCOTT schema.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table emp&lt;br /&gt;  2  as&lt;br /&gt;  3  select *&lt;br /&gt;  4    from scott.emp&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To make it a little more interesting, we also add the DEPT table and place some constraints on them. &lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table dept&lt;br /&gt;  2  as&lt;br /&gt;  3  select *&lt;br /&gt;  4    from scott.dept&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; alter table dept&lt;br /&gt;  2  add constraint dept_pk primary key (deptno)&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; alter table emp&lt;br /&gt;  2  add constraint emp_dept_fk foreign key (deptno) references dept (deptno)&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you use DBMS_METADATA just like that, you might get more than you asked for.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; set long 5000&lt;br /&gt;SQL&gt; select dbms_metadata.get_ddl('TABLE', 'EMP')&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;DBMS_METADATA.GET_DDL('TABLE','EMP')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;  CREATE TABLE "ALEX"."EMP"&lt;br /&gt;   (    "EMPNO" NUMBER(4,0),&lt;br /&gt;        "ENAME" VARCHAR2(10),&lt;br /&gt;        "JOB" VARCHAR2(9),&lt;br /&gt;        "MGR" NUMBER(4,0),&lt;br /&gt;        "HIREDATE" DATE,&lt;br /&gt;        "SAL" NUMBER(7,2),&lt;br /&gt;        "COMM" NUMBER(7,2),&lt;br /&gt;        "DEPTNO" NUMBER(2,0),&lt;br /&gt;         CONSTRAINT "EMP_DEPT_FK" FOREIGN KEY ("DEPTNO")&lt;br /&gt;          REFERENCES "ALEX"."DEPT" ("DEPTNO") ENABLE&lt;br /&gt;   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING&lt;br /&gt;  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645&lt;br /&gt;  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)&lt;br /&gt;  TABLESPACE "USERS"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can in the code block above, you get the complete DDL including the storage clauses and the constraints. If this is not what you want, and I didn't want all this, than you need to modify some transformation parameters.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_metadata.set_transform_param (dbms_metadata.session_transform,'STORAGE',false);&lt;br /&gt;  3     dbms_metadata.set_transform_param (dbms_metadata.session_transform,'TABLESPACE',false);&lt;br /&gt;  4     dbms_metadata.set_transform_param (dbms_metadata.session_transform,'SEGMENT_ATTRIBUTES', false);&lt;br /&gt;  5     dbms_metadata.set_transform_param (dbms_metadata.session_transform,'REF_CONSTRAINTS', false);&lt;br /&gt;  6     dbms_metadata.set_transform_param (dbms_metadata.session_transform,'CONSTRAINTS', false);&lt;br /&gt;  7  end;&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each of these transformation parameters take out bit by bit parts of the generated DDL. After running the above anonymous block we will get the following result.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select dbms_metadata.get_ddl ('TABLE', 'EMP')&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;DBMS_METADATA.GET_DDL('TABLE','EMP')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;&lt;br /&gt;  CREATE TABLE "ALEX"."EMP"&lt;br /&gt;   (    "EMPNO" NUMBER(4,0),&lt;br /&gt;        "ENAME" VARCHAR2(10),&lt;br /&gt;        "JOB" VARCHAR2(9),&lt;br /&gt;        "MGR" NUMBER(4,0),&lt;br /&gt;        "HIREDATE" DATE,&lt;br /&gt;        "SAL" NUMBER(7,2),&lt;br /&gt;        "COMM" NUMBER(7,2),&lt;br /&gt;        "DEPTNO" NUMBER(2,0)&lt;br /&gt;   )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's more like it, just what I was after. The plain DDL for the EMP table.&lt;br /&gt;One of the nice things is that you don't need to modify all the transformation parameters again to go back to the default. They made it really easy to return to the default settings:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_metadata.set_transform_param (dbms_metadata.session_transform, 'DEFAULT');&lt;br /&gt;  3  end;&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_metada.htm#ARPLS640" target="_blank"&gt;Documentation on DBMS_METADATA&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5852156740315211203?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5852156740315211203/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/09/just-plain-ddl-please-dbmsmetadata.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5852156740315211203'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5852156740315211203'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/09/just-plain-ddl-please-dbmsmetadata.html' title='Just the plain DDL, please.. DBMS_METADATA'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1570406890277505847</id><published>2009-10-06T13:27:00.007+02:00</published><updated>2009-10-22T16:19:29.163+02:00</updated><title type='text'>OPP 5: The Big Fat Book on PL/SQL</title><content type='html'>Yesterday I got the &lt;a href="http://oreilly.com/catalog/9780596514464/" target="blank"&gt;fifth edition of "Oracle PL/SQL Programming"&lt;/a&gt; by Steven Feuerstein. Another 1200+ pages on PL/SQL. This edition has been updated to cover versions through Oracle 11gR2. &lt;br /&gt;It is loaded with the latest on Oracle PL/SQL Programming, hence the title. &lt;br /&gt;When I started programming in PL/SQL I had a great mentor. When I told him I had a hard time writing PL/SQL, he advised me to get Feuerstein's book (the second edition was just out). &lt;br /&gt;Immediately I loved it, couldn't put it down. I dragged this book everywhere. Reading it in the traffic jam on my way to work, reading it before I went to sleep. By the time I finished that book, people at work started asking me questions about PL/SQL. Next to my copy of the Second Edition are also the Third and the Fourth edition. And now, I need to clear some space on the shelve, the fifth edition will take it's place soon. After I'm finished reading it.&lt;br /&gt;I'm sure this book is a must have for anyone who uses PL/SQL on a daily basis.&lt;br /&gt;&lt;br /&gt;I did mention in &lt;a href="http://nuijten.blogspot.com/2009/02/value-error-and-invalid-number.html" target="_blank"&gt;an earlier blogpost&lt;/a&gt; that I was reviewing some chapters for an upcoming book, well it was this book. And needless to say I'm very proud that I was mentioned in the preface. But I have to say I just submitted the first errata. Not a biggie, but still... My lastname in the book was spelled Nuitjen, while it should be Nuijten. Oh well...&lt;br /&gt;&lt;br /&gt;Update 22-10-2009:&lt;br /&gt;My friend and colleague Patrick Barel pointed out that Google knows who I am ;)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/SuBp1C8SbtI/AAAAAAAAAC0/pSJnGOKmb4k/s1600-h/01+Oct.+22+16.17.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 211px; height: 134px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/SuBp1C8SbtI/AAAAAAAAAC0/pSJnGOKmb4k/s320/01+Oct.+22+16.17.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5395428713633574610" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-1570406890277505847?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/1570406890277505847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/10/opp-5-big-fat-book-on-plsql.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1570406890277505847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1570406890277505847'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/10/opp-5-big-fat-book-on-plsql.html' title='OPP 5: The Big Fat Book on PL/SQL'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_tdqazgf19_s/SuBp1C8SbtI/AAAAAAAAAC0/pSJnGOKmb4k/s72-c/01+Oct.+22+16.17.gif' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-147435632742134126</id><published>2009-09-28T10:09:00.003+02:00</published><updated>2010-11-01T16:39:48.363+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EBR'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>Planboard Symposium: Registration open</title><content type='html'>For the fourth time the Planboard Symposium will be held on November 17. This symposium is unique as it's "for DBA by DBA". This one day symposium will have 5 parallel sessions with lots of time for networking and open discussions.&lt;br /&gt;I am lucky enough to be one of the presentors and my topic will be "Continuous Database Application Evolution in Oracle Database 11g Release 2 ", a mouth full.&lt;br /&gt;This session will discuss a new feature of Oracle 11gR2: Edition Based Redefinition. Not just "a new feature", it's "&lt;span style="font-style:italic;"&gt;The&lt;/span&gt; new feature".&lt;br /&gt;&lt;br /&gt;You can register &lt;a href="http://www.planboard.com/symposium/" target="_blank"&gt; on the Planboard Symposium site&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Oh, and by the way... don't tell anyone I'm not a DBA... ssshhh...&lt;br /&gt;see you there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-147435632742134126?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/147435632742134126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/09/planboard-symposium-registration-open.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/147435632742134126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/147435632742134126'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/09/planboard-symposium-registration-open.html' title='Planboard Symposium: Registration open'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5667645349631244776</id><published>2009-09-24T12:01:00.004+02:00</published><updated>2009-09-24T13:05:02.328+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Carrying down values with Analytic Functions</title><content type='html'>The other day - yesterday actually - I got an email with a question regarding Analytic Functions.&lt;br /&gt;This was the requirement for the query:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;In a table there are a CODE, STARTDATE and VAL columns. The combination of CODE and STARTDATE are mandatory, the VAL is optional. I want to get an overview where the VAL column shows the latest (based on the STARTDATE) value. If there is no value filled out for a particular date, it should show the value of the latest entry for that particular CODE.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Let's take a look at an example to clarify the requirement. First of all the data in the table:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;COD STARTDATE VAL&lt;br /&gt;--- --------- ----------&lt;br /&gt;A   24-SEP-08 QRS&lt;br /&gt;A   24-OCT-08&lt;br /&gt;A   24-NOV-08&lt;br /&gt;A   24-DEC-08 a&lt;br /&gt;A   24-JAN-09&lt;br /&gt;A   24-FEB-09 XY&lt;br /&gt;A   24-MAR-09 ABC&lt;br /&gt;A   24-APR-09&lt;br /&gt;A   24-MAY-09&lt;br /&gt;B   24-DEC-08 BLA&lt;br /&gt;B   24-JAN-09&lt;br /&gt;B   24-FEB-09&lt;br /&gt;B   24-MAR-09 BLABLA&lt;br /&gt;B   24-APR-09&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see in the above output, there are two CODES ("A" and "B"). The combination of CODE and STARTDATE is unique. The last column (VAL) has some values filled out, not all.&lt;br /&gt;The desired output would be:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;COD STARTDATE VAL&lt;br /&gt;--- --------- ------&lt;br /&gt;A   24-SEP-08 QRS&lt;br /&gt;A   24-OCT-08 QRS&lt;br /&gt;A   24-NOV-08 QRS&lt;br /&gt;A   24-DEC-08 a&lt;br /&gt;A   24-JAN-09 a&lt;br /&gt;A   24-FEB-09 XY&lt;br /&gt;A   24-MAR-09 ABC&lt;br /&gt;A   24-APR-09 ABC&lt;br /&gt;A   24-MAY-09 ABC&lt;br /&gt;B   24-DEC-08 BLA&lt;br /&gt;B   24-JAN-09 BLA&lt;br /&gt;B   24-FEB-09 BLA&lt;br /&gt;B   24-MAR-09 BLABLA&lt;br /&gt;B   24-APR-09 BLABLA&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When the VAL column has no entry (IS NULL) the output should show the latest value of the VAL column - based on the Startdate within the CODE. Latest in this case means: the most recent value of the VAL column with regards to the STARTDATE column of the current record.&lt;br /&gt;&lt;br /&gt;Is the requirement clear? Time to solve it. I will show you two ways of solving this, there are possibly many more ways of getting the required output. One way is quite cumbersome, but will work in Oracle 8.1.6 EE and up. The other way is really trivial and works in Oracle 10g and up.&lt;br /&gt;&lt;br /&gt;First method, fully explained.&lt;br /&gt;Per CODE, we need to identify which VAL should be carried down, until a different VAL is encountered. To identify these subgroups we use a Case statement:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select code&lt;br /&gt;  2       , startdate&lt;br /&gt;  3       , val&lt;br /&gt;  4       , case&lt;br /&gt;  5           when val is not null&lt;br /&gt;  6           then 1&lt;br /&gt;  7         end new_val&lt;br /&gt;  8    from tbl&lt;br /&gt;  9   order by code&lt;br /&gt; 10          , startdate&lt;br /&gt; 11  /&lt;br /&gt;&lt;br /&gt;COD STARTDATE VAL           NEW_VAL&lt;br /&gt;--- --------- ---------- ----------&lt;br /&gt;A   24-SEP-08 QRS                 1&lt;br /&gt;A   24-OCT-08&lt;br /&gt;A   24-NOV-08&lt;br /&gt;A   24-DEC-08 a                   1&lt;br /&gt;A   24-JAN-09&lt;br /&gt;A   24-FEB-09 XY                  1&lt;br /&gt;A   24-MAR-09 ABC                 1&lt;br /&gt;A   24-APR-09&lt;br /&gt;A   24-MAY-09&lt;br /&gt;B   24-DEC-08 BLA                 1&lt;br /&gt;B   24-JAN-09&lt;br /&gt;B   24-FEB-09&lt;br /&gt;B   24-MAR-09 BLABLA              1&lt;br /&gt;B   24-APR-09&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the column NEW_VAL shows the marker for each subgroup. Using the "running total" technique we can clearly see which records belong together.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select code&lt;br /&gt;  2       , startdate&lt;br /&gt;  3       , val&lt;br /&gt;  4       , sum (case&lt;br /&gt;  5                when val is not null&lt;br /&gt;  6                then 1&lt;br /&gt;  7              end&lt;br /&gt;  8             ) over (partition by code&lt;br /&gt;  9                         order by startdate&lt;br /&gt; 10                    ) new_val&lt;br /&gt; 11    from tbl&lt;br /&gt; 12   order by code&lt;br /&gt; 13          , startdate&lt;br /&gt; 14  /&lt;br /&gt;&lt;br /&gt;COD STARTDATE VAL           NEW_VAL&lt;br /&gt;--- --------- ---------- ----------&lt;br /&gt;A   24-SEP-08 QRS                 1&lt;br /&gt;A   24-OCT-08                     1&lt;br /&gt;A   24-NOV-08                     1&lt;br /&gt;A   24-DEC-08 a                   2&lt;br /&gt;A   24-JAN-09                     2&lt;br /&gt;A   24-FEB-09 XY                  3&lt;br /&gt;A   24-MAR-09 ABC                 4&lt;br /&gt;A   24-APR-09                     4&lt;br /&gt;A   24-MAY-09                     4&lt;br /&gt;B   24-DEC-08 BLA                 1&lt;br /&gt;B   24-JAN-09                     1&lt;br /&gt;B   24-FEB-09                     1&lt;br /&gt;B   24-MAR-09 BLABLA              2&lt;br /&gt;B   24-APR-09                     2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With the distinction made - you can see the different subgroups, each having a unique number within the partition, we need the first value of the VAL column per subgroup. For that we will use the FIRST_VALUE function. Notice that the partitioning clause in the FIRST_VALUE is using the subgroups we defined in the previous section as well as the CODE.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select code&lt;br /&gt;  2       , startdate&lt;br /&gt;  3       , first_value (val)&lt;br /&gt;  4             over (partition by code&lt;br /&gt;  5                              , new_val&lt;br /&gt;  6                       order by startdate&lt;br /&gt;  7                  ) new_val&lt;br /&gt;  8    from (&lt;br /&gt;  9  select code&lt;br /&gt; 10       , startdate&lt;br /&gt; 11       , val&lt;br /&gt; 12       , sum (case&lt;br /&gt; 13                when val is not null&lt;br /&gt; 14                then 1&lt;br /&gt; 15              end&lt;br /&gt; 16             ) over (partition by code&lt;br /&gt; 17                         order by startdate&lt;br /&gt; 18                    ) new_val&lt;br /&gt; 19    from tbl&lt;br /&gt; 20  )&lt;br /&gt; 21   order by code&lt;br /&gt; 22          , startdate&lt;br /&gt; 23  /&lt;br /&gt;&lt;br /&gt;COD STARTDATE NEW_VAL&lt;br /&gt;--- --------- ----------&lt;br /&gt;A   24-SEP-08 QRS&lt;br /&gt;A   24-OCT-08 QRS&lt;br /&gt;A   24-NOV-08 QRS&lt;br /&gt;A   24-DEC-08 a&lt;br /&gt;A   24-JAN-09 a&lt;br /&gt;A   24-FEB-09 XY&lt;br /&gt;A   24-MAR-09 ABC&lt;br /&gt;A   24-APR-09 ABC&lt;br /&gt;A   24-MAY-09 ABC&lt;br /&gt;B   24-DEC-08 BLA&lt;br /&gt;B   24-JAN-09 BLA&lt;br /&gt;B   24-FEB-09 BLA&lt;br /&gt;B   24-MAR-09 BLABLA&lt;br /&gt;B   24-APR-09 BLABLA&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And there you have it, the required result. But it takes a lot of typing. In Oracle 10g we can get the same results, but a lot simpler.&lt;br /&gt;Newly added in Oracle 10g is the IGNORE NULLS clause. And it does exactly what it says, it ignores nulls.&lt;br /&gt;In the result we want to get the last value, per partition within the window that gets larger with the current row. The default windowing clause is Rows Unbounded Preceding (the docs say Range Unbounded Preceding, but I believe this is wrong. Range only works for a numeric offset like with Dates and Numbers)&lt;br /&gt;In the following query, I inserted the windowing clause in there. Just to be very explicit.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select code&lt;br /&gt;  2       , startdate&lt;br /&gt;  3       , val&lt;br /&gt;  4       , last_value (val ignore nulls)&lt;br /&gt;  5              over (partition by code&lt;br /&gt;  6                        order by startdate&lt;br /&gt;  7                        rows between unbounded preceding&lt;br /&gt;  8                                 and current row&lt;br /&gt;  9                   ) new_val&lt;br /&gt; 10    from tbl&lt;br /&gt; 11  ;&lt;br /&gt;&lt;br /&gt;COD STARTDATE VAL        NEW_VAL&lt;br /&gt;--- --------- ---------- ----------&lt;br /&gt;A   24-SEP-08 QRS        QRS&lt;br /&gt;A   24-OCT-08            QRS&lt;br /&gt;A   24-NOV-08            QRS&lt;br /&gt;A   24-DEC-08 a          a&lt;br /&gt;A   24-JAN-09            a&lt;br /&gt;A   24-FEB-09 XY         XY&lt;br /&gt;A   24-MAR-09 ABC        ABC&lt;br /&gt;A   24-APR-09            ABC&lt;br /&gt;A   24-MAY-09            ABC&lt;br /&gt;B   24-DEC-08 BLA        BLA&lt;br /&gt;B   24-JAN-09            BLA&lt;br /&gt;B   24-FEB-09            BLA&lt;br /&gt;B   24-MAR-09 BLABLA     BLABLA&lt;br /&gt;B   24-APR-09            BLABLA&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As the Windowing Clause is the default, you can also omit it.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select code&lt;br /&gt;  2       , startdate&lt;br /&gt;  3       , last_value (val ignore nulls)&lt;br /&gt;  4              over (partition by code&lt;br /&gt;  5                        order by startdate&lt;br /&gt;  6                   ) new_val&lt;br /&gt;  7    from tbl&lt;br /&gt;  8  ;&lt;br /&gt;&lt;br /&gt;COD STARTDATE NEW_VAL&lt;br /&gt;--- --------- ----------&lt;br /&gt;A   24-SEP-08 QRS&lt;br /&gt;A   24-OCT-08 QRS&lt;br /&gt;A   24-NOV-08 QRS&lt;br /&gt;A   24-DEC-08 a&lt;br /&gt;A   24-JAN-09 a&lt;br /&gt;A   24-FEB-09 XY&lt;br /&gt;A   24-MAR-09 ABC&lt;br /&gt;A   24-APR-09 ABC&lt;br /&gt;A   24-MAY-09 ABC&lt;br /&gt;B   24-DEC-08 BLA&lt;br /&gt;B   24-JAN-09 BLA&lt;br /&gt;B   24-FEB-09 BLA&lt;br /&gt;B   24-MAR-09 BLABLA&lt;br /&gt;B   24-APR-09 BLABLA&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5667645349631244776?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5667645349631244776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/09/carrying-down-values-with-analytic.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5667645349631244776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5667645349631244776'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/09/carrying-down-values-with-analytic.html' title='Carrying down values with Analytic Functions'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6320550154236059240</id><published>2009-07-29T09:58:00.002+02:00</published><updated>2009-07-29T10:35:41.489+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MERGE'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>DELETE in the MERGE statement</title><content type='html'>The Merge statement was introduced in Oracle 9i and improved upon in Oracle 10g.&lt;br /&gt;In Oracle 9i only the INSERT and UPDATE parts were supported, in Oracle 10g DELETE was added. The "merge_update_clause" and "merge_insert_clause" became optional.&lt;br /&gt;The basic syntax for the MERGE statement:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tdqazgf19_s/SnAJrdvL0KI/AAAAAAAAACc/SFH1_dKK-5I/s1600-h/merge.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 137px;" src="http://3.bp.blogspot.com/_tdqazgf19_s/SnAJrdvL0KI/AAAAAAAAACc/SFH1_dKK-5I/s320/merge.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5363797798519820450" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;DELETE can only occur in the "merge_update_clause" of the above schema. This means that it must occur in the WHEN MATCHED THEN clause.&lt;br /&gt;&lt;br /&gt;Until recent, I missed this part of the description of the "merge_update_clause" concerning the DELETE operation. First I will show you what I thought, then I'll show you where the behavior is documented.&lt;br /&gt;&lt;br /&gt;First we'll create a table with two columns:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from v$version&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;BANNER&lt;br /&gt;----------------------------------------------------------------&lt;br /&gt;Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi&lt;br /&gt;PL/SQL Release 10.2.0.3.0 - Production&lt;br /&gt;CORE    10.2.0.3.0      Production&lt;br /&gt;TNS for IBM/AIX RISC System/6000: Version 10.2.0.3.0 - Productio&lt;br /&gt;NLSRTL Version 10.2.0.3.0 - Production&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; create table t&lt;br /&gt;  2  as&lt;br /&gt;  3  select rownum rn&lt;br /&gt;  4       , 'A' ind&lt;br /&gt;  5    from all_objects&lt;br /&gt;  6   where rownum &lt;= 5&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; update t&lt;br /&gt;  2     set ind = 'D'&lt;br /&gt;  3   where rn = 5&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;1 row updated.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from t&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;        RN I&lt;br /&gt;---------- -&lt;br /&gt;         1 A&lt;br /&gt;         2 A&lt;br /&gt;         3 A&lt;br /&gt;         4 A&lt;br /&gt;         5 D&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that the last record, with RN 5, has an Ind "D".&lt;br /&gt;&lt;br /&gt;Next we will merge a record into this table.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; merge into t&lt;br /&gt;  2  using (select 3 i&lt;br /&gt;  3              , 'D' ind &lt;br /&gt;  4           from dual&lt;br /&gt;  5        ) dat&lt;br /&gt;  6     on (t.rn = dat.i)&lt;br /&gt;  7   when matched then&lt;br /&gt;  8     update set t.ind  = dat.ind&lt;br /&gt;  9     delete where t.ind = 'D' --&lt;--- Here is the DELETE&lt;br /&gt; 10  /&lt;br /&gt;&lt;br /&gt;1 row merged.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Only one row? Shouldn't that be two rows? One for the UPDATE and one for the DELETE?&lt;br /&gt;Question for you: Which record(s) is (are) affected by this statement?&lt;br /&gt;&lt;br /&gt;My wrong assumption was this:&lt;br /&gt;Record with RN 3 has had the IND column changed to "D" and all records with IND "D" are removed. Effectively removing records with RN 3 and 5.&lt;br /&gt;&lt;br /&gt;Now the quote from the documentation.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;The only rows affected by this clause are those rows in the destination table that are updated by the merge operation.&lt;br /&gt;&lt;/blockquote&gt; &lt;br /&gt;&lt;br /&gt;This means that records in the destination table are not deleted when they are not updated by the MERGE first.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from t&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;        RN I&lt;br /&gt;---------- -&lt;br /&gt;         1 A&lt;br /&gt;         2 A&lt;br /&gt;         4 A&lt;br /&gt;         5 D&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see the record with RN 5 is still in the table. Because it was not updated in the merge, it was not deleted.&lt;br /&gt;In order to remove some records from the table using the MERGE statement, you need to update these records first. It is not possible to dismiss the UPDATE statement from the MERGE:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; merge into t&lt;br /&gt;  2  using (select 3 i&lt;br /&gt;  3              , 'D' ind &lt;br /&gt;  4           from dual&lt;br /&gt;  5        ) dat&lt;br /&gt;  6     on (t.rn = dat.i)&lt;br /&gt;  7   when matched then&lt;br /&gt;  8     delete where t.ind = 'D' --&lt;--- Here is the DELETE&lt;br /&gt;  9  /&lt;br /&gt;   delete where t.ind = 'D' --&lt;--- Here is the DELETE&lt;br /&gt;   *&lt;br /&gt;ERROR at line 8:&lt;br /&gt;ORA-00905: missing keyword&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;... learn something every day.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_9016.htm#SQLRF01606" target="_blank"&gt;documentation link&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6320550154236059240?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6320550154236059240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/07/delete-in-merge-statement.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6320550154236059240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6320550154236059240'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/07/delete-in-merge-statement.html' title='DELETE in the MERGE statement'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_tdqazgf19_s/SnAJrdvL0KI/AAAAAAAAACc/SFH1_dKK-5I/s72-c/merge.gif' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1356091396555006531</id><published>2009-07-21T10:26:00.004+02:00</published><updated>2009-07-27T15:14:48.501+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>NVL2 - useful? Two use cases</title><content type='html'>Sometimes you encounter a function that makes you wonder why Oracle made it. NVL2 is one of those functions, at least I think so. Even though it was introduced quite a long time ago, I've never used it in production code.&lt;br /&gt;Until now that is.&lt;br /&gt;NVL2 was introduced in Oracle 8i (&lt;a href="http://www.oracle.com/pls/db102/print_hit_summary?search_string=nvl2" target="_blank"&gt;8.1.7 according to Tahiti.Oracle.com&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Syntax&lt;/b&gt;&lt;br /&gt;The NVL2 function takes three arguments. The first argument (could also be an expression) is evaluated for NULL or NOT NULL. When this argument evaluates to NOT NULL, the second expression is returned. When the first argument evaluates to NULL then last (third) expression is returned.&lt;br /&gt;It works like this pseudocode:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;if argument1 is not null&lt;br /&gt;then &lt;br /&gt;   argument2;&lt;br /&gt;else&lt;br /&gt;   argument3;&lt;br /&gt;end if;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;First Use Case: Concatenate strings with an optional delimiter&lt;/b&gt;&lt;br /&gt;In The Netherlands, where I'm from, you can choose the way your last name should be used after marriage.&lt;br /&gt;In general there are four options, say your lastname is "Jansen" and your partners name is "de Vries" - both very common names in The Netherlands - then these are your options:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;keep your own lastname: Jansen&lt;/li&gt;&lt;br /&gt;&lt;li&gt;use the lastname of your partner: de Vries&lt;/li&gt;&lt;br /&gt;&lt;li&gt;your own lastname hyphen partner lastname: Jansen - de Vries&lt;/li&gt;&lt;br /&gt;&lt;li&gt;partner lastname hyphen own lastname: de Vries - Jansen&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Say we have a table which store your name, your partners name and your preference of "name usage"&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;drop table names&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;create table names&lt;br /&gt;(lastname         varchar2(35)&lt;br /&gt;,partner_lastname varchar2(35)&lt;br /&gt;,name_usage       varchar2(2)&lt;br /&gt;)&lt;br /&gt;/&lt;br /&gt;insert into names values ('Jansen', 'de Vries', 'O') -- Own&lt;br /&gt;/&lt;br /&gt;insert into names values ('Jansen', 'de Vries', 'P') -- Partner&lt;br /&gt;/&lt;br /&gt;insert into names values ('Jansen', 'de Vries', 'OP') -- Own - Partner&lt;br /&gt;/&lt;br /&gt;insert into names values ('Jansen', 'de Vries', 'PO') -- Partner - Own&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using a simple Case Expression we can have the names the way we want them&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select case name_usage&lt;br /&gt;  2         when 'O'  then lastname&lt;br /&gt;  3         when 'P'  then partner_lastname&lt;br /&gt;  4         when 'OP' then lastname||' - '||partner_lastname&lt;br /&gt;  5         when 'PO' then partner_lastname||' - '||lastname&lt;br /&gt;  6         end full_lastname&lt;br /&gt;  7    from names&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;FULL_LASTNAME&lt;br /&gt;------------------------------------------------------------&lt;br /&gt;Jansen&lt;br /&gt;de Vries&lt;br /&gt;Jansen - de Vries&lt;br /&gt;de Vries - Jansen&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looks good sofar. But,... there is always a but... The data is not always up to par. Some records have "inappropriate" name_usage indicators:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;insert into names values ('Jansen', null, 'O') -- Own&lt;br /&gt;/&lt;br /&gt;insert into names values ('Jansen', null, 'OP') -- Own - Partner&lt;br /&gt;/&lt;br /&gt;insert into names values ('Jansen', null, 'PO') -- Partner - Own&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I left out the Partner usage when there is no Partner - this would just be a "regular" NVL.&lt;br /&gt;&lt;br /&gt;When we run the query we used before, we will get this output&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;FULL_LASTNAME&lt;br /&gt;------------------------&lt;br /&gt;Jansen&lt;br /&gt;de Vries&lt;br /&gt;Jansen - de Vries&lt;br /&gt;de Vries - Jansen&lt;br /&gt;Jansen&lt;br /&gt;Jansen -&lt;br /&gt; - Jansen&lt;br /&gt;&lt;br /&gt;7 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last two look strange, don't you agree? There are hyphens there when they shouldn't be.&lt;br /&gt;We could of course write another Case Expression nested inside our already present Case:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select case name_usage&lt;br /&gt;  2         when 'O'  then lastname&lt;br /&gt;  3         when 'P'  then partner_lastname&lt;br /&gt;  4         when 'OP' then lastname||case&lt;br /&gt;  5                                  when partner_lastname is not null&lt;br /&gt;  6                                  then  ' - '||partner_lastname&lt;br /&gt;  7                                  end&lt;br /&gt;  8         when 'PO' then partner_lastname||case&lt;br /&gt;  9                                          when partner_lastname is not null&lt;br /&gt; 10                                          then  ' - '||lastname&lt;br /&gt; 11                                          end&lt;br /&gt; 12         end full_lastname&lt;br /&gt; 13    from names&lt;br /&gt; 14  /&lt;br /&gt;&lt;br /&gt;FULL_LASTNAME&lt;br /&gt;-------------------------------------------------------------------------&lt;br /&gt;Jansen&lt;br /&gt;de Vries&lt;br /&gt;Jansen - de Vries&lt;br /&gt;de Vries - Jansen&lt;br /&gt;Jansen&lt;br /&gt;Jansen&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;7 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see the SQL statement becomes quite bulky, but very easy to understand. It's also possible to use NVL2 to do the same&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select case name_usage&lt;br /&gt;  2         when 'O'  then lastname&lt;br /&gt;  3         when 'P'  then partner_lastname&lt;br /&gt;  4         when 'OP' then lastname ||nvl2 (partner_lastname, ' - ', null)||partner_lastname&lt;br /&gt;  5         when 'PO' then partner_lastname ||nvl2 (partner_lastname, ' - ', null)||lastname&lt;br /&gt;  6         end full_lastname&lt;br /&gt;  7    from names&lt;br /&gt;  8  /&lt;br /&gt;&lt;br /&gt;FULL_LASTNAME&lt;br /&gt;-------------------------------------------------------------------------&lt;br /&gt;Jansen&lt;br /&gt;de Vries&lt;br /&gt;Jansen - de Vries&lt;br /&gt;de Vries - Jansen&lt;br /&gt;Jansen&lt;br /&gt;Jansen&lt;br /&gt;Jansen&lt;br /&gt;&lt;br /&gt;7 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Nice.&lt;br /&gt;&lt;br /&gt;Of course the same thing can be achieved using the good ol' DECODE: &lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select case name_usage&lt;br /&gt;  2         when 'O'  then lastname&lt;br /&gt;  3         when 'P'  then partner_lastname&lt;br /&gt;  4         when 'OP' then lastname ||decode (partner_lastname&lt;br /&gt;  5                                          , null, null, ' - ')||partner_lastname&lt;br /&gt;  6         when 'PO' then partner_lastname ||decode (partner_lastname&lt;br /&gt;  7                                                  , null, null, ' - ')||lastname&lt;br /&gt;  8         end full_lastname&lt;br /&gt;  9    from names&lt;br /&gt; 10  /&lt;br /&gt;&lt;br /&gt;FULL_LASTNAME&lt;br /&gt;-------------------------------------------------------------------------&lt;br /&gt;Jansen&lt;br /&gt;de Vries&lt;br /&gt;Jansen - de Vries&lt;br /&gt;de Vries - Jansen&lt;br /&gt;Jansen&lt;br /&gt;Jansen&lt;br /&gt;Jansen&lt;br /&gt;&lt;br /&gt;7 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Second Use Case: Implementing an Arc&lt;/b&gt;&lt;br /&gt;To implement an Arc relation in the database, the NVL2 function could also be useful.&lt;br /&gt;Let's start with the model&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;drop table t3&lt;br /&gt;/&lt;br /&gt;drop table t1&lt;br /&gt;/&lt;br /&gt;drop table t2&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;create table t1&lt;br /&gt;(c1 int primary key)&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;create table t2&lt;br /&gt;(c1 int primary key)&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;create table t3&lt;br /&gt;(c1 int primary key&lt;br /&gt;,t1_c1 int references t1&lt;br /&gt;,t2_c1 int references t2&lt;br /&gt;)&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/SmarOQ9Aq-I/AAAAAAAAACU/coorYtEv5LQ/s1600-h/arc.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 281px; height: 208px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/SmarOQ9Aq-I/AAAAAAAAACU/coorYtEv5LQ/s320/arc.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5361160667988536290" /&gt;&lt;/a&gt;&lt;br /&gt;The Arc should be implemented on the T3 table. Either the relation to T1 should be filled or the relation to T2 should be filled. They should not both be filled for the same record.&lt;br /&gt;This can be implemented with a Case Expression in a Check Constraint&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; alter table t3&lt;br /&gt;  2  add constraint arc_chk check (case &lt;br /&gt;  3                                when t1_c1 is not null &lt;br /&gt;  4                                 and t2_c1 is null&lt;br /&gt;  5                                then 1&lt;br /&gt;  6                                when t1_c1 is null&lt;br /&gt;  7                                 and t2_c1 is not null&lt;br /&gt;  8                                then 1&lt;br /&gt;  9                                else 0&lt;br /&gt; 10                                end = 1&lt;br /&gt; 11                                )&lt;br /&gt; 12  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The expression in the Check Constraint is &lt;br /&gt;&lt;blockquote&gt;the value of the Case Expression (either "1" or "0") must be equal to "1"&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;And to test our Arc implementation:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; insert into t1 values (1)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t2 values (2)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (10, 1, null)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (11, null, 2)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (12, 1, 2)&lt;br /&gt;  2  /&lt;br /&gt;insert into t3 values (12, 1, 2)&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02290: check constraint (ALEX.ARC_CHK) violated&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (13, null, null)&lt;br /&gt;  2  /&lt;br /&gt;insert into t3 values (13, null, null)&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02290: check constraint (ALEX.ARC_CHK) violated&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This seems to work, When either relation is filled, all is well. When both relations are filled, or none of the relations is, the Check Constraint is violated.&lt;br /&gt;&lt;br /&gt;Now let's do the same thing with NVL2.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; rollback&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;Rollback complete.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; alter table t3&lt;br /&gt;  2  drop constraint arc_chk&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; alter table t3&lt;br /&gt;  2  add constraint arc_chk check (nvl2 (t1_c1, 1, 0)&lt;br /&gt;  3                                + nvl2 (t2_c1, 1, 0)&lt;br /&gt;  4                                = 1)&lt;br /&gt;  5  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Check Constraint is a lot shorter, but does the same thing.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t1 values (1)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t2 values (2)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (10, 1, null)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (11, null, 2)&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (12, 1, 2)&lt;br /&gt;  2  /&lt;br /&gt;insert into t3 values (12, 1, 2)&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02290: check constraint (ALEX.ARC_CHK) violated&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt; &lt;br /&gt;SQL&gt; insert into t3 values (13, null, null)&lt;br /&gt;  2  /&lt;br /&gt;insert into t3 values (13, null, null)&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-02290: check constraint (ALEX.ARC_CHK) violated&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The NVL2 function can also take an expression as the first argument. Just for completeness I have taken this example from the documentation:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SELECT last_name, salary, NVL2(commission_pct, &lt;br /&gt;   salary + (salary * commission_pct), salary) income&lt;br /&gt;   FROM employees WHERE last_name like 'B%'&lt;br /&gt;   ORDER BY last_name;&lt;br /&gt;&lt;br /&gt;LAST_NAME                     SALARY     INCOME&lt;br /&gt;------------------------- ---------- ----------&lt;br /&gt;Baer                           10000      10000&lt;br /&gt;Baida                           2900       2900&lt;br /&gt;Banda                           6200       6882&lt;br /&gt;Bates                           7300       8468&lt;br /&gt;Bell                            4000       4000&lt;br /&gt;Bernstein                       9500      11970&lt;br /&gt;Bissot                          3300       3300&lt;br /&gt;Bloom                          10000      12100&lt;br /&gt;Bull                            4100       4100&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And there you have it, two use cases - and an example from the docs - for the NVL2 function.&lt;br /&gt;&lt;br /&gt;What would be a reason not to use the NVL2 function? One reason would be that the function is quite hard to read (comparable to interpreting DECODE).  Another would be that this function is not very well known.&lt;br /&gt;The Use Cases described above are the cases where I use the NVL2 function. Simply because it's so concise.  Matter of preference I think.&lt;br /&gt;Do you use it? And how - for which use cases - do you use it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-1356091396555006531?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/1356091396555006531/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/07/nvl2-useful-two-use-cases.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1356091396555006531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1356091396555006531'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/07/nvl2-useful-two-use-cases.html' title='NVL2 - useful? Two use cases'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_tdqazgf19_s/SmarOQ9Aq-I/AAAAAAAAACU/coorYtEv5LQ/s72-c/arc.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1104860229495832213</id><published>2009-07-07T12:00:00.004+02:00</published><updated>2009-07-21T11:54:50.905+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Making up Data with Partition Outer Join</title><content type='html'>Just the other day on the Oracle SQL and PL/SQL forum, someone asked on how to create non-existent rows. This post is not about generating a number of rows.&lt;br /&gt;It's about handling Sparse Data, when you want to fill in some missing records in your result set.&lt;br /&gt;First time I heard about this was in a blog written by Lucas Jellema. &lt;br /&gt;Let's first start out with the table and some test data:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; create table test_table1 &lt;br /&gt;  2  (status varchar2(15)&lt;br /&gt;  3  ,manager number&lt;br /&gt;  4  ,sales number);&lt;br /&gt;&lt;br /&gt;Table created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test_table1 values ('in process', 14, 100);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test_table1 values ('in process', 15, 10);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test_table1 values ('in process', 15, 40);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test_table1 values ('done', 14, 200);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;&lt;br /&gt;SQL&gt; insert into test_table1 values ('done', 16, 50);&lt;br /&gt;&lt;br /&gt;1 row created.&lt;br /&gt;SQL&gt; select *&lt;br /&gt;  2    from test_table1&lt;br /&gt;  3  /&lt;br /&gt;&lt;br /&gt;STATUS             MANAGER      SALES&lt;br /&gt;--------------- ---------- ----------&lt;br /&gt;in process              14        100&lt;br /&gt;in process              15         10&lt;br /&gt;in process              15         40&lt;br /&gt;done                    14        200&lt;br /&gt;done                    16         50&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see in the sample data, Manager 14 has entries for the status "in process" and "done". Manager 15 only has entries for "in process". Manager 16 only has a single entry for "done".&lt;br /&gt;The result that we are after is to show for each Manager a total sales value for both statuses "in process" and "done".&lt;br /&gt;When we use a regular SUM and GROUP BY:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select manager&lt;br /&gt;  2       , status&lt;br /&gt;  3       , sum(sales)&lt;br /&gt;  4    from test_table1&lt;br /&gt;  5   group by manager&lt;br /&gt;  6          , status&lt;br /&gt;  7   order by manager&lt;br /&gt;  8          , status&lt;br /&gt;  9  /&lt;br /&gt;&lt;br /&gt;   MANAGER STATUS          SUM(SALES)&lt;br /&gt;---------- --------------- ----------&lt;br /&gt;        14 done                   200&lt;br /&gt;        14 in process             100&lt;br /&gt;        15 in process              50&lt;br /&gt;        16 done                    50&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;we only see values for records that are actually in the table... Go Figure!&lt;br /&gt;&lt;br /&gt;Nice results, but not exactly what we are after. We want an extra record for Manager 15 (with status "done" and sales value of 0) and an extra record for Manager 16 (with status "in process" and also a value of 0).&lt;br /&gt;&lt;br /&gt;One way to tackle this problem (or challenge if you prefer) is to use a Partition Outer Join. As far as i know this is not ANSI-SQL, but Oracle specific syntax. Tahiti.Oracle.com calls it an "extension to the ANSI syntax".&lt;br /&gt;To make this query work, we need a "table" (or inline view) which has all possible statuses. Something like&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select 'done' st from dual&lt;br /&gt;  2  union all&lt;br /&gt;  3  select 'in process' from dual&lt;br /&gt;  4  /&lt;br /&gt;&lt;br /&gt;ST&lt;br /&gt;----------&lt;br /&gt;done&lt;br /&gt;in process&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This inline view will be outer joined to our table.&lt;br /&gt;What makes a Partition Outer Join work differently from a regular Outer Join?&lt;br /&gt;A regular Outer Join will show an extra single record even when a matching value is not present. In our case, this will not make a difference as the values "done" and "in process" are present in our base table.&lt;br /&gt;What we want is to outer join all statuses from the inline view to our base table &lt;i&gt;for each manager&lt;/i&gt;.&lt;br /&gt;And this is exactly what the Partition Clause does. It breaks up the result set per manager. Per partition (one for Manager 14, one for Manager 15 and one for Manager 16) we want to outer join to the inline view.&lt;br /&gt;&lt;br /&gt;Putting it all together, and here is the final result:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select manager&lt;br /&gt;  2       , st&lt;br /&gt;  3       , nvl (sum (sales) , 0)&lt;br /&gt;  4    from test_table1 t partition by (manager)&lt;br /&gt;  5   right outer&lt;br /&gt;  6    join (select 'done' st from dual&lt;br /&gt;  7          union all&lt;br /&gt;  8          select 'in process' from dual&lt;br /&gt;  9         ) sts&lt;br /&gt; 10      on (t.status = sts.st)&lt;br /&gt; 11   group by manager&lt;br /&gt; 12          , st&lt;br /&gt; 13   order by manager&lt;br /&gt; 14          , st&lt;br /&gt; 15  /&lt;br /&gt;&lt;br /&gt;   MANAGER ST         NVL(SUM(SALES),0)&lt;br /&gt;---------- ---------- -----------------&lt;br /&gt;        14 done                     600&lt;br /&gt;        14 in process               300&lt;br /&gt;        15 done                       0&lt;br /&gt;        15 in process               150&lt;br /&gt;        16 done                     150&lt;br /&gt;        16 in process                 0&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each Manager shows an entry for both statuses "done" and "in process", even when this value is not in the base table.&lt;br /&gt;&lt;br /&gt;If -for whatever reason- you don't like RIGHT OUTER, just flip the tables around and call it a LEFT OUTER:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select manager&lt;br /&gt;  2       , st&lt;br /&gt;  3       , nvl (sum (sales) , 0)&lt;br /&gt;  4    from (select 'done' st from dual&lt;br /&gt;  5          union all&lt;br /&gt;  6          select 'in process' from dual&lt;br /&gt;  7         ) sts&lt;br /&gt;  8    left outer&lt;br /&gt;  9    join test_table1 t partition by (manager)&lt;br /&gt; 10      on (t.status = sts.st)&lt;br /&gt; 11   group by manager&lt;br /&gt; 12          , st&lt;br /&gt; 13   order by manager&lt;br /&gt; 14          , st&lt;br /&gt; 15  /&lt;br /&gt;&lt;br /&gt;   MANAGER ST         NVL(SUM(SALES),0)&lt;br /&gt;---------- ---------- -----------------&lt;br /&gt;        14 done                     800&lt;br /&gt;        14 in process               400&lt;br /&gt;        15 done                       0&lt;br /&gt;        15 in process               200&lt;br /&gt;        16 done                     200&lt;br /&gt;        16 in process                 0&lt;br /&gt;&lt;br /&gt;6 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=924108&amp;tstart=0" target="_blank"&gt;Original question&lt;/a&gt;&lt;br /&gt;&lt;a href="http://technology.amis.nl/blog/819/summary-results-for-all-dates-including-the-ones-that-i-do-not-have-data-for-example-of-using-partition-outer-join-oracle10g-sql-feature" target="_blank"&gt;Lucas Jellema on Partition Outer Join&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14223/analysis.htm#sthref1836" target="_blank"&gt;Oracle 10g Documentation&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B28359_01/server.111/b28314/tdpdw_sql.htm#TDPDW0072" target="_blank"&gt;Oracle 11g Documentation&lt;/a&gt;&lt;br /&gt;&lt;a href="http://rwijk.blogspot.com/2007/11/interval-based-row-generation.html" target="_blank"&gt;Rob van Wijk on Interval Based Row Generation&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-1104860229495832213?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/1104860229495832213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/07/making-up-data-with-partition-outer.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1104860229495832213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1104860229495832213'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/07/making-up-data-with-partition-outer.html' title='Making up Data with Partition Outer Join'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3093702992573863576</id><published>2009-07-03T13:55:00.008+02:00</published><updated>2009-07-21T11:55:13.745+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 10g'/><category scheme='http://www.blogger.com/atom/ns#' term='regexp'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Splitting a comma delimited string the RegExp way</title><content type='html'>This is one of those recurring questions on the &lt;a href="http://forums.oracle.com/forums/forum.jspa?forumID=75&amp;start=0"&gt;Oracle Forums of OTN&lt;/a&gt;.&lt;br /&gt;How to split a comma delimited string? Of course there are several options how to tackle this problem. One of the most elegant ones, at least I think so, uses a regular expression.&lt;br /&gt;&lt;br /&gt;Let's just look at an example&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; with test as  &lt;br /&gt;  2  (select 'ABC,DEF,GHI,JKL,MNO' str from dual  &lt;br /&gt;  3  )  &lt;br /&gt;  4  select regexp_substr (str, '[^,]+', 1, rownum) split  &lt;br /&gt;  5    from test  &lt;br /&gt;  6  connect by level &lt;= length (regexp_replace (str, '[^,]+'))  + 1&lt;br /&gt;  7  /  &lt;br /&gt;&lt;br /&gt;SPLIT&lt;br /&gt;---------------------------------------------------------------------&lt;br /&gt;ABC&lt;br /&gt;DEF&lt;br /&gt;GHI&lt;br /&gt;JKL&lt;br /&gt;MNO&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first part creates some test data using the WITH clause (aka Subquery Factoring). The actual query with the regular expression starts on line 4.&lt;br /&gt;The expression is&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;regexp_substr (str, '[^,]+', 1, rownum)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The meaning of "[^,]+" in normal English:&lt;br /&gt;&lt;blockquote&gt;Give me one or more characters which are not in the list&lt;/blockquote&gt;&lt;br /&gt;The list consists of all characters between the square brackets. Here the "^" (circumflex) indicates "except" or "not in". The "+" means: one or more times.&lt;br /&gt;&lt;br /&gt;The arguments of the REGEXP_SUBSTR determine which part of the string to subtract.&lt;br /&gt;The third argument to the REGEXP_SUBSTR functions tells it where to start with the regular expression. The last argument means which occurence to match.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions131.htm#sqlrf06303" target="_blank"&gt;REGEXP_SUBSTR&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/ap_posix001.htm#i690819" target="_blank"&gt;Multilingual Regular Expression Syntax&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3093702992573863576?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3093702992573863576/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/07/splitting-comma-delimited-string-regexp.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3093702992573863576'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3093702992573863576'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/07/splitting-comma-delimited-string-regexp.html' title='Splitting a comma delimited string the RegExp way'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5433131121437788126</id><published>2009-06-12T12:50:00.012+02:00</published><updated>2009-07-21T11:55:50.933+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 9i'/><category scheme='http://www.blogger.com/atom/ns#' term='Analytic Functions'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Analytic Function: Finding Gaps</title><content type='html'>A little while ago Anton Nielsen &lt;a href="http://c2anton.blogspot.com/2009/06/sql-for-date-spanned-data.html" target="_blank"&gt;posted a blog&lt;/a&gt; named "SQL for Spanned Data".&lt;br /&gt;&lt;br /&gt;In this blog he describes a challenging query involving a table which stores start and end dates. The challenge as stated by Anton:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;The challenge was to create a sql statement to only return contiguous spans by division.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;To illustrate what is required:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;Id, Division, Start_date, End_date&lt;br /&gt;10 1 09-JUN-2009 10-JUN-2009&lt;br /&gt;11 1 11-JUN-2009 12-JUN-2009&lt;br /&gt;12 1 13-JUN-2009 14-JUN-2009 -- Note the following row is not contiguous&lt;br /&gt;14 1 17-JUN-2009 18-JUN-2009&lt;br /&gt;15 1 19-JUN-2009 20-JUN-2009&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The final result will be&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;Division, Start_date, End_date&lt;br /&gt;1 09-JUN-2009 14-JUN-2009&lt;br /&gt;1 17-JUN-2009 20-JUN-2009&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this post, I will use Analytic Functions to find contiguous spans. As Anton already provided the DDL for this blog, I will not be repeating that here. So, if you want to follow along grab the DDL from his blog and join the fun.&lt;br /&gt;&lt;br /&gt;The logic that we are using, is like this&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;compare the current start date with the previous end date&lt;/li&gt;&lt;br /&gt;&lt;li&gt;if the difference between these dates is one day, we have a contiguous span&lt;/li&gt;&lt;br /&gt;&lt;li&gt;if the difference is anything else (greater than one day or NULL) then we start a new group of spans&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Now that we have a list of numbers (1 and 0) we do a running total, each span group will have it's own number&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Get the first and last day of each span group&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;To compare the current start date with the previous end date, we will use the LAG function. With the LAG function you can "look back" in your result set.&lt;br /&gt;Let's have a look at the effect of this function&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , start_date&lt;br /&gt;  3       , end_date&lt;br /&gt;  4       , lag (end_date) over (partition by division&lt;br /&gt;  5                                  order by start_date&lt;br /&gt;  6                             )&lt;br /&gt;  7    from spantest&lt;br /&gt;  8   where start_date between to_date ('01-06-2009', 'dd-mm-yyyy')&lt;br /&gt;  9                        and to_date ('20-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 10   and division = 1&lt;br /&gt; 11   order by start_date&lt;br /&gt; 12  /&lt;br /&gt;&lt;br /&gt;        ID START_DAT END_DATE  LAG(END_D&lt;br /&gt;---------- --------- --------- ---------&lt;br /&gt;     73383 02-JUN-09 03-JUN-09&lt;br /&gt;     73384 04-JUN-09 05-JUN-09 03-JUN-09&lt;br /&gt;     73385 06-JUN-09 07-JUN-09 05-JUN-09&lt;br /&gt;     73386 08-JUN-09 09-JUN-09 07-JUN-09&lt;br /&gt;     73387 10-JUN-09 11-JUN-09 09-JUN-09&lt;br /&gt;     73388 12-JUN-09 13-JUN-09 11-JUN-09&lt;br /&gt;     73389 14-JUN-09 15-JUN-09 13-JUN-09&lt;br /&gt;     73391 18-JUN-09 19-JUN-09 15-JUN-09&lt;br /&gt;     73392 20-JUN-09 21-JUN-09 19-JUN-09&lt;br /&gt;&lt;br /&gt;9 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last column is the end date of the previous record. This makes it easy to implement steps 2 and 3. For this we will use a CASE statement&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , start_date&lt;br /&gt;  3       , end_date&lt;br /&gt;  4       , case&lt;br /&gt;  5          when start_date - &lt;br /&gt;  6             lag (end_date) over (partition by division&lt;br /&gt;  7                                      order by start_date&lt;br /&gt;  8                                 ) = 1&lt;br /&gt;  9          then 0&lt;br /&gt; 10          else 1&lt;br /&gt; 11         end span_group&lt;br /&gt; 12    from spantest&lt;br /&gt; 13   where start_date between to_date ('01-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 14                        and to_date ('20-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 15   and division = 1&lt;br /&gt; 16   order by start_date&lt;br /&gt; 17  /&lt;br /&gt;&lt;br /&gt;        ID START_DAT END_DATE  SPAN_GROUP&lt;br /&gt;---------- --------- --------- ----------&lt;br /&gt;     73383 02-JUN-09 03-JUN-09          1&lt;br /&gt;     73384 04-JUN-09 05-JUN-09          0&lt;br /&gt;     73385 06-JUN-09 07-JUN-09          0&lt;br /&gt;     73386 08-JUN-09 09-JUN-09          0&lt;br /&gt;     73387 10-JUN-09 11-JUN-09          0&lt;br /&gt;     73388 12-JUN-09 13-JUN-09          0&lt;br /&gt;     73389 14-JUN-09 15-JUN-09          0&lt;br /&gt;     73391 18-JUN-09 19-JUN-09          1&lt;br /&gt;     73392 20-JUN-09 21-JUN-09          0&lt;br /&gt;&lt;br /&gt;9 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last column (named Span_Group) now contains a list of 1 and 0. The "1" indicate the start of a new Span Group. This sample set therefore contains two Span Groups.&lt;br /&gt;Using the Running Total technique, we can more clearly identify the Span Groups. Because it is not possible to nest analytic functions, we push the query we build so far into an inline view. Then we can use the SUM () OVER () on the Span Groups we created earlier.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select id&lt;br /&gt;  2       , start_date&lt;br /&gt;  3       , end_date&lt;br /&gt;  4       , sum (span_group) over (partition by division&lt;br /&gt;  5                                    order by start_date&lt;br /&gt;  6                               ) span_grps&lt;br /&gt;  7    from (&lt;br /&gt;  8        select id&lt;br /&gt;  9           , division&lt;br /&gt; 10           , start_date&lt;br /&gt; 11           , end_date&lt;br /&gt; 12           , case&lt;br /&gt; 13              when start_date - &lt;br /&gt; 14                 lag (end_date) over (partition by division&lt;br /&gt; 15                                            order by start_date&lt;br /&gt; 16                                      ) = 1&lt;br /&gt; 17              then 0&lt;br /&gt; 18              else 1&lt;br /&gt; 19              end span_group&lt;br /&gt; 20        from spantest&lt;br /&gt; 21        where start_date between to_date ('01-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 22                             and to_date ('20-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 23        and division = 1&lt;br /&gt; 24        )&lt;br /&gt; 25   order by start_date&lt;br /&gt; 26  /&lt;br /&gt;&lt;br /&gt;        ID START_DAT END_DATE   SPAN_GRPS&lt;br /&gt;---------- --------- --------- ----------&lt;br /&gt;     73383 02-JUN-09 03-JUN-09          1&lt;br /&gt;     73384 04-JUN-09 05-JUN-09          1&lt;br /&gt;     73385 06-JUN-09 07-JUN-09          1&lt;br /&gt;     73386 08-JUN-09 09-JUN-09          1&lt;br /&gt;     73387 10-JUN-09 11-JUN-09          1&lt;br /&gt;     73388 12-JUN-09 13-JUN-09          1&lt;br /&gt;     73389 14-JUN-09 15-JUN-09          1&lt;br /&gt;     73391 18-JUN-09 19-JUN-09          2&lt;br /&gt;     73392 20-JUN-09 21-JUN-09          2&lt;br /&gt;&lt;br /&gt;9 rows selected.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last column in the result set (named SPAN_GRPS) now clearly identifies the two groups.&lt;br /&gt;&lt;br /&gt;The final thing we need to do is retrieve the earliest start date and the latest end date, a simple aggregate will suffice&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select division&lt;br /&gt;  2       , min (start_date) start_date&lt;br /&gt;  3       , max (end_date)   end_date&lt;br /&gt;  4    from (&lt;br /&gt;  5     select id&lt;br /&gt;  6        , division&lt;br /&gt;  7        , start_date&lt;br /&gt;  8        , end_date&lt;br /&gt;  9        , sum (span_group) over (partition by division&lt;br /&gt; 10                                      order by start_date&lt;br /&gt; 11                                ) span_grps&lt;br /&gt; 12     from (&lt;br /&gt; 13           select id&lt;br /&gt; 14              , division&lt;br /&gt; 15              , start_date&lt;br /&gt; 16              , end_date&lt;br /&gt; 17              , case&lt;br /&gt; 18                 when start_date - &lt;br /&gt; 19                    lag (end_date) over (partition by division&lt;br /&gt; 20                                               order by start_date&lt;br /&gt; 21                                         ) = 1&lt;br /&gt; 22                 then 0&lt;br /&gt; 23                 else 1&lt;br /&gt; 24                 end span_group&lt;br /&gt; 25           from spantest&lt;br /&gt; 26           where start_date between to_date ('01-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 27                                and to_date ('20-06-2009', 'dd-mm-yyyy')&lt;br /&gt; 28           and division = 1&lt;br /&gt; 29           )&lt;br /&gt; 30     )&lt;br /&gt; 31  group by division, span_grps&lt;br /&gt; 32  /&lt;br /&gt;&lt;br /&gt;  DIVISION START_DAT END_DATE&lt;br /&gt;---------- --------- ---------&lt;br /&gt;         1 02-JUN-09 15-JUN-09&lt;br /&gt;         1 18-JUN-09 21-JUN-09&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Personally I think that Analytic Functions are a lot easier to understand than the CONNECT BY query. Just out of curiosity I ran both Queries with Autotrace on, and here are the Statistics on the queries:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;Statistics&lt;br /&gt;---------------------------------------------------&lt;br /&gt;          0  recursive calls&lt;br /&gt;          0  db block gets&lt;br /&gt;        248  consistent gets&lt;br /&gt;          0  physical reads&lt;br /&gt;          0  redo size&lt;br /&gt;     112543  bytes sent via SQL*Net to client&lt;br /&gt;       3164  bytes received via SQL*Net from client&lt;br /&gt;        255  SQL*Net roundtrips to/from client&lt;br /&gt;          3  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;       3800  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The CONNECT BY Query showed these statistics:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;Statistics&lt;br /&gt;---------------------------------------------------&lt;br /&gt;          3  recursive calls&lt;br /&gt;        233  db block gets&lt;br /&gt;       3882  consistent gets&lt;br /&gt;        224  physical reads&lt;br /&gt;        692  redo size&lt;br /&gt;     112543  bytes sent via SQL*Net to client&lt;br /&gt;       3164  bytes received via SQL*Net from client&lt;br /&gt;        255  SQL*Net roundtrips to/from client&lt;br /&gt;         18  sorts (memory)&lt;br /&gt;          0  sorts (disk)&lt;br /&gt;       3800  rows processed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And yes, I did run the latter query a few times to reduce the recursive calls (the first time around 161 recursive calls)&lt;br /&gt;&lt;br /&gt;Enough said,.. Another case to show &lt;a href="http://technology.amis.nl/blog/506/analytical-power"&gt;the power of Analytic Functions&lt;/a&gt;.&lt;br /&gt;Final Note:&lt;br /&gt;The results which I got from my query are the same as the results from the first query in the original post.&lt;br /&gt;The query, which Anton labeled "Don't run this"... well, I did run it and got these results for the Division 1 in June:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;1 02-JUN-09 15-JUN-09&lt;br /&gt;1 04-JUN-09 15-JUN-09&lt;br /&gt;1 06-JUN-09 15-JUN-09&lt;br /&gt;1 08-JUN-09 15-JUN-09&lt;br /&gt;1 10-JUN-09 15-JUN-09&lt;br /&gt;1 12-JUN-09 15-JUN-09&lt;br /&gt;1 14-JUN-09 15-JUN-09&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And I must admit, I don't understand these results... They are completely different than my query and Anton's first query. Maybe I don't understand the original requirements,...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5433131121437788126?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5433131121437788126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/06/analytic-function-finding-gaps.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5433131121437788126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5433131121437788126'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/06/analytic-function-finding-gaps.html' title='Analytic Function: Finding Gaps'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-5062231465413380869</id><published>2009-06-11T12:46:00.007+02:00</published><updated>2009-06-11T13:09:07.362+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Developer'/><title type='text'>SQL Developer: Backspace not working properly</title><content type='html'>Nowadays I use SQL Developer on a daily basis, and I like it more and more. But every now and then something rears its ugly head, and that can be annoying.&lt;br /&gt;&lt;br /&gt;Just this morning I had one of those encounters. The "Backspace"-key stopped working. Not only the backspace, but DELETE, ENTER, navigation with the arrow keys... &lt;br /&gt;&lt;br /&gt;I tried re-installing SQL Developer, reboot the computer... no success.&lt;br /&gt;&lt;br /&gt;"Google is your friend" at times like these. One of the first hits I came across was &lt;a href="http://decoding.wordpress.com/2008/01/09/oracle-sql-developer-problem-backspace-delete-button-not-working-quick-fix/" target="_blank"&gt;this blog post&lt;/a&gt; and that solved my problem.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/SjDlVm6A_ZI/AAAAAAAAACE/cKylBCPduG8/s1600-h/sd1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 244px; height: 215px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/SjDlVm6A_ZI/AAAAAAAAACE/cKylBCPduG8/s320/sd1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346024917072674194" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The only thing you need to do is go from the menu "Tools -&gt; Preferences" and in the Preferences window to "Accelerators", push the button "Load Preset" and pick the Default. And that's it.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/SjDlct0XtEI/AAAAAAAAACM/OXIZf2hm2_A/s1600-h/sd2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 216px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/SjDlct0XtEI/AAAAAAAAACM/OXIZf2hm2_A/s320/sd2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346025039187129410" /&gt;&lt;/a&gt;&lt;br /&gt;Now it works like a charm again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-5062231465413380869?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/5062231465413380869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/06/sql-developer-backspace-not-working.html#comment-form' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5062231465413380869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/5062231465413380869'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/06/sql-developer-backspace-not-working.html' title='SQL Developer: Backspace not working properly'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_tdqazgf19_s/SjDlVm6A_ZI/AAAAAAAAACE/cKylBCPduG8/s72-c/sd1.PNG' height='72' width='72'/><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-4927449665874522391</id><published>2009-05-26T13:17:00.005+02:00</published><updated>2009-10-04T14:20:22.109+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='odtug kaleidoscope 2009'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>ODTUG Sneak Preview, the dressed rehearsal</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/ShvTQX6WPGI/AAAAAAAAAB0/U6a-8EUQ4A4/s1600-h/Picture+1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 96px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/ShvTQX6WPGI/AAAAAAAAAB0/U6a-8EUQ4A4/s320/Picture+1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5340094061427965026" /&gt;&lt;/a&gt;&lt;br /&gt;On Monday, June 15 there will be a sneak preview of the upcoming &lt;a href="http://www.odtugkaleidoscope.com/" target="_blank"&gt;ODTUG Kaleidoscope Conference&lt;/a&gt;. Some of the European speakers (that is Belgian and Dutch) will be doing their presentation. I'm just guessing here, but I can imagine that the presentation will be in Dutch.&lt;br /&gt;&lt;/br&gt;&lt;br /&gt;&lt;br /&gt;The presentation that will take place are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Aino Andriessen (AMIS Services) - ADF Development: More tales from the Trenches&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Lonneke Dikmans (Approach Alliance) - Top Ten Tips: Best Practices for Designing Services, Events, and Business Processes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Olivier Dupont (iAdvise) – APEX at the Belgium airport &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Dimitri Gielis (APEX Evangelists) – Mastering an APEX page&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Roel Hartman (Logica) – How to integrate APEX and Oracle Forms?&lt;/li&gt; &lt;br /&gt;&lt;li&gt;Lucas Jellema (AMIS Services) - Truth and Dare—The Story of How an Oracle Classic Stronghold Successfully Embraced SOA&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Toon Koppelaars – Fat databases: A layered approach&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ronald van Luttikhuizen (Approach Alliance) - Customer case: Implementing SOA in a database-centric environment &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Alex Nuijten (AMIS Services) – SQL Holmes – The case of the missing performance &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Last year we did a similar Sneak Preview which was a great success. If you want to attend then register early, seating is limited. Oh, and did I already mention that this session is free?&lt;br /&gt;More &lt;a href="http://www.amis.nl/activiteiten.php?id=744" target="_blank"&gt;information and registration&lt;/a&gt; can be found in the Agenda via the AMIS homepage.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-4927449665874522391?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/4927449665874522391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/05/odtug-sneak-preview-dressed-rehearsal.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4927449665874522391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/4927449665874522391'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/05/odtug-sneak-preview-dressed-rehearsal.html' title='ODTUG Sneak Preview, the dressed rehearsal'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_tdqazgf19_s/ShvTQX6WPGI/AAAAAAAAAB0/U6a-8EUQ4A4/s72-c/Picture+1.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-3346679801859027816</id><published>2009-04-21T11:32:00.001+02:00</published><updated>2009-04-21T11:33:52.914+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Developer'/><title type='text'>Copy and Paste: Clipboard Items</title><content type='html'>By accident I discovered something in &lt;a href="http://www.oracle.com/technology/software/products/sql/index.html"&gt;SQL Developer&lt;/a&gt;. Another one of those things I need to remember for future reference. Hope you can use this feature too.&lt;br /&gt;&lt;br /&gt;You know you can use CTRL + C for copying and CTRL + V for pasting. Nothing new there, but what is really neat is that you can use SHIFT + CTRL + V to see the contents of all your clipboard items.&lt;br /&gt;&lt;br /&gt;Because I needed to do a lot of copying today, this came in really handy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-3346679801859027816?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/3346679801859027816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/04/copy-and-paste-clipboard-items.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3346679801859027816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/3346679801859027816'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/04/copy-and-paste-clipboard-items.html' title='Copy and Paste: Clipboard Items'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-8139697449507277441</id><published>2009-04-06T11:45:00.001+02:00</published><updated>2009-07-06T09:01:18.040+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle Text'/><category scheme='http://www.blogger.com/atom/ns#' term='Diacritic'/><title type='text'>Oracle Text: diacritic search</title><content type='html'>For the client where I'm currently working, the need arose that we needed to find lastnames regardless of diacritical characters. In Dutch, we have lastnames (and firstname too) where characters like ü, ä, ö, é occur.&lt;br /&gt;&lt;br /&gt;The "usual" way to handle this was to add a column to the table, and store the name there without the diacritical characters. Meaning the name "müller" would be in one column, while another column would contain the name "muller", the same letters but without the double dots (called a trema in Dutch) over the "u". A common complaint with this approach, is that when you query with the double dots, you won't be able to find what you are looking for.&lt;br /&gt;There is also the possibility of using Oracle Text to handle this. In the past I have been to &lt;a target="_blank"href="http://technology.amis.nl/blog/671/oracle-text-and-otn"&gt;a presentation on Oracle Text&lt;/a&gt;, but never used it before. It scared me in a certain way. I thought it would be quite complicated to use it, but it turned out to be real easy.&lt;br /&gt;&lt;br /&gt;Let's start with the table&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;create table text_test&lt;br /&gt;(name varchar2(255)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;insert into text_test values ('muller');&lt;br /&gt;insert into text_test values ('müller');&lt;br /&gt;insert into text_test values ('MULLER');&lt;br /&gt;insert into text_test values ('MÜLLER');&lt;br /&gt;insert into text_test values ('mueller');&lt;br /&gt;insert into text_test values ('MUELLER');&lt;br /&gt;--&lt;br /&gt;insert into text_test values ('möller');&lt;br /&gt;insert into text_test values ('moller');&lt;br /&gt;--&lt;br /&gt;insert into text_test values ('mäller');&lt;br /&gt;insert into text_test values ('maller');&lt;br /&gt;--&lt;br /&gt;insert into text_test values ('Médar');&lt;br /&gt;&lt;br /&gt;commit;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In this table are some variations of "Muller", upper- and lowercase and with and without the diacritical character.&lt;br /&gt;In order to use Oracle Text to search for the name regardless of diacritic, we need to create a Custom Lexer. This custom lexer is needed, because we need to change the Base Letter setting. From the documentation:&lt;br /&gt;&lt;blockquote&gt;With base-letter conversions enabled, letters with umlauts, acute accents, cedillas, and the like are converted to their basic forms for indexing, so fiancé is indexed both as fiancé and as fiance, and a query of fiancé returns documents containing either form.&lt;/blockquote&gt;&lt;br /&gt;And this is exactly what we're after.&lt;br /&gt;&lt;br /&gt;To be able to search using the Base Letter conversion, as described in the quote from the ducmentation, we need to create a Preference.&lt;br /&gt;To change the setting of the Base Letter, the default is NO, we to set this attribute to YES.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;begin&lt;br /&gt;   ctxsys.ctx_ddl.create_preference ('cust_lexer','BASIC_LEXER');&lt;br /&gt;   ctxsys.ctx_ddl.set_attribute ('cust_lexer','base_letter','YES'); -- removes diacritics&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the above code, we create a preference, called CUST_LEXER. Because the main language will be Dutch, I use the BASIC_LEXER. More information regarding this in the Oracle documentation.&lt;br /&gt;The attribute that I want to override is the BASE_LETTER, so set that attribute to YES. And that should take care of it.&lt;br /&gt;Now the only thing to do is create the Oracle Text index and specify that we want to use our preference set.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;CREATE INDEX text_test_idx ON text_test(name) INDEXTYPE IS CTXSYS.CONTEXT&lt;br /&gt;parameters ('LEXER cust_lexer');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now that we are all set, let's run a query and verify the results.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select name&lt;br /&gt;  2    from text_test&lt;br /&gt;  3   where contains (name, 'muller') &gt; 0&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;NAME&lt;br /&gt;-----------------------------------------&lt;br /&gt;müller&lt;br /&gt;MULLER&lt;br /&gt;muller&lt;br /&gt;MÜLLER&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you have it, it works as advertised.&lt;br /&gt;&lt;br /&gt;While writing this blog, a colleague of mine pointed out that in Germany it is accepted to spell the name "müller" as "mueller". Oracle Text can even handle that.&lt;br /&gt;Add this to your preference set, and you're good to go.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;ctxsys.ctx_ddl.set_attribute ('cust_lexer','alternate_spelling','GERMAN');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With a little demo:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select name&lt;br /&gt;  2    from text_test&lt;br /&gt;  3   where contains (name, 'muller') &gt; 0&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;NAME&lt;br /&gt;-----------------------------------------&lt;br /&gt;muller&lt;br /&gt;müller&lt;br /&gt;MULLER&lt;br /&gt;MÜLLER&lt;br /&gt;mueller&lt;br /&gt;MUELLER&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This also works when searching for "Mueller"&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select name&lt;br /&gt;  2    from text_test&lt;br /&gt;  3   where contains (name, 'mueller') &gt; 0&lt;br /&gt;  4  ;&lt;br /&gt;&lt;br /&gt;NAME&lt;br /&gt;-----------------------------------------&lt;br /&gt;muller&lt;br /&gt;müller&lt;br /&gt;MULLER&lt;br /&gt;MÜLLER&lt;br /&gt;mueller&lt;br /&gt;MUELLER&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The problem with Oracle Text used to be the synchronization of the Text indexes with DML actions. In the old days, you needed to take care of this yourself. Schedule a "make sure the Text index is updated to be in sync with the table". This could mean that after you added a name like "Désiré" to your table, you wouldn't be able to find because it wasn't in the text index.&lt;br /&gt;Starting with Oracle 10g (release 2) you can indicate that the index needs to be synchronized during a COMMIT. Just specify it with the creation of the index, and that's it.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;CREATE INDEX text_test_idx ON text_test(name) INDEXTYPE IS CTXSYS.CONTEXT&lt;br /&gt;parameters ('LEXER cust_lexer SYNC (ON COMMIT)');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This Oracle Text is really cool stuff, and this is just the beginning. The Oracle Documentation is a great source of information. A link is included at the bottom.&lt;br /&gt;&lt;br /&gt;Finally the cleanup:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;drop table text_test cascade constraints&lt;br /&gt;/&lt;br /&gt;begin&lt;br /&gt;   ctxsys.ctx_ddl.drop_preference ('cust_lexer');&lt;br /&gt;end;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://stanford.edu/dept/itss/docs/oracle/10g/text.101/b10729/toc.htm"&gt;Oracle Text Documentation&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-8139697449507277441?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/8139697449507277441/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/03/oracle-text-diacritic-search.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8139697449507277441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/8139697449507277441'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/03/oracle-text-diacritic-search.html' title='Oracle Text: diacritic search'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6042564944890408112</id><published>2009-03-18T08:20:00.010+01:00</published><updated>2009-03-18T16:44:58.037+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Developer'/><title type='text'>Display Images with SQL Developer</title><content type='html'>Images can be stored as a BLOB in the database. SQL Developer can show them as well.&lt;br /&gt;&lt;br /&gt;Step 1) Create a Table to store the image in&lt;br /&gt;&lt;pre name="code" class="sql"&gt;create table t&lt;br /&gt;(img blob);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Step 2) Upload an Image&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/ScCiBg9Bq9I/AAAAAAAAABU/NG9_0aHQYxE/s1600-h/add_img.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 245px; height: 108px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/ScCiBg9Bq9I/AAAAAAAAABU/NG9_0aHQYxE/s320/add_img.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5314425707206650834"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/ScCiSv2VltI/AAAAAAAAABc/STedVRo-GPM/s1600-h/add_img2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 311px; height: 320px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/ScCiSv2VltI/AAAAAAAAABc/STedVRo-GPM/s320/add_img2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5314426003262904018"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Choose an image from your file system, and commit your changes.&lt;br /&gt;&lt;br /&gt;Step 3) On the "Data" tab, click the "..." button with the BLOB column. &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/ScCioabg2YI/AAAAAAAAABk/rUDfXCDqWj8/s1600-h/add_img3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 257px; height: 127px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/ScCioabg2YI/AAAAAAAAABk/rUDfXCDqWj8/s320/add_img3.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5314426375470373250"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Step 4) In the dialog box, check the "View as Image" box&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_tdqazgf19_s/ScCjFKhXvcI/AAAAAAAAABs/sCQYlIFhMJg/s1600-h/add_img4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 286px;" src="http://2.bp.blogspot.com/_tdqazgf19_s/ScCjFKhXvcI/AAAAAAAAABs/sCQYlIFhMJg/s320/add_img4.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5314426869416181186"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And that's it... Looks a lot better than SQL*Plus would display images :)&lt;br /&gt;&lt;pre name="code" class="sql"&gt;SQL&gt; select img&lt;br /&gt;  2    from t&lt;br /&gt;  3  /&lt;br /&gt;SP2-0678: Column or attribute type can not be displayed by SQL*Plus&lt;br /&gt;SQL&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-a36468b0f5d32415" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v15.nonxt7.googlevideo.com/videoplayback?id%3Da36468b0f5d32415%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330243051%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D5A60AB513C08C87AA4A5D1FB12D317E04CBEEEA4.56884F7E4A7BC13F77EF2036FC5AFA19DB80A60C%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Da36468b0f5d32415%26offsetms%3D5000%26itag%3Dw160%26sigh%3DReI5DbXKf_aoYm-5C2g4gAd6caI&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v15.nonxt7.googlevideo.com/videoplayback?id%3Da36468b0f5d32415%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1330243051%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D5A60AB513C08C87AA4A5D1FB12D317E04CBEEEA4.56884F7E4A7BC13F77EF2036FC5AFA19DB80A60C%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Da36468b0f5d32415%26offsetms%3D5000%26itag%3Dw160%26sigh%3DReI5DbXKf_aoYm-5C2g4gAd6caI&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6042564944890408112?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='video/mp4' href='http://www.blogger.com/video-play.mp4?contentId=a36468b0f5d32415&amp;type=video%2Fmp4' length='0'/><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6042564944890408112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/03/display-images-with-sql-developer.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6042564944890408112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6042564944890408112'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/03/display-images-with-sql-developer.html' title='Display Images with SQL Developer'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_tdqazgf19_s/ScCiBg9Bq9I/AAAAAAAAABU/NG9_0aHQYxE/s72-c/add_img.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-351666417108044935</id><published>2009-03-13T16:23:00.014+01:00</published><updated>2009-07-06T09:02:31.595+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='associative arrays'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Developer'/><category scheme='http://www.blogger.com/atom/ns#' term='debug'/><title type='text'>Debugging Associative Arrays</title><content type='html'>My favorite development tool is &lt;a href="http://www.allroundautomations.com/"&gt;PL/SQL Developer by Allround Automations&lt;/a&gt;. The Debugger included with this tool is great, I really like it… but… not for Associative Arrays.&lt;br /&gt;Let’s take a look at some code we want to debug&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;type emp_rt is record&lt;br /&gt;(ename    emp.ename%type&lt;br /&gt;,hiredate emp.hiredate%type&lt;br /&gt;,sal      emp.sal%type&lt;br /&gt;);&lt;br /&gt;type emp_tt is table of emp_rt&lt;br /&gt;index by pls_integer;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;First we declare a Record, based on some of the columns of the infamous EMP table. Next we declare the Associative Array based on the Record structure.&lt;br /&gt;Then we need a (packaged) procedure, which we are going to debug. The type declaration and the procedure are both in a Package, of course.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;procedure test&lt;br /&gt;is&lt;br /&gt;emps emp_tt;&lt;br /&gt;idx  pls_integer;&lt;br /&gt;begin&lt;br /&gt;select ename&lt;br /&gt;    , hiredate&lt;br /&gt;    , sal&lt;br /&gt; bulk collect&lt;br /&gt; into emps&lt;br /&gt; from emp&lt;br /&gt;;&lt;br /&gt;idx := emps.first;&lt;br /&gt;while idx is not null&lt;br /&gt;loop&lt;br /&gt;  dbms_output.put_line ('Employee: '||emps(idx).ename);&lt;br /&gt;  idx := emps.next(idx);&lt;br /&gt;end loop;&lt;br /&gt;end test;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;PL/SQL Developer has a Test Window, where you can run an anonymous block. It is possible to call this window whenever you feel like, no need to be in the context of a Package like in SQL Developer. Nor does the package need a breakpoint. And this is a good thing, 'cause most of time I forget to set breakpoints.&lt;br /&gt;&lt;p&gt;When you are stepping through the code and you want to see the content of the Associative Array, you will get the message: &lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_tdqazgf19_s/Sbp_mlKlbpI/AAAAAAAAAA8/ZZKkhSrbJY0/s1600-h/plsqldev_debug.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 248px; height: 123px;" src="http://4.bp.blogspot.com/_tdqazgf19_s/Sbp_mlKlbpI/AAAAAAAAAA8/ZZKkhSrbJY0/s320/plsqldev_debug.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5312699011225185938" /&gt;&lt;/a&gt;&lt;p&gt;And that's too bad.&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;SQL Developer to the Rescue &lt;/h3&gt;&lt;br /&gt;&lt;a href="http://www.oracle.com/technology/products/database/sql_developer/index.html"&gt;SQL Developer&lt;/a&gt;, Oracle's free IDE, also has a Debugger. And what is really nice about this debugger, is that you can view the content of the Associative Array.&lt;div&gt;&lt;ol&gt;&lt;li&gt;Set a Breakpoint in the procedure (this can only be done when you are in edit mode)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Compile the Package in Debug (with the black compile button&lt;/li&gt;&lt;li&gt;Choose Run --&gt; Debug from the menu&lt;/li&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/SbqAAgEiVFI/AAAAAAAAABE/Oll7UJtVh7Y/s1600-h/sql_debug1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 254px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/SbqAAgEiVFI/AAAAAAAAABE/Oll7UJtVh7Y/s320/sql_debug1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5312699456534238290" /&gt;&lt;/a&gt;&lt;li&gt;Run the procedure from the Debug window&lt;/li&gt;&lt;li&gt;Execution halts at the Breakpoint&lt;/li&gt;&lt;/ol&gt;You can look on the "Smart Data" tab to see the content of the Associative Array.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_tdqazgf19_s/SbqAGYLvazI/AAAAAAAAABM/3PPSzTTGUD4/s1600-h/sql_debug2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 90px;" src="http://1.bp.blogspot.com/_tdqazgf19_s/SbqAGYLvazI/AAAAAAAAABM/3PPSzTTGUD4/s320/sql_debug2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5312699557496187698" /&gt;&lt;/a&gt;&lt;br /&gt;Simply Brilliant! &lt;a href="http://sueharper.blogspot.com/"&gt;Great Job, Sue!&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-351666417108044935?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/351666417108044935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/03/debugging-associative-arrays.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/351666417108044935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/351666417108044935'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/03/debugging-associative-arrays.html' title='Debugging Associative Arrays'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_tdqazgf19_s/Sbp_mlKlbpI/AAAAAAAAAA8/ZZKkhSrbJY0/s72-c/plsqldev_debug.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-6570429628923373907</id><published>2009-03-04T15:22:00.006+01:00</published><updated>2009-07-21T11:56:23.977+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='overloading boolean'/><title type='text'>Overloading Functions that Return Boolean</title><content type='html'>When you have a function that returns a Boolean, you know that you can't use it in SQL. SQL just doesn't have a Boolean datatype.&lt;br /&gt;&lt;br /&gt;Say you have a function like the following:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;function ftest (p_param1 in varchar2&lt;br /&gt;               ,p_param2 in varchar2&lt;br /&gt;               )&lt;br /&gt;   return boolean;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It is easy to overload the function, provided you placed it in a package, to return a datatype which is compatible with SQL.&lt;br /&gt;All too often an overloading would look like&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;if ftest (param1, param2)&lt;br /&gt;then&lt;br /&gt;   return 1;&lt;br /&gt;else&lt;br /&gt;   return 0;&lt;br /&gt;end if;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Can you spot the flaw in this?&lt;br /&gt;The function will return a zero when the original function returns FALSE or NULL.&lt;br /&gt;&lt;br /&gt;A better way to overload is to use SYS.DIUTIL to do the job&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;   return sys.diutil.bool_to_int (ftest (param1, param2));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This function will return a 1, 0 or NULL.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     dbms_output.put_line ('True : '||sys.diutil.bool_to_int (true));&lt;br /&gt;  3     dbms_output.put_line ('False: '||sys.diutil.bool_to_int (false));&lt;br /&gt;  4     dbms_output.put_line ('NULL : '||sys.diutil.bool_to_int (null));&lt;br /&gt;  5  end;&lt;br /&gt;  6  /&lt;br /&gt;True : 1&lt;br /&gt;False: 0&lt;br /&gt;NULL :&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And there is also a "reverse" function, to turn an integer into a Boolean&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; begin&lt;br /&gt;  2     if sys.diutil.int_to_bool (1)&lt;br /&gt;  3     then&lt;br /&gt;  4        dbms_output.put_line ('True');&lt;br /&gt;  5     end if;&lt;br /&gt;  6     if not sys.diutil.int_to_bool (0)&lt;br /&gt;  7     then&lt;br /&gt;  8        dbms_output.put_line ('False');&lt;br /&gt;  9     end if;&lt;br /&gt; 10     if sys.diutil.int_to_bool (null) is null&lt;br /&gt; 11     then&lt;br /&gt; 12        dbms_output.put_line ('Null');&lt;br /&gt; 13     end if;&lt;br /&gt; 14  end;&lt;br /&gt; 15  /&lt;br /&gt;True&lt;br /&gt;False&lt;br /&gt;Null&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-6570429628923373907?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/6570429628923373907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/03/overloading-functions-that-return.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6570429628923373907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/6570429628923373907'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/03/overloading-functions-that-return.html' title='Overloading Functions that Return Boolean'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1998510888403370482</id><published>2009-02-06T13:32:00.007+01:00</published><updated>2009-07-21T11:56:36.543+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PL/SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='exceptions'/><title type='text'>Value Error and Invalid Number</title><content type='html'>Currently I'm in the process on reviewing some chapters on an upcoming book... more on that in a later blog, 'cause I am not sure if I can talk about it or not.&lt;br /&gt;&lt;br /&gt;One of the chapters is on "Exceptions" and I noticed a line from the Oracle documentation which I didn't notice before. The VALUE_ERROR (ORA-6502) and INVALID_NUMBER (ORA-1722) are predefined exception, which can be handled by name in your exception handler.&lt;br /&gt;Quote from the docs:&lt;br /&gt;"...In procedural statements, VALUE_ERROR is raised if the conversion of a character string into a number fails. (In SQL statements, INVALID_NUMBER is raised.)"&lt;br /&gt;&lt;br /&gt;Let's first check the latter part, about the SQL statements:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select to_number ('a')&lt;br /&gt;  2    from dual&lt;br /&gt;  3  /&lt;br /&gt;select to_number ('a')&lt;br /&gt;                  *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-01722: invalid number&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now let's check the procedural part, in PL/SQL.&lt;br /&gt;Based on the description, this statement would cause a VALUE_ERROR exception&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2     n number;&lt;br /&gt;  3  begin&lt;br /&gt;  4     n := 'a';&lt;br /&gt;  5  exception&lt;br /&gt;  6    when value_error&lt;br /&gt;  7    then&lt;br /&gt;  8      dbms_output.put_line ('Value Error');&lt;br /&gt;  9  end;&lt;br /&gt; 10  /&lt;br /&gt;Value Error&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and it does.&lt;br /&gt;&lt;br /&gt;But what if you would use a SQL statement in PL/SQL?&lt;br /&gt;Would this also raise a VALUE_ERROR?&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2    n number;&lt;br /&gt;  3  begin&lt;br /&gt;  4     select 'a'&lt;br /&gt;  5       into n&lt;br /&gt;  6       from dual&lt;br /&gt;  7     ;&lt;br /&gt;  8  exception&lt;br /&gt;  9    when value_error&lt;br /&gt; 10    then&lt;br /&gt; 11      dbms_output.put_line ('Value Error');&lt;br /&gt; 12  end;&lt;br /&gt; 13  /&lt;br /&gt;Value Error&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Yes, it does. In accordance with the documentation, doing a conversion in procedural statement will raise the VALUE_ERROR exception,... &lt;br /&gt;One last option, let's use the first SQL statement from this blog and use it in PL/SQL:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; declare&lt;br /&gt;  2    n number;&lt;br /&gt;  3  begin&lt;br /&gt;  4    select to_number('a')&lt;br /&gt;  5      into n&lt;br /&gt;  6      from dual&lt;br /&gt;  7    ;&lt;br /&gt;  8  exception&lt;br /&gt;  9    when value_error&lt;br /&gt; 10    then&lt;br /&gt; 11      dbms_output.put_line ('Value Error');&lt;br /&gt; 12    when invalid_number&lt;br /&gt; 13    then&lt;br /&gt; 14      dbms_output.put_line ('Invalid Number');&lt;br /&gt; 15  end;&lt;br /&gt; 16  /&lt;br /&gt;Invalid Number&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, it raises the INVALID_NUMBER exception. I expected that it would raise the VALUE_ERROR as well.&lt;br /&gt;&lt;br /&gt;Tracing the statements reveals the explanation. With the implicit datatype conversion, this is the statement that is executed by the SQL engine (I added some text to the statement to identify the actual statement):&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SELECT /*+ implicit_conversion */'a' &lt;br /&gt;FROM&lt;br /&gt; DUAL &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and with the explicit datatype conversion, this SQL statement gets executed&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SELECT /*+ explicit_conversion */ TO_NUMBER('a') &lt;br /&gt;FROM&lt;br /&gt; DUAL &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Sometimes it's too easy to ignore these little things. I learned something about exceptions again.&lt;br /&gt;Link:&lt;br /&gt;&lt;a href="http://http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/errors.htm#LNPLS00703"&gt;Oracle Documentation on Predefined PL/SQL Exceptions&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-1998510888403370482?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/1998510888403370482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2009/02/value-error-and-invalid-number.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1998510888403370482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/1998510888403370482'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2009/02/value-error-and-invalid-number.html' title='Value Error and Invalid Number'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2259723196924084378</id><published>2008-12-19T19:58:00.001+01:00</published><updated>2009-07-06T09:06:33.228+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Function Based Index'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='Virtual Column'/><title type='text'>Business Rule: Only One Clerk per Department</title><content type='html'>During the 7Up workshop, a workshop geared towards experienced developers who want to get up to speed with everything that happened in Oracle since release 7, one of the “tricks” that passes by is how to implement the Business Rule:&lt;p&gt;&lt;/p&gt;  &lt;blockquote&gt;Only one Clerk per Department is Allowed&lt;/blockquote&gt;&lt;br /&gt;The way we show how to declaratively implement this Business Rule is by using a Unique Function Based Index. Every now and then someone will comment that the implementation is a hack, “'cause an Index is not meant to implement a Business Rule, it’s there to enhance performance.”&lt;br /&gt;I don’t necessarily agree with this, but I do believe that Oracle 11g offers a more elegant solution (be it very similar, but is considered less of a hack).&lt;br /&gt;&lt;p&gt;First let’s take a look at the “hack”, with the Unique Function Based Index. Then we’ll look at the way to do the same in Oracle 11g. Lastly I will show you the similarity between the two.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;Unique Function Based Index&lt;/h3&gt;&lt;br /&gt;First off, the Function Based Index. FBI’s were introduced in Oracle 9&lt;i&gt;i&lt;/i&gt; and allow you to create an index using a function.&lt;br /&gt;Especially useful when you have a query like &lt;pre class="brush: sql"&gt;&lt;br /&gt;select *&lt;br /&gt;from emp&lt;br /&gt;where upper (ename) = :ename&lt;br /&gt;;&lt;br /&gt;&lt;/pre&gt;When the ename column in the example above is indexed (the regular way) you can not use this index because of the UPPER function.&lt;br /&gt;Indexing the ename column with the UPPER function would allow use of the index. Here is the code for this example:&lt;pre class="brush: sql"&gt;create index upper_name&lt;br /&gt;on emp (upper(ename))&lt;br /&gt;&lt;/pre&gt;When the index is created this way, it can be used with the first statement.&lt;br /&gt;&lt;br /&gt;The way to implement the Business Rule “Only one Clerk per Department”:&lt;br /&gt;&lt;pre class="brush: sql"&gt;create UNIQUE index one_clerk_per_deptno&lt;br /&gt;on emp (case job&lt;br /&gt;        when 'CLERK'&lt;br /&gt;  then deptno&lt;br /&gt;  end&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;Using a simple CASE expression only DEPTNO are used in the index when the JOB is equal to CLERK. Because it is a unique index, there can only be one CLERK in each department.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Virtual Columns&lt;/h3&gt;&lt;br /&gt;Some might call the implementation of the Business Rule in the previous section a “hack”. Instead of using indexes to increase performance, the index is used to declaratively implement a Business Rule.&lt;br /&gt;Oracle 11g introduced Virtual Columns. I really like Virtual Columns more and more because of the multitude of possibilities. Using Virtual Columns to implement this Business Rule is very straight forward.&lt;br /&gt;Instead of using the simple CASE expression in an index, we will use it in the definition of the Virtual Column:&lt;pre class="brush: sql"&gt;alter table emp&lt;br /&gt;add one_clerk_per_dept as (case&lt;br /&gt;                          when job='CLERK'&lt;br /&gt;     then deptno&lt;br /&gt;       end&lt;br /&gt;                        )&lt;br /&gt;&lt;/pre&gt;With the statement above an extra column is added to the EMP table. This Virtual Column, based on the CASE expression, will only show a value when the JOB equals CLERK.&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; select job&lt;br /&gt;2       , one_clerk_per_dept&lt;br /&gt;3    from emp&lt;br /&gt;4  /&lt;br /&gt;&lt;br /&gt;JOB       ONE_CLERK_PER_DEPT&lt;br /&gt;--------- ------------------&lt;br /&gt;CLERK                     20&lt;br /&gt;SALESMAN&lt;br /&gt;SALESMAN&lt;br /&gt;MANAGER&lt;br /&gt;SALESMAN&lt;br /&gt;MANAGER&lt;br /&gt;MANAGER&lt;br /&gt;ANALYST&lt;br /&gt;PRESIDENT&lt;br /&gt;SALESMAN&lt;br /&gt;CLERK                     20&lt;br /&gt;CLERK                     30&lt;br /&gt;ANALYST&lt;br /&gt;CLERK                     10&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see in the resultset above, the ONE_CLERK_PER_DEPT has a value in its column when the JOB is a CLERK. Because the Business Rule says “Only one Clerk per Department” we need to make this column UNIQUE.&lt;br /&gt;With this data this is not possible, because department has two CLERKS. One has to go…&lt;br /&gt;&lt;pre class="brush: sql"&gt;SQL&gt; update emp&lt;br /&gt;2     set job = 'NOCLERK'&lt;br /&gt;3   where job = 'CLERK'&lt;br /&gt;4     and rownum = 1&lt;br /&gt;5  /&lt;br /&gt;&lt;br /&gt;1 row updated.&lt;br /&gt;&lt;br /&gt;SQL&gt; alter table emp&lt;br /&gt;2    add constraint one_clerk_uk UNIQUE (one_clerk_per_dept)&lt;br /&gt;3  /&lt;br /&gt;&lt;br /&gt;Table altered.&lt;br /&gt;&lt;br /&gt;SQL&gt; update emp&lt;br /&gt;2     set job = 'CLERK'&lt;br /&gt;3   where job = 'NOCLERK'&lt;br /&gt;4  /&lt;br /&gt;update emp&lt;br /&gt;*&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-00001: unique constraint (ALEX.ONE_CLERK_UK) violated&lt;br /&gt;&lt;/pre&gt;And now the  Business Rule is implemented in a more “natural” way, without resorting to a “hack”.&lt;br /&gt;&lt;h3&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3&gt;The Same Difference…&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Both methods implement the same Business Rule. Both implement the Business Rule in a declarative way. To some people the first method feels like a “hack” and find the second method more “natural”. But how different are they really?&lt;br /&gt;Not as much as you might have suspected. When you use the first method (hacking with Function Based Indexes), the USER_IND_COLUMNS datadictionary view reveals the implementation.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select column_name&lt;br /&gt;2    from user_ind_columns&lt;br /&gt;3   where index_name = 'ONE_CLERK_PER_DEPT'&lt;br /&gt;4  /&lt;br /&gt;&lt;br /&gt;COLUMN_NAME&lt;br /&gt;---------------------------------------------------------&lt;br /&gt;SYS_NC00010$&lt;br /&gt;&lt;br /&gt;SQL&gt; select job&lt;br /&gt;2       , SYS_NC00010$&lt;br /&gt;3    from emp&lt;br /&gt;4  /&lt;br /&gt;&lt;br /&gt;JOB       SYS_NC00010$&lt;br /&gt;--------- ------------&lt;br /&gt;NOCLERK&lt;br /&gt;SALESMAN&lt;br /&gt;SALESMAN&lt;br /&gt;MANAGER&lt;br /&gt;SALESMAN&lt;br /&gt;MANAGER&lt;br /&gt;MANAGER&lt;br /&gt;ANALYST&lt;br /&gt;PRESIDENT&lt;br /&gt;SALESMAN&lt;br /&gt;CLERK               20&lt;br /&gt;CLERK               30&lt;br /&gt;ANALYST&lt;br /&gt;CLERK               10&lt;br /&gt;&lt;/pre&gt;A hidden column is added to the table with a very obscure name. Apparently a Function Based Index is pretty similar to a Virtual Column.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2259723196924084378?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2259723196924084378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2008/12/business-rule-only-one-clerk-per.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2259723196924084378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2259723196924084378'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2008/12/business-rule-only-one-clerk-per.html' title='Business Rule: Only One Clerk per Department'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-2723971684515151220</id><published>2008-12-12T15:02:00.004+01:00</published><updated>2009-07-06T09:10:46.096+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle 11g'/><category scheme='http://www.blogger.com/atom/ns#' term='Materialized View'/><category scheme='http://www.blogger.com/atom/ns#' term='Virtual Column'/><title type='text'>Business Rule: Only use Active Records</title><content type='html'>&lt;p&gt;The Business Rule that needed to implemented: &lt;/p&gt;  &lt;blockquote&gt;Only "active" records can be used in other tables.&lt;/blockquote&gt;  &lt;p&gt;Before Oracle 11g, one way to implement this Business Rule was to Materialized View with a Check Constraint defined on it. However In Oracle 11g you can implement this Business Rule a lot simpler by using a Virtual Column and a Foreign Key.&lt;/p&gt;  &lt;p&gt;First let set up the tables that we are going to use in this example:&lt;/p&gt;  &lt;pre class="brush: sql"&gt;create table lookup&lt;br /&gt;(id number primary key&lt;br /&gt;,code varchar2(10)&lt;br /&gt;,value varchar2(100)&lt;br /&gt;,ind_active varchar2(1)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;alter table lookup&lt;br /&gt;add constraint lookup_ind_chk check (ind_active in ('Y', 'N'))&lt;br /&gt;/ &lt;/pre&gt;&lt;p&gt;This table has the ind_active column which dictates whether the record in this table may be used in other tables. "Y" indicates that we can use it in other tables, "N" indicates that you can not use it (anymore). &lt;/p&gt;&lt;p&gt;Next we need another table to reference our Lookup Table:&lt;/p&gt;&lt;pre class="brush: sql"&gt;create table t&lt;br /&gt;(id number primary key&lt;br /&gt;,lu_id number references lookup&lt;br /&gt;); &lt;/pre&gt;&lt;br /&gt;Together with some data in both tables:&lt;br /&gt;&lt;pre class="brush: sql"&gt;insert into lookup values (1, 'One', 'First Value', 'Y')&lt;br /&gt;/&lt;br /&gt;insert into lookup values (2, 'Two', 'Second Value', 'Y')&lt;br /&gt;/&lt;br /&gt;insert into t values (1, 1)&lt;br /&gt;/&lt;br /&gt;insert into t values (2, 2)&lt;br /&gt;/ &lt;/pre&gt;&lt;p&gt;Now to implement the Business Rule, using the Materialized View method. Because the rule needs to be checked whenever someone wants to use a record from the Lookup table, we want to validate the rule as soon as possible. To have the Materialized View refresh when a COMMIT is issued, we need Materialized View Logs on both base tables: &lt;/p&gt;&lt;pre class="brush: sql"&gt;create materialized view log on t with sequence, primary key including new values&lt;br /&gt;/&lt;br /&gt;create materialized view log on lookup with sequence, primary key including new values&lt;br /&gt;/ &lt;/pre&gt;&lt;p&gt;The Materialized View definition will be &lt;/p&gt;&lt;pre class="brush: sql"&gt;create materialized view t_lookup_mv&lt;br /&gt;build immediate&lt;br /&gt;refresh force on commit&lt;br /&gt;as&lt;br /&gt;select l.ind_active&lt;br /&gt;from t&lt;br /&gt;  , lookup l&lt;br /&gt;where l.id = t.lu_id&lt;br /&gt;/ &lt;/pre&gt;&lt;br /&gt;This way we will get a record in the Materialized View whenever the T-table uses a record in our Lookup Table. The final thing we need is a Check Constraint on this Materialized View, because we only want Lookup records with the active_ind is “Y”:&lt;br /&gt;&lt;pre class="brush: sql"&gt;alter materialized view t_lookup_mv&lt;br /&gt;add constraint active_chk check (ind_active = 'Y');&lt;/pre&gt;&lt;p&gt;Just to check that the Materialized View does what it is supposed to do: &lt;/p&gt;&lt;pre class="brush: sql"&gt;update lookup&lt;br /&gt;set ind_active = 'N'&lt;br /&gt;where rownum &amp;lt;= 1&lt;br /&gt;/&lt;br /&gt;commit;&lt;br /&gt;&lt;br /&gt;ORA-12008: error in materialized view refresh path&lt;br /&gt;ORA-02290: check constraint (ALEX.ACTIVE_CHK) violated &lt;/pre&gt;&lt;p&gt;That seems to work. But as you can see, you need quite a lot of code to implement such a simple rule. The same Business Rule can be implemented in Oracle 11g a lot simpler, with less code.&lt;/p&gt;&lt;h2&gt;Virtual Columns&lt;/h2&gt;&lt;p&gt;This is the way we're going to implement the rule:&lt;/p&gt;&lt;ol&gt;  &lt;li&gt;Add a Virtual Column to the Lookup table&lt;/li&gt;  &lt;li&gt;Create a Foreign Key referencing the Virtual Column&lt;/li&gt;&lt;/ol&gt;And that's all there is to it. Let's take a look at the code. First of all the Virtual Column.&lt;br /&gt;&lt;br /&gt;Virtual Columns are a new feature of the Oracle 11gR1 database. Using this feature you can create an extra column in the table that is based on an expression.&lt;br /&gt;&lt;pre class="brush: sql"&gt;active_fk as (case when ind_active = 'Y' then id end) unique &lt;/pre&gt;&lt;p&gt;The values in this Virtual Column are only shown when the IND_ACTIVE column has the value of "Y". The value that is shown is the primary key of the base table. Querying the LOOKUP table, reveals the content of the ACTIVE_FK Virtual Column: &lt;/p&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;2    from lookup&lt;br /&gt;3  /&lt;br /&gt;&lt;br /&gt;     ID CODE       VALUE                I  ACTIVE_FK&lt;br /&gt;---------- ---------- -------------------- - ----------&lt;br /&gt;      1 One        First Value          Y          1&lt;br /&gt;      2 Two        Second Value         Y          2&lt;/pre&gt;&lt;br /&gt;Changing the IND_ACTIVE column to "N" will remove the value from the ACTIVE_FK column:&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;SQL&gt; update lookup&lt;br /&gt;2 set ind_active = 'N'&lt;br /&gt;3 where id = 2&lt;br /&gt;4 /&lt;br /&gt;&lt;br /&gt;1 row updated.&lt;br /&gt;&lt;br /&gt;SQL&gt; select *&lt;br /&gt;2 from lookup&lt;br /&gt;3 /&lt;br /&gt;&lt;br /&gt;     ID CODE       VALUE                I  ACTIVE_FK&lt;br /&gt;---------- ---------- -------------------- - ----------&lt;br /&gt;      1 One        First Value          Y          1&lt;br /&gt;      2 Two        Second Value         N&lt;br /&gt;&lt;br /&gt;SQL&gt; rollback;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;Because we are going to use the Virtual Column as referenced by a Foreign Key we are going to make it UNIQUE as well.&lt;/p&gt;&lt;p&gt;Now for the table which uses the records from our Lookup Table:&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush: sql"&gt;create table t&lt;br /&gt;(id number primary key&lt;br /&gt;,lu_id number&lt;br /&gt;);&lt;br /&gt;alter table t&lt;br /&gt;add constraint t_lookup_fk foreign key (lu_id) references lookup (active_fk)&lt;br /&gt;/&lt;/pre&gt;The foreign key is this case is not on the primary key, but on the Virtual Column instead.&lt;br /&gt;&lt;br /&gt;Let's add some data to the table and try it out.&lt;br /&gt;&lt;pre class="brush: sql"&gt;&lt;br /&gt;insert into t values (1, 1);&lt;br /&gt;insert into t values (2, 2);&lt;br /&gt;&lt;br /&gt;update lookup&lt;br /&gt;set ind_active = 'N'&lt;br /&gt;where rownum &lt;= 1&lt;br /&gt;/&lt;br /&gt;update lookup&lt;br /&gt; * ERROR at line 1: ORA-02292: integrity constraint (ALEX.T_LOOKUP_FK) violated - child record found&lt;/pre&gt;&lt;br /&gt;And there you have it. It's not possible to use deactivated records anymore.&lt;br /&gt;&lt;h2&gt;Check when you COMMIT?&lt;/h2&gt;&lt;p&gt;Note the difference though, the Materialized View method is checked when you commit your transaction. The Virtual Column method checks it immediately. If you want the Virtual Column to have similar to the Materialized View method, change the foreign key: &lt;/p&gt;&lt;pre class="brush: sql"&gt;alter table t&lt;br /&gt;add constraint t_lookup_fk foreign key (lu_id) references lookup (active_fk)&lt;br /&gt;deferrable initially deferred&lt;br /&gt;/&lt;/pre&gt;Note the last sentence, deferrable initially deferred, this tells Oracle to not validate the constraint immediately but to postpone it until the transaction is ended.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1271763227002553835-2723971684515151220?l=nuijten.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuijten.blogspot.com/feeds/2723971684515151220/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuijten.blogspot.com/2008/12/business-rule-only-use-active-records.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2723971684515151220'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1271763227002553835/posts/default/2723971684515151220'/><link rel='alternate' type='text/html' href='http://nuijten.blogspot.com/2008/12/business-rule-only-use-active-records.html' title='Business Rule: Only use Active Records'/><author><name>Alex Nuijten</name><uri>https://profiles.google.com/118115428098299370580</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-knbfKZFH_8w/AAAAAAAAAAI/AAAAAAAABNY/9_Ah1skArZ0/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1271763227002553835.post-1089060758592649269</id><published>2008-12-06T12:05:00.001+01:00</published><updated>2009-01-09T11:58:25.422+01:00</updated><title type='text'>On my own</title><content type='html'>In 2004 I started working for AMIS Services BV, my current employer, and they started with something "new"...blogging.&lt;div&gt;AMIS is all about sharing knowledge so a Technology Blog was the thing to have.&lt;/div&gt;&lt;div&gt;Everybody in the company was asked to write a contribution for the company blog.&lt;/div&gt;&lt;div&gt;My first entry on the blog was on Tora, an Open Source tool. You can find that entry by &lt;a href="http://technology.amis.nl/blog/119/oracle-open-source-tora-itoolkit-for-oraclei"&gt;clicking here.&lt;/a&gt;&lt;/div&gt;&lt;div&gt;It was blog number 119, as you can see in the URL. Yesterday afternoon I wrote another blog on the &lt;a href="http://technology.amis.nl/blog/4252/subquery-factoring-in-
