﻿<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Steven Feuerstein's Blog</title>
    <description> Life’s short and there’s more to it than coding. How can we change the way we write our programs so that we can spend less time in front of a screen and more time with family, friends and planet earth? Visit my blog for tasty, tight, tidbits of advice and code that you can put to immediate use in your world of programming.</description>
    <link>http://www.toadworld.com/Community/Blogs/tabid/67/BlogId/13/Default.aspx</link>
    <language>en-US</language>
    <managingEditor>Steven Feuerstein</managingEditor>
    <webMaster>webmaster@toadworld.com</webMaster>
    <pubDate>Tue, 02 Dec 2008 07:04:06 GMT</pubDate>
    <lastBuildDate>Tue, 02 Dec 2008 07:04:06 GMT</lastBuildDate>
    <docs>http://backend.userland.com/rss</docs>
    <generator>Blog RSS Generator Version 3.2.0.15477</generator>
    <item>
      <title>Congratulations to the newest Oracle Magazine PL/SQL Developer of the Year!</title>
      <description>&lt;div&gt;As I travel certain parts of the globe doing presentations on PL/SQL, I meet many extremely talented PL/SQL programmers – people who meet the most complex challenges you can imagine with a deft combination of intense creativity, hard work, and of course Oracle PL/SQL.&lt;/div&gt;
&lt;p&gt;Oracle Magazine names only one as the PL/SQL Developer of the Year at each Oracle Open World, and this year the award was given to Alex De Vergori of Betfair.  Oracle Magazine &lt;a href="http://www.oracle.com/technology/oramag/oracle/08-nov/o68awards.html#devergori"&gt;writes&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote dir="ltr" style="margin-right: 0px"&gt;
&lt;p&gt;&lt;strong&gt;Developer works with 250,000 lines of PL/SQL code that deliver success.&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;Successful PL/SQL developers require more than just good technical knowledge, according to Alex De Vergori, Oracle Magazine’s PL/SQL Developer of the Year. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;“They also need fine attention to detail, a passion about what they’re doing, and a high degree of pride in the software that they’re producing,” says De Vergori, a database architect who channels those skills at Betfair, an online gaming company that pioneered the concept of betting exchanges in 2000. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;To manage customers’ bets, De Vergori and the Betfair team have created a core betting engine and business logic that encompass more than 250,000 lines of PL/SQL code. The Betfair solution runs on Oracle Database using distributed databases around the world and an Oracle data warehouse running Oracle Real Application Clusters. Betfair also uses Oracle Coherence for its in-memory distributed data caching requirements, Java for its middle tier, and Ajax for its front-end applications. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;“We take in excess of five million transactions a day, and they all go straight through to the database, where those bets match with other transactions in real time,” says De Vergori. “At peak, we can easily see something like 1,000 bet transactions per second. With Oracle, our growth has been exponential, so that has enabled us to succeed in our market.”&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div&gt;Click &lt;a href="http://www.oracle.com/technology/oramag/oracle/08-may/o38developer.html"&gt;here&lt;/a&gt; if you would like to read more details about how Betfair is using PL/SQL.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Alex, welcome to the "select circle" of PL/SQL Developer of the Year awardees. Now, finally, you understand why you were spending all those long days, nights and weekends writing PL/SQL code!&lt;br /&gt;
 &lt;/div&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/305/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/305/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=305</guid>
      <pubDate>Mon, 03 Nov 2008 15:03:00 GMT</pubDate>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=305</trackback:ping>
    </item>
    <item>
      <title>Play games to become a better developer!</title>
      <description>&lt;div&gt;&lt;strong&gt;Part 2: The Game of Mastermind&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Software development is one heck of a serious job. It turns out, however, that there are several games you can play to improve the quality of code you write. This is the second of two blog entries that introduce you to two of my favorite brain development and training games: &lt;a href="http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/289/Default.aspx"&gt;Set&lt;/a&gt; and Mastermind.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Play either (preferably both) of these games, and you will write better software.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;[ Note that I do &lt;em&gt;not &lt;/em&gt;include Solitaire in this list. Playing this game will definitely not help you become a better developer, but it will pass the time. ]&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I even encourage you to play these games &lt;em&gt;on company time&lt;/em&gt;, with management approval. It will be a good investment by your employer. Sound crazy? I have been told by a number of my students that their manager did, in fact, agree to do this, and everyone is happy with the results. I will come back to this point at the end of this blog.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So the game featured in this blog is Mastermind. Mastermind has been around for decades, usually played on a small plastic box filled with holes into which pegs of different colors are placed. You can find lots of electronic versions of Mastermind on the Internet. Here's the one I used in my blog:&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;a href="http://www.irt.org/games/js/mind/index.htm?b=8&amp;t=8&amp;c=4"&gt;http://www.irt.org/games/js/mind/index.htm?b=8&amp;t=8&amp;c=4&lt;/a&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So how does it work? You have to guess the colors of the pegs in each of the positions, with the smallest possible number of guesses. Each time you make a guess, Mastermind (the electronic version) tells you how close you are by providing clues (when playing with another human being, &lt;em&gt;they &lt;/em&gt;provide the clues – and hopefully they don't make any mistakes).&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;There are two kinds of clues:&lt;/div&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;You got the color and the position right. Usually this clue is indicated with a black peg.&lt;/li&gt;
    &lt;li&gt;You got the color right, but the position is wrong. Usually this clue is indicated with a white peg.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;When you get four black pegs, you have won that round.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The game is more or less difficult depending on:&lt;/div&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;the number of positions&lt;/li&gt;
    &lt;li&gt;the number of choices of colors (including or excluding blanks)&lt;/li&gt;
    &lt;li&gt;whether or not you can repeat colors in the solution&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;You could, of course, simply &lt;em&gt;guess &lt;/em&gt;each time, in which case you will be very lucky (and only lucky) to actually ever win. But the point of the game (at least, the way that I understood the point of the game) is to use &lt;em&gt;logic&lt;/em&gt; to narrow down the choices that are valid based on the clues provided.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Let's walk through a game of Mastermind. In this version, we will not allow blanks nor repeated colors. Here's the starting point:&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;
&lt;img height="531" alt="" width="452" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-1.gif" /&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;First try: it's luck and random selection. In other words, one guess is as good as any other (this first turn is the only time at which this statement is valid)....so I will simply choose the first four colors in the order shown in the game.&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-2.gif" /&gt; &lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;Then I press OK and Mastermind shows me how close I got. One white clue. &lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-3.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Not very close! So only one of these colors is in the solution. This is a good start, precisely because there is so little information. Once I figure out which of these colors is in the solution, I know that the other three are &lt;em&gt;not&lt;/em&gt;.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;What shall I do next? Pick the color that I will assume is in the solution, and move it to another location so that, theoretically, it could then result in a black clue.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I will make the smallest possible effort here, and move black over one. I will then use the next three colors on the second row:&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-4.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Time to verify my second guess against my first clue. I have only color repeated from the first guess, and it is in a different position. OK, this could be right. Wish me luck....press OK..and:&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-5.gif" /&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Not bad at all! Now Mastermind is telling me that two of these four colors are in the solution &lt;em&gt;and&lt;/em&gt; they are in the right place. One other color is also in the solution, but is in the wrong place.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Time to make another guess. Let's assume that red and black are the black clues and white is in the wrong place. We already know that green, brown and purples are not in the solution (if my assumption about black is correct), so what can I use for the fourth color? There is just one left: yellow.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Where should I put the yellow and white? I have already assumed that red and black are in the right positions. So that means that white would have to be in the fourth position (I don't have three black clues), and yellow in the third.&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-6.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;This is getting suspenseful! Press OK and....&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-7.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Hmmm. I seem to have taken a step backwards. Instead of two black clues, I have just one. OK, it is time now to stop and take stock.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;What can I now conclude about the solution, given these three sets of clues? Quite a lot, namely:&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;table cellspacing="2" cellpadding="2" border="1"&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="193"&gt;
            &lt;div&gt;&lt;strong&gt;A fact&lt;/strong&gt;&lt;/div&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="397"&gt;
            &lt;div&gt;&lt;strong&gt;How do I know?&lt;/strong&gt;&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="193"&gt;
            &lt;div&gt;The white is in the third position or the blue is in the fourth. &lt;/div&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="397"&gt;
            &lt;div&gt;I repeated red and black and lost a black clue. Therefore, one of the two on the right half must have been the source of the black clue.&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="193"&gt;
            &lt;div&gt;Both black and red cannot be in the solution.&lt;/div&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="397"&gt;
            &lt;div&gt;If black is in the solution, then green, brown and purple are not. If red is also in the solution, then only one of white and blue can be in it. If white is in it, then the fourth clue tells me that yellow cannot be in it. I have now exhausted all the colors and we do not have a &lt;em&gt;fourth&lt;/em&gt; color. That's not possible. So it's either red or black, but not both.&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td valign="top" width="193"&gt;
            &lt;div&gt;White, blue and yellow must be in the solution.&lt;/div&gt;
            &lt;/td&gt;
            &lt;td valign="top" width="397"&gt;
            &lt;div&gt;The second guess has three clues, so three of the four must be in the solution, but only one of red and black can be, so the other two must be. The third guess also has three clues and again, this means that the two that are &lt;em&gt;not&lt;/em&gt; red and black must be in the solution, so yellow must be in it.&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Now I have enough information to make the smallest numbers of assumptions and produce a solution. If this works, we win. If it doesn't, I will get lots of additional data from which to work (that is, it will show me that an assumption was invalid, forcing me to re-evaluate and take a new path).&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Let's keep going with black in the second position; I haven't yet encountered any reason to think it is not there. That accounts for the one black clue from the second turn. Let's now go with white in the third position. Blue must then be in the first position, and yellow in the fourth.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-8.gif" /&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I then go back and make sure this last guess is consistent with all previous guesses and clues:&lt;/div&gt;
&lt;ul&gt;
    &lt;li&gt;Guess/clue 1: Only black is repeated from the first turn and it is in a different position, so guess 4 could be correct based on this clue.&lt;/li&gt;
    &lt;li&gt;Guess/clue 2: The black and white colors are in the same position and the blue is in a different position, so guess 4 could be correct based on this clue.&lt;/li&gt;
    &lt;li&gt;Guess/clue 3: The black is in the same position, but yellow and white are now in two different positions, and I added in a different color. Guess 4 could be correct based on this clue.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;OK, I have verified that this guess &lt;em&gt;could&lt;/em&gt; be correct. So how did I do? Press OK and....&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102808-9.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I did it!&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Now, that's very satisfying, but I must also stay humble. I was lucky enough to make the correct assumption about black in the very first turn. I could easily have decided to assume that green was the correct color, and then spent several turns making guesses before I discovered I was wrong.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Regardless, at each step of the way, I used deductive logic to extract all possible information from the clues, and use them to construct another guess that is consistent will previous clues.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="3"&gt;So what does this have to do with software?&lt;br /&gt;
 &lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;Isn't it enough fun all by itself?&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Sure, but I did claim that playing this game would help you become a better developer. So I really &lt;em&gt;should&lt;/em&gt; explain. Here goes....&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I claim that playing Mastermind a lot and getting really good at it will help you debug your code more quickly and accurately.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Conversely, if you have trouble "mining" conclusions from clues in Mastermind, I believe that you would find it very difficult, perhaps even torturous to debug your (or anyone else's) code.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Why do I say this? Well, let's walk through how we build our code.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I am handed requirements. I write a first pass at the code to implement those requirements. I get my code to compile (wow!). Then (theoretically), it is time to test my code.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So I run my test, and I get "clues" – my test (hopefully a regression test that exercises my code for a variety of input scenarios) tells me the circumstances of failure (When I pass in "ABC", I get 1 – the right answer. When I pass in "123", I get NULL – the wrong answer).&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I might also trace execution of my code, giving me &lt;em&gt;lots&lt;/em&gt; of additional clues.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I then take all this information (forget about up to four conveniently colored clues – I will usually get many more pieces of feedback then that!), use it to guide my debugging session, and identify changes to the code that I believe will result in a correct program.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;That's my next "guess." I then rerun my tests, get my next set of clues, and on I go.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Now, doesn't that sound just like the process of finding a solution to Mastermind, only way more complicated?&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So if you have trouble playing Mastermind, I've got to conclude that you will more easily get lost in the tangle of code and clues you get when testing. And if you can play Mastermind very well (which means, by the way, that you can also rapidly get to a solution even when duplicate colors and blanks are allowed, greatly increasing the number of possible solutions and requiring more sophisticated strategies for solving the puzzle), I am certain you will find it that much easier to juggle larger amounts of test feedback and apply all of that productively to your code.&lt;br /&gt;
 &lt;/div&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/301/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/301/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=301</guid>
      <pubDate>Tue, 28 Oct 2008 16:23:00 GMT</pubDate>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=301</trackback:ping>
    </item>
    <item>
      <title>Play games to become a better developer!</title>
      <description>&lt;div&gt;&lt;strong&gt;Part 1:  The Game of Set&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Software development is one heck of a serious job. It turns out, however, that there are several games you can play to improve the quality of code you write. This is the first of two blog entries that introduce you to two of my favorite brain development and training games: Set and Mastermind.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Play either (preferably both) of these games, and you will write better software.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;[ Note that I do &lt;em&gt;not &lt;/em&gt;include Solitaire in this list. Playing this game will definitely not help you become a better developer, but it will pass the time. ]&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I even encourage you to play these games &lt;em&gt;on company time&lt;/em&gt;, with management approval. It will be a good investment by your employer. Sound crazy? I have been told by a number of my students that their manager did, in fact, agree to do this, and everyone is happy with the results. I will come back to this point at the end of this blog.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So the game featured in this blog is Set. Set is a "family game of visual perception," according to the creator (lots more details at &lt;a href="http://www.setgame.com"&gt;www.setgame.com&lt;/a&gt;). I think it is better described as a subtle and sophisticated pattern analysis/matching game.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Set is a physical card game, though you can play the daily puzzle &lt;a href="http://setgame.com/set/index.html"&gt;here&lt;/a&gt;. They used to sell a software version of the game, which I will be using in this blog, but it is no longer available.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So how does it work? You lay out twelve cards from the deck on the table of your choice. Here we go:&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;
&lt;img height="300" alt="" width="400" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-1.gif" /&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So, the object of the game is to find sets of three cards. Whoever finds the most sets wins the game.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;You are now wondering: how do I know which three cards go together in a set? Here are the rules:&lt;/div&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;Each card has four characteristics: shape, coloring, number, and filling.&lt;/li&gt;
    &lt;li&gt;Each characteristic has three possible values. Oval, squiggle and diamond for shape; green, red or purple for color; one, two or three objects for number; lined, solid and empty for filling.&lt;/li&gt;
    &lt;li&gt;Three cards make up a set if each characteristic, independent of the others, is all the same or all different.   &lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;An example of a set: all reds, all ones, oval-squiggle-diamond and lined-empty-solid.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Three cards that do not make up a set: red-red-red, two-one-three, solid-solid-solid, and oval-diamond-oval. Two ovals and one diamond are neither all different or all the same.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;In other words, two out of three is a no-go in Set.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Note that you can never have all four characteristics the same (they would then be the same card, and the Set deck contains no repeats). You can, however, have a set in which all characteristics are different. I find this to be the challenging and satisfying set to find.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;OK, let's use deductive logic to find ourselves a set. Notice the two cards in the middle of the top row?&lt;/div&gt;
&lt;p&gt;&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-2.gif" /&gt;&lt;/p&gt;
&lt;div&gt;They could be in a set (any two could, in fact!). Let's see if we can figure out what the third card would &lt;em&gt;have &lt;/em&gt;to be. Then if we are lucky enough to have that card on the table, I will have my first set.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The first two cards:&lt;/div&gt;
&lt;ul&gt;
    &lt;li&gt;Each have two items. So the third card has to have two items, too.&lt;/li&gt;
    &lt;li&gt;Are green and red. So the third card would have to have a different color: purple.&lt;/li&gt;
    &lt;li&gt;Are solid and lined. So the third card would have to have a different filling: empty.&lt;/li&gt;
    &lt;li&gt;Are oval and squiggle. So the third card would have to have a different shape: empty.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;So the third card would have to be: two-purple-diamond-empty.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Can you see it?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-3.gif" /&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;There it is on the bottom row, third column!&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Now, if we were sitting around a table together, I would shout: "Set!" and if did so before anyone else, I would scoop up my set. If someone else said "Set" first, they could then take that or a different set, if they had found one (I don't think there is one).&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So those three cards go into my pile and we put down another three cards:&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-4.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;And the game proceeds this way until all sets have been found.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Can you identify the (at least) three sets in the above twelve cards?&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;There are two different sets using &lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-5.gif" /&gt; and two different sets using &lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-6.gif" /&gt;. Can you find them? See answer at end of this entry.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So that is Set. When you first start to play this game, you will likely follow the path to a solution I showed above; namely, use deductive logic to find the third card given the first two.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;You probably, however, will not play this way very long, at least not if you are playing (competing) with others.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;In Set, stepping your way through a logical proof ("If the first two are red, the third must be red.") is too slow. You will lose again and again. Not to wrry! After you have been playing for a while, your brain will of its own accord (whatever &lt;em&gt;that&lt;/em&gt; means) abandon explicit deductive logic. Instead, your eyes will roam over the entire set of twelve cards and the three cards that make up a set will "appear." Your brain will have by this time trained itself to assimilate the various kinds of input and resolve the problem very rapidly.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="3"&gt;&lt;font size="2"&gt;How does Set help software developers?&lt;br /&gt;
&lt;/font&gt; &lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;So what's the connection to software? There is a movement in the world of software called "Design Patterns." Here's the basic idea: there's nothing new under the sun; any code you write today is likely very similar to what you or someone else wrote yesterday. Most of our code, in other words, follow common patterns, because we are mostly solving the same kinds of problems, albeit described differently in different domains, over and over again.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;So if you can abstract out the pattern, and then provide a similarly generic &lt;em&gt;solution&lt;/em&gt; to the pattern, the next time you encounter a problem that fits the pattern, you can immediately apply the general solution, and work simply on fitting it precisely to the specifics of your current challenge.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;In other words, lots of software development is all about pattern analysis and recognition, followed by taking logic-based action in response to that pattern.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The better you are at identifying patterns, the better you will be at writing elegant software that avoids the repetitions of logic that invites bugs and mitigates against maintainability.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;And that is why I encourage playing this game while you are on company time. In fact, I suggest that team leads or managers organize a weekly Friday pizza lunch during which you play Set as a group. Great team builder and brain builder!&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="2"&gt;Is Set only good for work?&lt;br /&gt;
 &lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;Ha! No way. In fact, if you have children under the age of twelve or thirteen – anyway, at an age when they are still willing to play a game with you, order Set now (you can find locations in the U.S. that sell Set right &lt;a href="http://setgame.com/stores/stores.htm"&gt;here&lt;/a&gt;) and start playing it with your kids.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;I bet a child as young as four could get started (perhaps with only two colors, or some other way of simplifying the rules). The sooner the better – Set will help make your child smarter!&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The other nice thing about playing Set with your kids is that it is very likely that once they get the hang of it, they will regularly and decisively defeat you. Their minds are so nimble, probably devoting their full concentration to the game, while yours is jumbled full of bills to pay, what to cook for dinner, and so on, that you will spare only a part of your total (and totally awesome) brain power.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;And that's OK. Kids generally don't get to control too much of their environment. To know that you are trying hard as you can and &lt;em&gt;still&lt;/em&gt; lose to them, well, that is very empowering. Great for kids when they're young, perhaps a point of regret when they become teenagers.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;In conclusion, I urge you to get your copy of Set today. Play it at home with your family and friends. Play it at work with your co-workers. Make your brain strong and help make the world a far better place than it is today.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="3"&gt;The Answers&lt;br /&gt;
 &lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;em&gt;&lt;font size="2"&gt;Two with &lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-5.gif" /&gt;&lt;/font&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;Green-purple-red&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;One-one-one&lt;/div&gt;
&lt;div&gt;Squiggle- squiggle- squiggle&lt;/div&gt;
&lt;div&gt;Lined-lined-lined&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt; &lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-7.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;One-two-three&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;Purple-purple-purple&lt;/div&gt;
&lt;div&gt;Lined-lined-lined&lt;/div&gt;
&lt;div&gt;Squiggle- squiggle- squiggle&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-8.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;em&gt;&lt;font size="2"&gt;Two with &lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-6.gif" /&gt;&lt;/font&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Same as before, namely:&lt;/div&gt;
&lt;div&gt;&lt;strong&gt;Green-purple-red&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;One-one-one&lt;/div&gt;
&lt;div&gt;Squiggle- squiggle- squiggle&lt;/div&gt;
&lt;div&gt;Lined-lined-lined&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-9.gif" /&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;And&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;Green-purple-red&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;One-one-one&lt;/div&gt;
&lt;div&gt;Squiggle- oval-diamond&lt;/div&gt;
&lt;div&gt;Lined-solid-empty&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;&lt;img alt="" src="http://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.comhttp://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog102008-10.gif" /&gt; &lt;/div&gt;
&lt;div&gt;  &lt;/div&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/289/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/289/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=289</guid>
      <pubDate>Mon, 20 Oct 2008 14:01:00 GMT</pubDate>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=289</trackback:ping>
    </item>
    <item>
      <title>Doing SQL in PL/SQL: key resource from Bryn Llewellyn</title>
      <description>&lt;div&gt;One of the highlights of Oracle Open World 2008 for me was the presentation by Bryn Llewellyn (PL/SQL Product Manager) on “Doing SQL in PL/SQL.”&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Bryn surely has the most thorough and clear understanding of the PL/SQL language of anyone I have met (definitely including me).&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;His talk was detailed and precise (and maybe just a little bit overwhelming. He needed twice the time allotted) on this most important topic.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The bad news about this talk is that most PL/SQL developers will never be able to hear Bryn present it.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;The good news is that everything he talks about (and more) may be found in a newly published whitepaper of the same name.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Simply visit the &lt;a href="http://www.oracle.com/technology/tech/pl_sql/index.html"&gt;Oracle Technology Network PL/SQL Homepage&lt;/a&gt; and click on the hyperlink for that paper. Notice also that Bryn has published an important whitepaper on SQL injection. &lt;br /&gt;
 &lt;br /&gt;
&lt;img height="402" alt="" width="700" src="http://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog100808-1.gif" /&gt;&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Your understanding of PL/SQL will increase dramatically, as will your ability to write high quality PL/SQL programs, by reading both of these papers.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Thanks, Bryn!&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/286/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/286/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=286</guid>
      <pubDate>Wed, 08 Oct 2008 17:09:00 GMT</pubDate>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=286</trackback:ping>
    </item>
    <item>
      <title>For loops or While loops to scan collections?</title>
      <description>&lt;div&gt;I have generally recommended in the past that whenever you are writing code to iterate through the elements of a collection, you should use a while loop, combined with the FIRST-NEXT or LAST-PRIOR collection methods.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;The key advantage of this approach is that the code will not raise a NO_DATA_FOUND exception if your collection is sparse (there is an index value between FIRST and LAST that is not defined). And if your collection is empty, the loop will not execute at all, whereas with a for loop, an empty collection could cause a VALUE_ERROR exception if you are not careful.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Here is an example of the kind of code that requires the use of the while loop:&lt;/div&gt;
&lt;blockquote dir="ltr" style="margin-right: 0px"&gt;
&lt;pre&gt;DECLARE&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;TYPE employee_tt&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;IS&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;TABLE OF employees%ROWTYPE&lt;br /&gt;&lt;span&gt;         &lt;/span&gt;INDEX BY pls_integer; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;employee_cache&lt;span&gt;   &lt;/span&gt;employee_tt;&lt;br /&gt;BEGIN&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;/* Fill the collection using the employee ID&lt;br /&gt;&lt;span&gt;      as the index value - most like these values are&lt;br /&gt;&lt;/span&gt;&lt;span&gt;      NOT sequentially defined; primary keys can&lt;br /&gt;&lt;/span&gt;&lt;span&gt;      certainly have "gaps". */&lt;br /&gt;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;FOR rec IN (SELECT&lt;span&gt;   *&lt;br /&gt;&lt;/span&gt;&lt;span&gt;                 &lt;/span&gt;FROM&lt;span&gt;   &lt;/span&gt;employees)&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;LOOP&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;employee_cache (rec.employee_id) := rec;&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;END LOOP; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;FOR indx IN 1 .. employee_cache.COUNT&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;LOOP&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;DBMS_OUTPUT.put_line (employee_cache (indx).last_name);&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;END LOOP;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;When I execute this block, I get the following error:&lt;br /&gt;
 &lt;br /&gt;
&lt;img height="173" alt="" width="448" src="http://www.toadworld.com/Portals/0/blogimages/Steven Feuerstein/SF-Blog092208-1.gif" /&gt;&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Instead, I should use a while loop:&lt;/div&gt;
&lt;blockquote dir="ltr" style="margin-right: 0px"&gt;
&lt;pre&gt;DECLARE&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;TYPE employee_tt&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;IS&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;TABLE OF employees%ROWTYPE&lt;br /&gt;&lt;span&gt;         &lt;/span&gt;INDEX BY pls_integer;&lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;employee_cache&lt;span&gt;   &lt;/span&gt;employee_tt;&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;l_index&lt;span&gt;           &lt;/span&gt;pls_integer;&lt;br /&gt;BEGIN&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;/* Fill the collection using the employee ID&lt;br /&gt;&lt;span&gt;      as the index value - most like these values are&lt;br /&gt;&lt;/span&gt;&lt;span&gt;      NOT sequentially defined; primary keys can&lt;br /&gt;&lt;/span&gt;&lt;span&gt;      certainly have "gaps". */&lt;br /&gt;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;FOR rec IN (SELECT&lt;span&gt;   *&lt;br /&gt;&lt;/span&gt;&lt;span&gt;                 &lt;/span&gt;FROM&lt;span&gt;   &lt;/span&gt;employees)&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;LOOP&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;employee_cache (rec.employee_id) := rec;&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;END LOOP;&lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;l_index := employee_cache.FIRST;&lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;WHILE (l_index IS NOT NULL)&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;LOOP&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;DBMS_OUTPUT.put_line (employee_cache (l_index).last_name);&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;l_index := employee_cache.NEXT (l_index);&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;END LOOP;&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;and then I will not see any errors.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;So, no doubt about it, this is good advice – but should you &lt;em&gt;always&lt;/em&gt; use the while loop?&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;I suggest that in situations when you know, when you are absolutely sure, that your collection is densely-filled, you should use the FOR loop, for two reasons:&lt;/div&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;It is a simpler solution, requiring less code (reducing the chance of a bug creeping into your code and making it easier to maintain).&lt;/li&gt;
    &lt;li&gt;It is more self-documenting. By using the for loop construct, you are stating that an assumption of this code is that the collection is densely-filled.&lt;/li&gt;
    &lt;li&gt;It is more efficient than the while loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;So when you can be certain that your collection is densely-filled? When....&lt;/div&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;The collection is populated by a BULK COLLECT query. In this situation, the first index used is always 1, and the collection is filled sequentially (2, 3, 4...) from that index.&lt;/li&gt;
    &lt;li&gt;The collection is a nested table assigned its contents from a MULTISET operator (UNION, INTERSECT and EXCEPT). These set level operators always fill sequentially a nested table from index value 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;And what kind of difference in performance can you expect to see? Not a very big difference, but the FOR loop is &lt;em&gt;definitely &lt;/em&gt;faster. I put together the script below (relying on the sf_timer package, included in the &lt;a href="http://www.toadworld.com/Portals/0/stevenf/demo.zip"&gt;demo.zip&lt;/a&gt;.&lt;/div&gt;
&lt;blockquote dir="ltr" style="margin-right: 0px"&gt;
&lt;pre&gt;DECLARE&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;l_index&lt;span&gt;     &lt;/span&gt;pls_integer;&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;l_source&lt;span&gt;   &lt;/span&gt;DBMS_SQL.varchar2a;&lt;br /&gt;BEGIN&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;sf_timer.start_timer; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;       &lt;/span&gt;SELECT&lt;span&gt;    &lt;/span&gt;text&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;BULK COLLECT INTO&lt;span&gt;   &lt;/span&gt;l_source&lt;br /&gt;&lt;span&gt;         &lt;/span&gt;FROM&lt;span&gt;    &lt;/span&gt;all_source; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;sf_timer.show_elapsed_time (&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;'Retrieved ' || TO_CHAR(l_source.COUNT) || ' elements'&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;);&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;--&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;sf_timer.start_timer; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;FOR indx IN 1 .. l_source.COUNT&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;LOOP&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;NULL;&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;END LOOP; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;sf_timer.show_elapsed_time ('FOR loop through collection');&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;--&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;sf_timer.start_timer;&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;l_index := l_source.FIRST; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;WHILE (l_index IS NOT NULL)&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;LOOP&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;NULL;&lt;br /&gt;&lt;span&gt;      &lt;/span&gt;l_index := l_source.NEXT (l_index);&lt;br /&gt;&lt;span&gt;   &lt;/span&gt;END LOOP; &lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;   &lt;/span&gt;sf_timer.show_elapsed_time ('Full collection scan with NEXT');&lt;br /&gt;END;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;I saw the following results:&lt;/div&gt;
&lt;blockquote dir="ltr" style="margin-right: 0px"&gt;
&lt;pre&gt;Retrieved 3079394 elements - Elapsed CPU : 11.14 seconds.&lt;br /&gt;FOR loop through collection - Elapsed CPU : .05 seconds.&lt;br /&gt;Full collection scan with NEXT - Elapsed CPU : .48 seconds. &lt;/pre&gt;
&lt;/blockquote&gt;
&lt;div&gt;In other words, the while loop is an &lt;em&gt;order of magnitude slower &lt;/em&gt; than the for loop. That sounds like a big deal, but it's only a difference of .4 seconds with a scan of over 3,000,000 elements. With smaller collections, you'll probably never notice the difference.&lt;br /&gt;
 &lt;/div&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/280/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/280/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=280</guid>
      <pubDate>Mon, 22 Sep 2008 16:21:00 GMT</pubDate>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=280</trackback:ping>
    </item>
    <item>
      <title>Analyzing code coverage with the PL/SQL profiler</title>
      <description>&lt;p&gt;I have, for the past several years, focused heavily on designing and building an automated code testing tool for PL/SQL: &lt;a href="http://www.toadworld.com/LinkClick.aspx?link=431&amp;tabid=67"&gt;Quest Code Tester for Oracle&lt;/a&gt;. One feature that is often requested as a part of code testing is analysis of code coverage, answering questions like:&lt;/p&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;When I run my program do I use 50% of the code? 75% of the code?  &lt;/li&gt;
    &lt;li&gt;Are there chunks of logic that are never run&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;The only way to get this kind of information is to turn on the PL/SQL profiler (and/or the new hierarchical profiler delivered with Oracle11g). The profiler keeps track of each line run by the program during the profile session. You then query the contents of the plsql_profiler_data|units|runs tables and draw your own conclusions.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;And therein lies the rub.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Code Tester users don't want to see lengthy reports showing which lines where executed. Instead, they want us to provide them a single number that tells them all they need to know: "75% of my program was executed." Period.&lt;/div&gt;
&lt;div&gt;And I would like to provide that feature, I really would. But I am feeling a little bit stumped and thought I would share with you what I have done so far, and my areas of befuddlement, in hopes that you, my dear reader, may be able to help provide additional clarity.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;So what's the problem? Well....to figure out the % of code coverage, I need to....&lt;/div&gt;
&lt;ul&gt;
    &lt;li&gt;decide &lt;em&gt;which&lt;/em&gt; lines of code I should include in the &lt;em&gt;total&lt;/em&gt; number of possible lines that could be executed. For example: do I include comments? What about the IS, BEGIN, END keywords?&lt;/li&gt;
    &lt;li&gt;understand what lines of the code the profiler actually pays attention to, and records as having been run. I have never found the profiler data to be entirely straightforward.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And so I have decided to do some research and exploration. I created a package that contains lines of code with all sorts of line breaks to see if I could isolate how Oracle treats such code in the context of profiling.&lt;/p&gt;
&lt;p&gt;You will find below the code for this package, followed by a utility I wrote to show profiling data in a way that I thought would be useful, and then the output from a profiling session.&lt;/p&gt;
&lt;p&gt;Which leads to my questions for you:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Do you have any ideas on how I should go about computing this single, golden number regarding code coverage?&lt;/li&gt;
    &lt;li&gt;What lines do you think should be included or not?&lt;/li&gt;
    &lt;li&gt;Should I take a different approach in analyzing code and profiler behavior?&lt;/li&gt;
    &lt;li&gt;What do you think of the output I got from my "test" package?&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;Looking forward to your comments,&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Steven&lt;br /&gt;
 &lt;hr /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="3"&gt;Package to exercise profiler&lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;pre&gt;CREATE OR REPLACE PACKAGE what_is_profiled&lt;br /&gt;IS&lt;br /&gt;   TYPE aa1 IS TABLE OF VARCHAR2 (100)&lt;br /&gt;      INDEX BY PLS_INTEGER;&lt;/pre&gt;
&lt;pre&gt;   TYPE aa2 IS TABLE OF VARCHAR2 (100)&lt;br /&gt;      INDEX BY PLS_INTEGER;&lt;/pre&gt;
&lt;pre&gt;   PROCEDURE proc1 (arg IN NUMBER, arg2 OUT VARCHAR2);&lt;/pre&gt;
&lt;pre&gt;   FUNCTION func1&lt;br /&gt;      RETURN VARCHAR2;&lt;br /&gt;      &lt;br /&gt;      procedure driver ;&lt;br /&gt;END what_is_profiled;&lt;br /&gt;/&lt;/pre&gt;
&lt;pre&gt;CREATE OR REPLACE PACKAGE BODY what_is_profiled&lt;br /&gt;IS&lt;br /&gt;   TYPE p_aa1 IS TABLE OF VARCHAR2 (100)&lt;br /&gt;      INDEX BY PLS_INTEGER;&lt;/pre&gt;
&lt;pre&gt;   TYPE p_aa2 IS TABLE OF VARCHAR2 (100)&lt;br /&gt;      INDEX BY PLS_INTEGER;&lt;/pre&gt;
&lt;pre&gt;   PROCEDURE loops (arg IN NUMBER, arg2 OUT VARCHAR2)&lt;br /&gt;   IS&lt;br /&gt;      val&lt;br /&gt;      INTEGER;&lt;br /&gt;      condition1 boolean := true;&lt;br /&gt;      condition2 boolean &lt;br /&gt;      := &lt;br /&gt;      true;&lt;br /&gt;      &lt;br /&gt;   BEGIN&lt;br /&gt;      FOR indx IN 1 .. 100&lt;br /&gt;      LOOP&lt;br /&gt;         NULL;&lt;br /&gt;      END LOOP;&lt;br /&gt;            &lt;br /&gt;      FOR &lt;br /&gt;      indx &lt;br /&gt;      IN &lt;br /&gt;      1 &lt;br /&gt;      .. &lt;br /&gt;      100&lt;br /&gt;      LOOP&lt;br /&gt;         val := 1;&lt;br /&gt;      END &lt;br /&gt;      LOOP; &lt;br /&gt;      &lt;br /&gt;      FOR indx IN 1 .. 100 LOOP NULL; END LOOP;     &lt;/pre&gt;
&lt;pre&gt;      FOR rec IN (SELECT *&lt;br /&gt;                    FROM all_source&lt;br /&gt;                   WHERE ROWNUM &lt; 101)&lt;br /&gt;      LOOP&lt;br /&gt;         val := 1;&lt;br /&gt;      END LOOP;&lt;/pre&gt;
&lt;pre&gt;      FOR &lt;br /&gt;      rec &lt;br /&gt;      IN &lt;br /&gt;      (&lt;br /&gt;      SELECT *&lt;br /&gt;                    FROM all_source&lt;br /&gt;                   WHERE ROWNUM &lt; 101&lt;br /&gt;      )&lt;br /&gt;      LOOP&lt;br /&gt;         val := 1;&lt;br /&gt;      END &lt;br /&gt;      LOOP;&lt;br /&gt;      &lt;br /&gt;      WHILE (condition1 AND condition2)&lt;br /&gt;      LOOP&lt;br /&gt;         condition1 := FALSE;&lt;br /&gt;      END LOOP;&lt;/pre&gt;
&lt;pre&gt;      WHILE &lt;br /&gt;      (&lt;br /&gt;      condition1 &lt;br /&gt;      AND &lt;br /&gt;      condition2&lt;br /&gt;      )&lt;br /&gt;      LOOP&lt;br /&gt;         condition1 &lt;br /&gt;         := &lt;br /&gt;         FALSE&lt;br /&gt;         ;&lt;br /&gt;      END LOOP;&lt;br /&gt;      &lt;br /&gt;      DECLARE&lt;br /&gt;         indx   INTEGER := 1;&lt;br /&gt;      BEGIN&lt;br /&gt;         LOOP&lt;br /&gt;            EXIT WHEN indx &gt; 100;&lt;br /&gt;            indx := indx + 1;&lt;br /&gt;         END LOOP;&lt;br /&gt;      END;&lt;br /&gt;      &lt;br /&gt;      DECLARE&lt;br /&gt;         indx   INTEGER := 1;&lt;br /&gt;      BEGIN&lt;br /&gt;         LOOP&lt;br /&gt;            EXIT &lt;br /&gt;            WHEN &lt;br /&gt;            indx &lt;br /&gt;            &gt; &lt;br /&gt;            100;&lt;br /&gt;            indx := indx + &lt;br /&gt;            1&lt;br /&gt;            ;&lt;br /&gt;         END LOOP;&lt;br /&gt;      END;      &lt;br /&gt;   END;&lt;/pre&gt;
&lt;pre&gt;   PROCEDURE conditionals &lt;br /&gt;   IS&lt;br /&gt;   a &lt;br /&gt;   boolean;&lt;br /&gt;   b boolean;&lt;br /&gt;   c boolean&lt;br /&gt;   ;&lt;br /&gt;   BEGIN&lt;br /&gt;      IF (a AND b OR c)&lt;br /&gt;      THEN&lt;br /&gt;         NULL;&lt;br /&gt;         elsif&lt;br /&gt;         a&lt;br /&gt;         then&lt;br /&gt;         null;&lt;br /&gt;         else&lt;br /&gt;         dbms_output.put_line ('a');&lt;br /&gt;      END IF;&lt;br /&gt;      &lt;br /&gt;      a := case&lt;br /&gt;      true&lt;br /&gt;      when true&lt;br /&gt;      then&lt;br /&gt;      false&lt;br /&gt;      when &lt;br /&gt;      false then&lt;br /&gt;      true&lt;br /&gt;      else&lt;br /&gt;      false&lt;br /&gt;      end&lt;br /&gt;      ;&lt;br /&gt;      a := case true&lt;br /&gt;      when true&lt;br /&gt;      then&lt;br /&gt;      false&lt;br /&gt;      when &lt;br /&gt;      false then&lt;br /&gt;      true&lt;br /&gt;      else&lt;br /&gt;      false&lt;br /&gt;      end&lt;br /&gt;      ;  &lt;br /&gt;      &lt;br /&gt;      case when &lt;br /&gt;      sysdate &gt; sysdate + 1&lt;br /&gt;      then&lt;br /&gt;      a := false;&lt;br /&gt;      when 1 &gt; 2 then&lt;br /&gt;      b := false;&lt;br /&gt;      when 1&lt;br /&gt;      &gt; 2   &lt;br /&gt;      then&lt;br /&gt;      c := false;&lt;br /&gt;      else null; end case; &lt;br /&gt;   END;&lt;/pre&gt;
&lt;pre&gt;   FUNCTION p_func1&lt;br /&gt;      RETURN VARCHAR2&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      RETURN NULL;&lt;br /&gt;   END;&lt;/pre&gt;
&lt;pre&gt;   PROCEDURE proc1 (arg IN NUMBER, arg2 OUT VARCHAR2)&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      NULL;&lt;br /&gt;   END;&lt;/pre&gt;
&lt;pre&gt;   FUNCTION func1&lt;br /&gt;      RETURN VARCHAR2&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      RETURN p_func1;&lt;br /&gt;   END;&lt;br /&gt;   &lt;br /&gt;   procedure driver is&lt;br /&gt;   l varchar2(100);&lt;br /&gt;   begin&lt;br /&gt;   loops(1, l);&lt;br /&gt;   conditionals;&lt;br /&gt;   proc1&lt;br /&gt;   (&lt;br /&gt;   1&lt;br /&gt;   ,&lt;br /&gt;   l);&lt;br /&gt;   GOTO checkloop;&lt;br /&gt;   &lt;&lt;checkloop&gt;&lt;/checkloop&gt;&gt;&lt;br /&gt;   dbms_output.put_line ('a');&lt;br /&gt;   end;&lt;br /&gt;END what_is_profiled;&lt;br /&gt;/&lt;/pre&gt;
&lt;pre&gt;&lt;hr /&gt;&lt;/pre&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="4"&gt;&lt;font size="3"&gt;Utility to show profiler data&lt;/font&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;pre&gt;CREATE OR REPLACE PROCEDURE show_lines_profiled&lt;br /&gt;/*&lt;br /&gt;Assumptions: the ONLY data inside the profiler tables are for a single&lt;br /&gt;run of what_is_profiled.driver&lt;br /&gt;*/&lt;br /&gt;IS&lt;br /&gt;   l_type varchar2( 10000 );&lt;br /&gt;BEGIN&lt;br /&gt;   DBMS_OUTPUT.put_line( 'Profiling Report' );&lt;br /&gt;   DBMS_OUTPUT.put_line( '  NOT PROFILED = No profile data for this line' );&lt;br /&gt;   DBMS_OUTPUT.put_line( '  ZERO RUNS    = Profiled, but TOTAL_OCCURS = 0' );&lt;br /&gt;   DBMS_OUTPUT.put_line( '  LINE RUN     = This line was executed at least once' );&lt;/pre&gt;
&lt;pre&gt;   DBMS_OUTPUT.put_line( 'Profile Info   Line Source');&lt;br /&gt;   DBMS_OUTPUT.put_line( &lt;br /&gt;'============== ==== ==============================================================');&lt;/pre&gt;
&lt;pre&gt;   FOR rec&lt;br /&gt;   IN (  SELECT line, text&lt;br /&gt;           FROM all_source als&lt;br /&gt;          WHERE     als.owner = USER&lt;br /&gt;                AND als.name = 'WHAT_IS_PROFILED'&lt;br /&gt;                AND als.TYPE = 'PACKAGE BODY'&lt;br /&gt;       ORDER BY line )&lt;br /&gt;   LOOP&lt;br /&gt;      BEGIN&lt;br /&gt;         SELECT CASE&lt;br /&gt;                   WHEN total_occur = 0 THEN 'ZERO RUNS'&lt;br /&gt;                   ELSE 'LINE RUN'&lt;br /&gt;                END&lt;br /&gt;                   profile_type&lt;br /&gt;           INTO l_type&lt;br /&gt;           FROM plsql_profiler_data ppd&lt;br /&gt;          WHERE ppd.line# = rec.line AND ppd.unit_number = 2;&lt;br /&gt;      EXCEPTION&lt;br /&gt;         WHEN NO_DATA_FOUND&lt;br /&gt;         THEN&lt;br /&gt;            l_type      := 'NOT PROFILED';&lt;br /&gt;      END;&lt;/pre&gt;
&lt;pre&gt; &lt;/pre&gt;
&lt;pre&gt;      DBMS_OUTPUT.put_line(   RPAD( l_type, 15 )&lt;br /&gt;                           || LPAD( rec.line, 4 )&lt;br /&gt;                           || ' '&lt;br /&gt;                           || rtrim (rec.text, chr(10)));&lt;br /&gt;   END LOOP;&lt;br /&gt;END show_lines_profiled;&lt;/pre&gt;
&lt;pre&gt;&lt;hr /&gt;&lt;/pre&gt;
&lt;div&gt;&lt;strong&gt;&lt;font size="4"&gt;&lt;font size="3"&gt;Results of profiling the package&lt;/font&gt;&lt;/font&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;pre&gt;BEGIN&lt;br /&gt;   DELETE FROM plsql_profiler_data;&lt;/pre&gt;
&lt;pre&gt;   DELETE FROM plsql_profiler_units;&lt;/pre&gt;
&lt;pre&gt;   DELETE FROM plsql_profiler_runs;&lt;/pre&gt;
&lt;pre&gt;   DBMS_OUTPUT.put_line(&lt;br /&gt;                         DBMS_PROFILER.start_profiler( 'What is profiled?' )&lt;br /&gt;   );&lt;br /&gt;   what_is_profiled.driver( );&lt;br /&gt;   DBMS_PROFILER.stop_profiler;&lt;br /&gt;   --&lt;br /&gt;   show_lines_profiled();&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;Profiling Report&lt;br /&gt;  NOT PROFILED = No profile data for this line&lt;br /&gt;  ZERO RUNS    = Profiled, but TOTAL_OCCURS = 0&lt;br /&gt;  LINE RUN     = This line was executed at least once&lt;br /&gt;Profile Info   Line Source&lt;br /&gt;============== ==== ==============================================================&lt;br /&gt;NOT PROFILED      1 PACKAGE BODY what_is_profiled&lt;br /&gt;NOT PROFILED      2 IS&lt;br /&gt;NOT PROFILED      3    TYPE p_aa1 IS TABLE OF VARCHAR2 (100)&lt;br /&gt;NOT PROFILED      4       INDEX BY PLS_INTEGER;&lt;br /&gt;NOT PROFILED      5 &lt;br /&gt;NOT PROFILED      6    TYPE p_aa2 IS TABLE OF VARCHAR2 (100)&lt;br /&gt;NOT PROFILED      7       INDEX BY PLS_INTEGER;&lt;br /&gt;NOT PROFILED      8 &lt;br /&gt;ZERO RUNS         9    PROCEDURE loops (arg IN NUMBER, arg2 OUT VARCHAR2)&lt;br /&gt;NOT PROFILED     10    IS&lt;br /&gt;NOT PROFILED     11       val&lt;br /&gt;NOT PROFILED     12       INTEGER;&lt;br /&gt;LINE RUN         13       condition1 boolean := true;&lt;br /&gt;LINE RUN         14       condition2 boolean &lt;br /&gt;NOT PROFILED     15       := &lt;br /&gt;NOT PROFILED     16       true;&lt;br /&gt;NOT PROFILED     17       &lt;br /&gt;NOT PROFILED     18    BEGIN&lt;br /&gt;LINE RUN         19       FOR indx IN 1 .. 100&lt;br /&gt;NOT PROFILED     20       LOOP&lt;br /&gt;LINE RUN         21          NULL;&lt;br /&gt;NOT PROFILED     22       END LOOP;&lt;br /&gt;NOT PROFILED     23             &lt;br /&gt;LINE RUN         24       FOR &lt;br /&gt;NOT PROFILED     25       indx &lt;br /&gt;NOT PROFILED     26       IN &lt;br /&gt;NOT PROFILED     27       1 &lt;br /&gt;NOT PROFILED     28       .. &lt;br /&gt;NOT PROFILED     29       100&lt;br /&gt;NOT PROFILED     30       LOOP&lt;br /&gt;LINE RUN         31          val := 1;&lt;br /&gt;NOT PROFILED     32       END &lt;br /&gt;NOT PROFILED     33       LOOP; &lt;br /&gt;NOT PROFILED     34       &lt;br /&gt;LINE RUN         35       FOR indx IN 1 .. 100 LOOP NULL; END LOOP;      &lt;br /&gt;NOT PROFILED     36 &lt;br /&gt;LINE RUN         37       FOR rec IN (SELECT *&lt;br /&gt;NOT PROFILED     38                     FROM all_source&lt;br /&gt;NOT PROFILED     39                    WHERE ROWNUM &lt; 101)&lt;br /&gt;NOT PROFILED     40       LOOP&lt;br /&gt;LINE RUN         41          val := 1;&lt;br /&gt;NOT PROFILED     42       END LOOP;&lt;br /&gt;NOT PROFILED     43 &lt;br /&gt;LINE RUN         44       FOR &lt;br /&gt;NOT PROFILED     45       rec &lt;br /&gt;NOT PROFILED     46       IN &lt;br /&gt;NOT PROFILED     47       (&lt;br /&gt;ZERO RUNS        48       SELECT *&lt;br /&gt;NOT PROFILED     49                     FROM all_source&lt;br /&gt;NOT PROFILED     50                    WHERE ROWNUM &lt; 101&lt;br /&gt;NOT PROFILED     51       )&lt;br /&gt;NOT PROFILED     52       LOOP&lt;br /&gt;LINE RUN         53          val := 1;&lt;br /&gt;NOT PROFILED     54       END &lt;br /&gt;NOT PROFILED     55       LOOP;&lt;br /&gt;NOT PROFILED     56       &lt;br /&gt;LINE RUN         57       WHILE (condition1 AND condition2)&lt;br /&gt;NOT PROFILED     58       LOOP&lt;br /&gt;NOT PROFILED     59          condition1 := FALSE;&lt;br /&gt;NOT PROFILED     60       END LOOP;&lt;br /&gt;NOT PROFILED     61 &lt;br /&gt;NOT PROFILED     62       WHILE &lt;br /&gt;NOT PROFILED     63       (&lt;br /&gt;NOT PROFILED     64       condition1 &lt;br /&gt;NOT PROFILED     65       AND &lt;br /&gt;NOT PROFILED     66       condition2&lt;br /&gt;NOT PROFILED     67       )&lt;br /&gt;NOT PROFILED     68       LOOP&lt;br /&gt;NOT PROFILED     69          condition1 &lt;br /&gt;NOT PROFILED     70          := &lt;br /&gt;NOT PROFILED     71          FALSE&lt;br /&gt;NOT PROFILED     72          ;&lt;br /&gt;NOT PROFILED     73       END LOOP;&lt;br /&gt;NOT PROFILED     74       &lt;br /&gt;NOT PROFILED     75       DECLARE&lt;br /&gt;LINE RUN         76          indx   INTEGER := 1;&lt;br /&gt;NOT PROFILED     77       BEGIN&lt;br /&gt;LINE RUN         78          LOOP&lt;br /&gt;LINE RUN         79             EXIT WHEN indx &gt; 100;&lt;br /&gt;LINE RUN         80             indx := indx + 1;&lt;br /&gt;NOT PROFILED     81          END LOOP;&lt;br /&gt;NOT PROFILED     82       END;&lt;br /&gt;NOT PROFILED     83       &lt;br /&gt;NOT PROFILED     84       DECLARE&lt;br /&gt;LINE RUN         85          indx   INTEGER := 1;&lt;br /&gt;NOT PROFILED     86       BEGIN&lt;br /&gt;LINE RUN         87          LOOP&lt;br /&gt;LINE RUN         88             EXIT &lt;br /&gt;NOT PROFILED     89             WHEN &lt;br /&gt;NOT PROFILED     90             indx &lt;br /&gt;NOT PROFILED     91             &gt; &lt;br /&gt;NOT PROFILED     92             100;&lt;br /&gt;LINE RUN         93             indx := indx + &lt;br /&gt;NOT PROFILED     94             1&lt;br /&gt;NOT PROFILED     95             ;&lt;br /&gt;NOT PROFILED     96          END LOOP;&lt;br /&gt;NOT PROFILED     97       END;      &lt;br /&gt;LINE RUN         98    END;&lt;br /&gt;NOT PROFILED     99 &lt;br /&gt;ZERO RUNS       100    PROCEDURE conditionals &lt;br /&gt;NOT PROFILED    101    IS&lt;br /&gt;NOT PROFILED    102    a &lt;br /&gt;NOT PROFILED    103    boolean;&lt;br /&gt;NOT PROFILED    104    b boolean;&lt;br /&gt;NOT PROFILED    105    c boolean&lt;br /&gt;NOT PROFILED    106    ;&lt;br /&gt;NOT PROFILED    107    BEGIN&lt;br /&gt;LINE RUN        108       IF (a AND b OR c)&lt;br /&gt;NOT PROFILED    109       THEN&lt;br /&gt;NOT PROFILED    110          NULL;&lt;br /&gt;NOT PROFILED    111          elsif&lt;br /&gt;LINE RUN        112          a&lt;br /&gt;NOT PROFILED    113          then&lt;br /&gt;NOT PROFILED    114          null;&lt;br /&gt;NOT PROFILED    115          else&lt;br /&gt;LINE RUN        116          dbms_output.put_line ('a');&lt;br /&gt;NOT PROFILED    117       END IF;&lt;br /&gt;NOT PROFILED    118       &lt;br /&gt;LINE RUN        119       a := case&lt;br /&gt;NOT PROFILED    120       true&lt;br /&gt;NOT PROFILED    121       when true&lt;br /&gt;NOT PROFILED    122       then&lt;br /&gt;NOT PROFILED    123       false&lt;br /&gt;NOT PROFILED    124       when &lt;br /&gt;NOT PROFILED    125       false then&lt;br /&gt;NOT PROFILED    126       true&lt;br /&gt;NOT PROFILED    127       else&lt;br /&gt;NOT PROFILED    128       false&lt;br /&gt;NOT PROFILED    129       end&lt;br /&gt;NOT PROFILED    130       ;&lt;br /&gt;LINE RUN        131       a := case true&lt;br /&gt;NOT PROFILED    132       when true&lt;br /&gt;NOT PROFILED    133       then&lt;br /&gt;NOT PROFILED    134       false&lt;br /&gt;NOT PROFILED    135       when &lt;br /&gt;NOT PROFILED    136       false then&lt;br /&gt;NOT PROFILED    137       true&lt;br /&gt;NOT PROFILED    138       else&lt;br /&gt;NOT PROFILED    139       false&lt;br /&gt;NOT PROFILED    140       end&lt;br /&gt;NOT PROFILED    141       ;  &lt;br /&gt;NOT PROFILED    142       &lt;br /&gt;LINE RUN        143       case when &lt;br /&gt;NOT PROFILED    144       sysdate &gt; sysdate + 1&lt;br /&gt;NOT PROFILED    145       then&lt;br /&gt;LINE RUN        146       a := false;&lt;br /&gt;NOT PROFILED    147       when 1 &gt; 2 then&lt;br /&gt;NOT PROFILED    148       b := false;&lt;br /&gt;NOT PROFILED    149       when 1&lt;br /&gt;NOT PROFILED    150       &gt; 2   &lt;br /&gt;NOT PROFILED    151       then&lt;br /&gt;NOT PROFILED    152       c := false;&lt;br /&gt;NOT PROFILED    153       else null; end case; &lt;br /&gt;NOT PROFILED    154    END;&lt;br /&gt;NOT PROFILED    155 &lt;br /&gt;ZERO RUNS       156    FUNCTION p_func1&lt;br /&gt;NOT PROFILED    157       RETURN VARCHAR2&lt;br /&gt;NOT PROFILED    158    IS&lt;br /&gt;NOT PROFILED    159    BEGIN&lt;br /&gt;ZERO RUNS       160       RETURN NULL;&lt;br /&gt;ZERO RUNS       161    END;&lt;br /&gt;NOT PROFILED    162 &lt;br /&gt;ZERO RUNS       163    PROCEDURE proc1 (arg IN NUMBER, arg2 OUT VARCHAR2)&lt;br /&gt;NOT PROFILED    164    IS&lt;br /&gt;NOT PROFILED    165    BEGIN&lt;br /&gt;LINE RUN        166       NULL;&lt;br /&gt;NOT PROFILED    167    END;&lt;br /&gt;NOT PROFILED    168 &lt;br /&gt;ZERO RUNS       169    FUNCTION func1&lt;br /&gt;NOT PROFILED    170       RETURN VARCHAR2&lt;br /&gt;NOT PROFILED    171    IS&lt;br /&gt;NOT PROFILED    172    BEGIN&lt;br /&gt;ZERO RUNS       173       RETURN p_func1;&lt;br /&gt;ZERO RUNS       174    END;&lt;br /&gt;NOT PROFILED    175    &lt;br /&gt;ZERO RUNS       176    procedure driver is&lt;br /&gt;NOT PROFILED    177    l varchar2(100);&lt;br /&gt;NOT PROFILED    178    begin&lt;br /&gt;LINE RUN        179    loops(1, l);&lt;br /&gt;LINE RUN        180    conditionals;&lt;br /&gt;LINE RUN        181    proc1&lt;br /&gt;NOT PROFILED    182    (&lt;br /&gt;NOT PROFILED    183    1&lt;br /&gt;NOT PROFILED    184    ,&lt;br /&gt;NOT PROFILED    185    l);&lt;br /&gt;LINE RUN        186    GOTO checkloop;&lt;br /&gt;NOT PROFILED    187    &lt;&lt;checkloop&gt;&lt;/checkloop&gt;&gt;&lt;br /&gt;LINE RUN        188    dbms_output.put_line ('a');&lt;br /&gt;LINE RUN        189    end;&lt;br /&gt;NOT PROFILED    190 END what_is_profiled;&lt;/pre&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/267/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/267/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=267</guid>
      <pubDate>Wed, 27 Aug 2008 22:55:00 GMT</pubDate>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=267</trackback:ping>
    </item>
    <item>
      <title>Oracle Open World Presentations</title>
      <description>&lt;p&gt;Hey folks, &lt;br /&gt;
 &lt;br /&gt;
For anyone attending Oracle Open World this year, here is my schedule of presentations: &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Session ID: S300184&lt;br /&gt;
Session Title: &lt;strong&gt;Weird PL/SQL&lt;br /&gt;
&lt;/strong&gt;Track: Oracle Develop: Database&lt;br /&gt;
Room: Golden Gate C3&lt;br /&gt;
Date: 2008-09-21&lt;br /&gt;
Start Time: 15:45&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
Session ID: S300183&lt;br /&gt;
Session Title: &lt;strong&gt;Break Your Addiction to SQL!&lt;br /&gt;
&lt;/strong&gt;Track: Oracle Develop: Database&lt;br /&gt;
Room: Salon 02&lt;br /&gt;
Date: 2008-09-22&lt;br /&gt;
Start Time: 13:00
&lt;p&gt; &lt;/p&gt;
Session ID: S300185&lt;br /&gt;
Session Title: &lt;strong&gt;Why You Should Care About Oracle 11g PL/SQL Now&lt;/strong&gt;&lt;br /&gt;
Track: Oracle Develop: Database&lt;br /&gt;
Room: Salon 02&lt;br /&gt;
Date: 2008-09-23&lt;br /&gt;
Start Time: 11:30&lt;/blockquote&gt;
&lt;p&gt; &lt;/p&gt;
Hope to see you there!
&lt;p&gt; &lt;/p&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/252/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/252/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=252</guid>
      <pubDate>Tue, 22 Jul 2008 15:41:00 GMT</pubDate>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=252</trackback:ping>
    </item>
    <item>
      <title>Always Bulk Collect</title>
      <description>&lt;div&gt;You learn something new every day, right? Well, I certainly do (more or less). Even about PL/SQL, about which I am sure many people think I already know &lt;em&gt;everything&lt;/em&gt;. Far from it.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;In fact, I learned just last week from the PL/SQL Product Manager, Bryn Llewellyn, that his recommendation regarding cursor FOR loops and bulk collect is different from mine – and for a very good reason.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Several years ago, he informed me (and provided a script to verify the statement: 10g_optimize_cfl.sql in the &lt;a href="http://www.toadworld.com/LinkClick.aspx?fileticket=WdgBQ7kABao%3d&amp;tabid=67"&gt;demo.zip&lt;/a&gt;) that in Oracle10g, the compiler now optimizes every cursor FOR loop to execute at a level of performance similar to BULK COLLECT. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;This is a wonderful and non-trivial optimization. Rather than have to go through a manual rewrite of all cursor FOR loops, you can leave them in place and reap the benefits of the BULK COLLECT (sort of).&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Well, that's not quite true – and it's even less true than I thought.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;First of all, if your cursor FOR loop contains any DML statements, you will still want to explicitly convert to BULK COLLECT. The reason is that while the optimizer will improve the performance of the query step, it will &lt;em&gt;not&lt;/em&gt; optimize the DML statements to execute like FORALLs (the bulk processing analogue for updates, inserts and deletes).&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;So if you have code that looks like (cfl_to_bulk_0.sql in &lt;em&gt;&lt;u&gt;&lt;a href="http://www.toadworld.comhttp://www.toadworld.com/LinkClick.aspx?fileticket=WdgBQ7kABao%3d&amp;tabid=67"&gt;demo.zip&lt;/a&gt;&lt;/u&gt;&lt;/em&gt;):&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;font face="courier new" size="2"&gt;PROCEDURE upd_for_dept (&lt;br /&gt;
   dept_in     IN   employees.department_id%TYPE&lt;br /&gt;
 , newsal_in   IN   employees.salary%TYPE&lt;br /&gt;
)&lt;br /&gt;
IS&lt;br /&gt;
   CURSOR emp_cur&lt;br /&gt;
   IS&lt;br /&gt;
      SELECT employee_id, salary, hire_date&lt;br /&gt;
        FROM employees&lt;br /&gt;
       WHERE department_id = dept_in;&lt;br /&gt;
BEGIN&lt;br /&gt;
   FOR rec IN emp_cur&lt;br /&gt;
   LOOP&lt;br /&gt;
      BEGIN&lt;br /&gt;
         INSERT INTO employee_history&lt;br /&gt;
                     (employee_id, salary, hire_date&lt;br /&gt;
                     )&lt;br /&gt;
              VALUES (rec.employee_id, rec.salary, rec.hire_date&lt;br /&gt;
                     );&lt;br /&gt;
 &lt;br /&gt;
         adjust_compensation (rec.employee_id, rec.salary);&lt;br /&gt;
 &lt;br /&gt;
         UPDATE employees&lt;br /&gt;
            SET salary = newsal_in&lt;br /&gt;
          WHERE employee_id = rec.employee_id;&lt;br /&gt;
      EXCEPTION&lt;br /&gt;
         WHEN OTHERS&lt;br /&gt;
         THEN&lt;br /&gt;
            log_error;&lt;br /&gt;
      END;&lt;br /&gt;
   END LOOP;&lt;br /&gt;
END upd_for_dept;&lt;br /&gt;
&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;/div&gt;
&lt;div&gt;You will need to change the cursor FOR loop to BULK COLLECT so that you can fill up collections with data from the query, which can then be used in the FORALL statement. You will, sadly (due to increased complexity and program length) end up with something like this (cfl_to_bulk_5.sql in &lt;em&gt;&lt;u&gt;&lt;a href="http://www.toadworld.comhttp://www.toadworld.com/LinkClick.aspx?fileticket=WdgBQ7kABao%3d&amp;tabid=67"&gt;demo.zip&lt;/a&gt;&lt;/u&gt;&lt;/em&gt;):&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;font size="2"&gt;&lt;font face="courier new" size="2"&gt;PROCEDURE upd_for_dept (&lt;br /&gt;
   dept_in     IN   employees.department_id%TYPE&lt;br /&gt;
 , newsal_in   IN   employees.salary%TYPE&lt;br /&gt;
)&lt;br /&gt;
IS&lt;br /&gt;
   bulk_errors    EXCEPTION;&lt;br /&gt;
   PRAGMA EXCEPTION_INIT (bulk_errors, -24381);&lt;br /&gt;
 &lt;br /&gt;
   TYPE employee_tt IS TABLE OF employees.employee_id%TYPE&lt;br /&gt;
      INDEX BY BINARY_INTEGER;&lt;br /&gt;
 &lt;br /&gt;
   employee_ids   employee_tt;&lt;br /&gt;
 &lt;br /&gt;
   TYPE salary_tt IS TABLE OF employees.salary%TYPE&lt;br /&gt;
     INDEX BY BINARY_INTEGER;&lt;br /&gt;
 &lt;br /&gt;
   salaries       salary_tt;&lt;br /&gt;
 &lt;br /&gt;
   TYPE hire_date_tt IS TABLE OF employees.hire_date%TYPE&lt;br /&gt;
      INDEX BY BINARY_INTEGER;&lt;br /&gt;
 &lt;br /&gt;
   hire_dates     hire_date_tt;&lt;br /&gt;
 &lt;br /&gt;
   CURSOR employees_cur&lt;br /&gt;
   IS&lt;br /&gt;
      SELECT     employee_id, salary, hire_date&lt;br /&gt;
            FROM employees&lt;br /&gt;
           WHERE department_id = dept_in&lt;br /&gt;
      FOR UPDATE;&lt;br /&gt;
 &lt;br /&gt;
   PROCEDURE fetch_data_quickly (&lt;br /&gt;
      limit_in           IN       PLS_INTEGER&lt;br /&gt;
    , employee_ids_out   OUT      employee_tt&lt;br /&gt;
    , salaries_out       OUT      salary_tt&lt;br /&gt;
    , hire_dates_out     OUT      hire_date_tt&lt;br /&gt;
   )&lt;br /&gt;
   IS&lt;br /&gt;
   BEGIN&lt;br /&gt;
      FETCH employees_cur&lt;br /&gt;
      BULK COLLECT INTO employee_ids_out, salaries_out, hire_dates_out LIMIT limit_in;&lt;br /&gt;
   END fetch_data_quickly;&lt;br /&gt;
 &lt;br /&gt;
   PROCEDURE adj_comp_for_arrays (&lt;br /&gt;
      employee_ids_io   IN OUT   employee_tt&lt;br /&gt;
    , salaries_io       IN OUT   salary_tt&lt;br /&gt;
   )&lt;br /&gt;
   IS&lt;br /&gt;
   BEGIN&lt;br /&gt;
      FOR indx IN 1 .. employee_ids_io.COUNT&lt;br /&gt;
      LOOP&lt;br /&gt;
         adjust_compensation (employee_ids_io (indx), salaries_io (indx));&lt;br /&gt;
      END LOOP;&lt;br /&gt;
   END adj_comp_for_arrays;&lt;br /&gt;
 &lt;br /&gt;
   PROCEDURE insert_history&lt;br /&gt;
   IS&lt;br /&gt;
   BEGIN&lt;br /&gt;
      FORALL indx IN employee_ids.FIRST .. employee_ids.LAST SAVE EXCEPTIONS&lt;br /&gt;
         INSERT INTO employee_history&lt;br /&gt;
                     (employee_id, salary, hire_date&lt;br /&gt;
                     )&lt;br /&gt;
              VALUES (employee_ids (indx), salaries (indx), hire_dates (indx)&lt;br /&gt;
                     );&lt;br /&gt;
   EXCEPTION&lt;br /&gt;
      WHEN bulk_errors&lt;br /&gt;
      THEN&lt;br /&gt;
         FOR indx IN 1 .. SQL%BULK_EXCEPTIONS.COUNT&lt;br /&gt;
         LOOP&lt;br /&gt;
            -- Log the error&lt;br /&gt;
            log_error&lt;br /&gt;
                     (   'Unable to insert history row for employee '&lt;br /&gt;
                      || employee_ids&lt;br /&gt;
                                     (SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX)&lt;br /&gt;
                    , SQL%BULK_EXCEPTIONS (indx).ERROR_CODE&lt;br /&gt;
                     );&lt;br /&gt;
            /*&lt;br /&gt;
            Communicate this failure to the update phase:&lt;br /&gt;
            Delete this row so that the update will not take place.&lt;br /&gt;
            */&lt;br /&gt;
            employee_ids.DELETE (SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX);&lt;br /&gt;
         END LOOP;&lt;br /&gt;
   END insert_history;&lt;br /&gt;
 &lt;br /&gt;
   PROCEDURE update_employee&lt;br /&gt;
   IS&lt;br /&gt;
   BEGIN&lt;br /&gt;
      /*&lt;br /&gt;
        Use Oracle10g INDICES OF to avoid errors &lt;br /&gt;
        from a sparsely-populated employee_ids collection.&lt;br /&gt;
      */&lt;br /&gt;
      FORALL indx IN INDICES OF employee_ids SAVE EXCEPTIONS&lt;br /&gt;
         UPDATE employees&lt;br /&gt;
            SET salary = newsal_in&lt;br /&gt;
              , hire_date = hire_dates (indx)&lt;br /&gt;
          WHERE employee_id = employee_ids (indx);&lt;br /&gt;
   EXCEPTION&lt;br /&gt;
      WHEN bulk_errors&lt;br /&gt;
      THEN&lt;br /&gt;
         FOR indx IN 1 .. SQL%BULK_EXCEPTIONS.COUNT&lt;br /&gt;
         LOOP&lt;br /&gt;
            log_error&lt;br /&gt;
                     (   'Unable to update salary for employee '&lt;br /&gt;
                      || employee_ids&lt;br /&gt;
                                     (SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX)&lt;br /&gt;
                    , SQL%BULK_EXCEPTIONS (indx).ERROR_CODE&lt;br /&gt;
                     );&lt;br /&gt;
         END LOOP;&lt;br /&gt;
   END update_employee;&lt;br /&gt;
BEGIN&lt;br /&gt;
   OPEN employees_cur;&lt;br /&gt;
 &lt;br /&gt;
   LOOP&lt;br /&gt;
      fetch_data_quickly (100, employee_ids, salaries, hire_dates);&lt;br /&gt;
      EXIT WHEN employee_ids.COUNT = 0;&lt;br /&gt;
      insert_history;&lt;br /&gt;
      adj_comp_for_arrays (employee_ids, salaries);&lt;br /&gt;
      update_employee;&lt;br /&gt;
   END LOOP;&lt;br /&gt;
END upd_for_dept;&lt;br /&gt;
&lt;/font&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;/div&gt;
&lt;div&gt;Sorry, I know that I should probably explain this code but to be brutally honest my dad had heart bypass surgery and I am currently at the hospital and so you get the abbreviated version ....&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Anyway, that is one scenario where you definitely need to convert from the cursor FOR loop. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;But Bryn, to my surprise, recommends that you &lt;em&gt;always&lt;/em&gt; convert explicitly to BULK COLLECT. Why is that? For performance reasons. Apparently, the cursor FOR loop optimization makes the code run faster, but not as quickly as BULK COLLECT.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;In my tests (which you can reproduce in cfl_vs_bulkcollect.sql), I found that BULK COLLECT ran 33% faster than the cursor FOR loop:&lt;/div&gt;
&lt;font size="2"&gt;
&lt;ul&gt;
    &lt;li&gt;Cursor For Loop Elapsed: 8.12 seconds. &lt;/li&gt;
    &lt;li&gt;Bulk Collect Elapsed: 5.46 seconds.  &lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;So if you really want the best performance, take the time and make the effort to switch over to BULK COLLECT.&lt;br /&gt;
 &lt;/div&gt;
&lt;/font&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/239/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/239/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=239</guid>
      <pubDate>Mon, 23 Jun 2008 14:34:00 GMT</pubDate>
      <slash:comments>4</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=239</trackback:ping>
    </item>
    <item>
      <title>How to Run an OS Command from PL/SQL</title>
      <description>&lt;p&gt;Oracle doesn't make it terribly easy to run operating system commands from within a PL/SQL block. I suppose that's understandable, given that PL/SQL is an embedded database-oriented language. Still, developers do ask me on a regular basis about how they can do this.&lt;/p&gt;
&lt;p&gt;As I understand it, there are basically three ways to do this:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Invoke a Java method from within a PL/SQL wrapper&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Call a C program as an external procedure from within PL/SQL.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Use the new DBMS_SCHEDULER package.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I will soon publish a DBMS_SCHEDULER solution (written by Bryn Llewellyn, PL/SQL Product Manager) on my OTN Best Practices column. In the meantime, you will find below a quick review of the steps needed to do this in Java and C.&lt;/p&gt;
&lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Executing Host Command with Java&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;With the Java approach, you will take these steps:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Find the Java class that implements host command execution.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Build a class that invokes that host command method. Let's call it HostCommand.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Build a PL/SQL program that calls a method in HostCommand to run your command.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Acquire the privileges needed to execute host commands via Java in the database.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's go through each of these steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Find the Java class that implements host command execution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Objects of the java.lang.Runtime class include an exec method that will execute a host command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Build a class that invokes that host command method.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let's call it HostCommand.&lt;/p&gt;
&lt;p&gt;Here's code to create a new Java class in the database to invoke this command for the Windows XP operating system:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "UTLcmd"&lt;br /&gt;AS import java.lang.Runtime;&lt;br /&gt;public class execHostCommand&lt;br /&gt;{ &lt;br /&gt;  public static void execute (String command) &lt;br /&gt;    throws java.io.IOException&lt;br /&gt;  {&lt;br /&gt;   String osName = System.getProperty("os.name");&lt;br /&gt;   if(osName.equals("Windows XP"))&lt;br /&gt;       command = "cmd /c " + command;&lt;br /&gt;   Runtime rt = java.lang.Runtime.getRuntime();&lt;br /&gt;   rt.exec(command);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can easily modify the execute method to support other operating systems based on the value of osName.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Build a PL/SQL program that calls a method in HostCommand to run your command.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;CREATE OR REPLACE PACKAGE host_command IS&lt;br /&gt;  PROCEDURE execute (cmd IN VARCHAR2) AS LANGUAGE JAVA NAME&lt;br /&gt;           'execHostCommand.execute(java.lang.String)';&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notice that I "map" the VARCHAR2 datatype to the java.lang.String class in my call to the new Java method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Acquire the privileges needed to execute host commands via Java in the database.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You need special privileges to execute host commands from within the database through Java. Otherwise when you try to execute your command you will see an error like this (the error message will vary depending on what you are trying to do):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img width="449" height="178" src="http://www.toadworld.com/Portals/0/blogimages/sf_blog060408.gif" alt="" /&gt;&lt;br /&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One way to obtain these privileges is to have the JAVASYSPRIV role granted to your schema. This role contains all the privileges you need (and more).&lt;/p&gt;
&lt;p&gt;For a more nuanced approach to granting the required privileges, you can also use the Java security API available in the Oracle database. For example, if I want to delete a file using a host command (perhaps it is not accessible through UTL_FILE), I will need to grant the following privileges to the schema in which the command is executed, such as HR:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;BEGIN&lt;br /&gt;   DBMS_JAVA.grant_permission ('HR'&lt;br /&gt;                             , 'SYS:java.io.FilePermission'&lt;br /&gt;                             , '&lt;&lt;ALL FILES&gt;&gt;'&lt;br /&gt;                             , 'execute'&lt;br /&gt;                              );&lt;br /&gt;   DBMS_JAVA.grant_permission ('HR'&lt;br /&gt;                             , 'SYS:java.lang.RuntimePermission'&lt;br /&gt;                             , 'writeFileDescriptor'&lt;br /&gt;                             , ''&lt;br /&gt;                              );&lt;br /&gt;   DBMS_JAVA.grant_permission ('HR'&lt;br /&gt;                             , 'SYS:java.lang.RuntimePermission'&lt;br /&gt;                             , 'readFileDescriptor'&lt;br /&gt;                             , ''&lt;br /&gt;                              );&lt;br /&gt;END;&lt;br /&gt;/&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;font size="3"&gt;&lt;strong&gt;Executing Host Command with C&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;To use C, you must define an external procedure and then invoke it within your PL/SQL block. It is not possible within this article to cover completely all the steps and issues involved in setting up such an external procedure. I will, instead, cover the highlights. For the full details, read Chapter 27 of Oracle PL/SQL Programming, 4th edition, in which my co-author Bill Pribyl thoroughly explains external procedures.&lt;/p&gt;
&lt;p&gt;As with Java, you will need help from your database administrator to supply the privileges needed to execute your host command in C. &lt;/p&gt;
&lt;p&gt;Here are the steps to follow with C:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Find (or build) the C program that implements host command execution.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Save the C source to a file and generate a shared library for it.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Save the library file where Oracle can find it.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Define a library inside Oracle that is associated with the shared library on disk.&lt;br /&gt;
     &lt;/li&gt;
    &lt;li&gt;Create a PL/SQL wrapper for the C function.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's go through each of these steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Find (or build) the C program that implements host command execution.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The C system function executes an operating system command. So I build a simple C function, extprocsh(), that accepts a string and passes it to the system function for execution:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;int extprocsh(char *cmd)&lt;br /&gt;{&lt;br /&gt;   return system(cmd);&lt;br /&gt;}&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The function returns the result code as provided by system, a function normally found in the C runtime library (libc) on Unix, or in msvcrt.dll on Microsoft platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Save the C source to a file and generate a shared library for it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I save the source code in a file named extprocsh.c. I then use the GNU C compiler to generate a shared library. On a 64-bit Solaris machine running GCC 3.4.2 and Oracle Database 10g Release 2, the following compiler command worked to create a shared library:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;gcc -m64 extprocsh.c -fPIC -G -o extprocsh.so&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Similarly, on Microsoft Windows XP Pro running GCC 3.2.3 from Minimal GNU for Windows (MinGW), also with Oracle Database 10g Release 2, this works:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;c:\MinGW\bin\gcc extprocsh.c -shared -o extprocsh.dll&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. Save the library file where Oracle can find it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;These commands generate a shared library file, extprocsh.so or extprocsh.dll. Now I need to put the library file somewhere that Oracle can find it. The default locations for Windows and Unix respectively are:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;$ORACLE_HOME/bin&lt;br /&gt;$ORACLE_HOME/lib&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you want to use a non-default location, you will need to edit the listener configuration file and supply path value(s) for the ENVS="EXTPROC_DLLS...".&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Define a library inside Oracle that is associated with the shared library on disk.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After copying the file and/or making adjustments to the listener, you will then define a "library" inside Oracle to point to the DLL. For example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;CREATE OR REPLACE LIBRARY extprocshell_lib &lt;br /&gt;   AS '/u01/app/oracle/local/lib/extprocsh.so';   -- Unix&lt;br /&gt;     &lt;br /&gt;CREATE OR REPLACE LIBRARY extprocshell_lib&lt;br /&gt;   AS 'c:\oracle\local\lib\extprocsh.dll';      -- Microsoft&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; performing this step requires Oracle's CREATE LIBRARY privilege.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Create a PL/SQL wrapper for the C function.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now I can create a PL/SQL call specification which uses the newly created library:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;CREATE OR REPLACE FUNCTION exec_host_command (cmd IN VARCHAR2)&lt;br /&gt;   RETURN PLS_INTEGER&lt;br /&gt;AS&lt;br /&gt;   LANGUAGE C&lt;br /&gt;   LIBRARY extprocshell_lib&lt;br /&gt;   NAME "extprocsh"&lt;br /&gt;   PARAMETERS (cmd STRING, RETURN INT);&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then, assuming that your DBA has set up the system environment to support external procedures, the exec_host_command function can now be called anywhere you can invoke a PL/SQL function.  &lt;/p&gt;
&lt;p&gt;Note that these operating system commands will execute with the same privileges as the Oracle Net listener that spawns the extproc process.&lt;/p&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/228/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/228/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=228</guid>
      <pubDate>Wed, 04 Jun 2008 14:11:00 GMT</pubDate>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=228</trackback:ping>
    </item>
    <item>
      <title>Testing and refreshing data from production</title>
      <description>&lt;div&gt;As many of my readers likely know by now, I have been working for the past several years on the Quest Code Tester development effort.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Code Tester is the most powerful PL/SQL test automation tool available. You describe the expected behavior of your programs and Code Tester generates your test code, which can then be run from the UI or via a script. With Code Tester, you can build comprehensive regression tests and even implement the Test Driven Development methodology.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;In the process of talking about Code Tester with many developers, I have come across a belief regarding code testing and the refreshing of data from production tables that I think actually reflects a misunderstanding about both how to use Code Tester specifically and, more generally, how to test our code.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Here's the way this belief was expressed by a customer recently:&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;"We have a development environment - let's call it TEST. Inside this environment is the supporting schema / repository for Code Tester, as well as the test definitions and generated test code."&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;"Once a week, we refresh the TEST environment from the production environment, PROD. PROD doesn't contain a Code Tester repository, so after I refresh, I lose my test repository and code."&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Clearly, this developer can do an export of the Code Tester schema and then import it after refresh, but he was concerned about having to add any overhead for the DBA to this refresh process.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Actually, I think the problem goes much deeper than that: if you refresh your test tables with production data on a regular basis, you will find it very difficult indeed to create stable regression tests that can be run against your code.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Why do I say that? Because when you refresh data from production, you change the "inputs" to your programs (contents of the tables) and therefore you will almost certainly have to change the expected results for your tests.&lt;/div&gt;
&lt;div&gt;In fact, I think that when it comes to &lt;em&gt;functional testing of your programs &lt;/em&gt;(does it meet user requirements?), you should &lt;em&gt;not&lt;/em&gt; be refreshing your test tables from production. To understand why I would say this, let's talk about....&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;em&gt;&lt;font size="3"&gt;How we make sure our programs work&lt;br /&gt;
 &lt;/font&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;There are as many kinds of tests as there are definitions of what it means for a program to "work." We need to make sure, for example, that our programs meet functional requirements (they are correct) and also that they run quickly enough to avoid user frustration. The programs need to scale up for many users and lots of data, etc.&lt;/div&gt;
&lt;div&gt; &lt;br /&gt;
&lt;a href="http://unittest.inside.quest.com/index.jspa"&gt;Quest Code Tester&lt;/a&gt; is designed specifically to help you implement tests on functional requirements; in essence, to verify that your program is correct.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;To do this, I will almost always want to compare the actual results of running my program with the expected results or control data. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;For example, if table XYZ contains a certain set of rows, then after running the program, table ABC should be changed in a specific way. Or the program is a function such that when I pass "ABC" for an IN argument, the function returns 100, and so on.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;In general, there is no room for ambiguity here. Either the program works as expected or it does not. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Now, if I want to build regression tests and &lt;em&gt;automate&lt;/em&gt; the process of testing my program, I need to able to tell Code Tester that for a given set of inputs, I expect the associated outcomes. And – this is the key thing to realize – those inputs can't keep changing on me. Every time the inputs change, I would need to change the expected outcomes. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Constantly updating one's test code might make sense if you are perform manual tests from hand-coded scripts. But if you want to build comprehensive, serious regression tests, then you need a stable, consistent environment from which to run those tests.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Every time you refresh data from production, you change the values in your table and thus you cannot reliably execute your regression test.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;em&gt;&lt;font size="3"&gt;But don't we need real production data to really test?&lt;br /&gt;
 &lt;/font&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;Is this a big problem? Does this mean that we can't or shouldn't build static, repeatable regression tests for our code? After all (so the thinking goes), we need to test our code against production data to make sure that code handles "real world" situations.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Clearly, our code does need to work properly with production data.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;This does not mean, however, that you need to constantly change the data for your functional tests as production data changes. It &lt;em&gt;does&lt;/em&gt; mean that the data used in functional tests should &lt;em&gt;represent&lt;/em&gt; the variety of data found in production. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;In other words, the data in your test tables must be sufficiently varied to allow you to exercise the program to verify all requirements. It doesn't really matter so much that the data is precisely &lt;em&gt;the same&lt;/em&gt; as that found in production.&lt;/div&gt;
&lt;div&gt;And, again, if you keep changing the test data, you must also change your test definition.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;&lt;strong&gt;&lt;em&gt;&lt;font size="3"&gt;OK, but don’t we need to test against production volume?&lt;br /&gt;
 &lt;/font&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;There's another problem with basing functionality testing around production data: the data volumes are generally too large, increasing the time it takes to complete the tests. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;You certainly do need to make sure your code executes with production volumes of data. But that sort of &lt;em&gt;stress testing&lt;/em&gt; should be done independently of your functionality testing. With functionality testing, you want your tests to run as quickly as possible, for these reasons:&lt;/div&gt;
&lt;ul type="disc"&gt;
    &lt;li&gt;You will have lots of (dozens, perhaps hundreds) of separate test cases to run; if each test takes five minutes due to data volume, the test cycle will take an enormous amount of time.&lt;br /&gt;
      &lt;/li&gt;
    &lt;li&gt;Ideally, you run your tests after each change you make to your program. That way, you can immediately determine if you have introduced any bugs. But if running those tests takes an hour, you will test less frequently and you will get less "return" on your investment of creating your tests. &lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;strong&gt;&lt;em&gt;&lt;font size="3"&gt;Conclusion: Segregate your functional test environment&lt;br /&gt;
 &lt;/font&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;div&gt;The most important thing to verify about your program is that it is &lt;em&gt;correct&lt;/em&gt;: it meets user requirements. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;The best way to do this is to build a regression test that you can run after any change to the program, to ensure that it has no bugs. &lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;A regression test should not have to be changed as long as the program itself has not changed. It should work from a consistent set of "inputs" (values for IN arguments, contents of any tables queried by programs, etc.) that do &lt;em&gt;not&lt;/em&gt; change. [Of course, you may need to make some changes along the way as program requirements change, as you add more test cases, etc. That is different, however, from daily or weekly refreshes.]&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;In addition, you want regression tests to run as efficiently as possible. These tests focus on program functionality, not performance. So you want the &lt;em&gt;minimum &lt;/em&gt;volume of data in test tables that allow you to cover your requirements. Use different tests to verify adequate performance.&lt;br /&gt;
 &lt;/div&gt;
&lt;div&gt;Consequently, when it comes to functionality testing, you should avoid refreshing your test tables from production. Instead, invest the time upfront to come up with setup scripts to populate tables with data that fully exercises your code. Include those setup scripts in your Code Tester test definitions and then you have an independent, consistent test environment.&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt; &lt;/div&gt;</description>
      <link>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/204/Default.aspx</link>
      <author>Steven Feuerstein</author>
      <comments>http://www.toadworld.com/Community/Blogs/tabid/67/EntryID/204/Default.aspx#Comments</comments>
      <guid isPermaLink="true">http://www.toadworld.com/Default.aspx?tabid=67&amp;EntryID=204</guid>
      <pubDate>Thu, 17 Apr 2008 16:21:00 GMT</pubDate>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://www.toadworld.com/DesktopModules/Blog/Trackback.aspx?id=204</trackback:ping>
    </item>
  </channel>
</rss>