<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Unfettered Blather</title>
	<atom:link href="http://unfetteredblather.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://unfetteredblather.wordpress.com</link>
	<description>Prattle about Postgres</description>
	<lastBuildDate>Wed, 08 Jun 2011 13:16:03 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='unfetteredblather.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Unfettered Blather</title>
		<link>http://unfetteredblather.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://unfetteredblather.wordpress.com/osd.xml" title="Unfettered Blather" />
	<atom:link rel='hub' href='http://unfetteredblather.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Anatomy of a FOR loop &#8211; part 3</title>
		<link>http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-part-3/</link>
		<comments>http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-part-3/#comments</comments>
		<pubDate>Sun, 22 Aug 2010 17:40:43 +0000</pubDate>
		<dc:creator>korryd</dc:creator>
				<category><![CDATA[PL/pgSQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[FOR loops]]></category>

		<guid isPermaLink="false">http://unfetteredblather.wordpress.com/?p=44</guid>
		<description><![CDATA[In parts 1 and 2 of this series (see here and here), I described many of the (otherwise under-documented) features of the PL/pgSQL FOR loop.  In this last installment, I&#8217;ll show you how to loop over things you may not have realized that you could loop over (my high-school grammar teacher is very offended at that [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=44&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In parts 1 and 2 of this series (see <a title="here" href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-1/">here</a> and <a title="here" href="http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%E2%80%93-part-2/">here</a>), I described many of the (otherwise under-documented) features of the PL/pgSQL FOR loop.  In this last installment, I&#8217;ll show you how to loop over things you may not have realized that you could loop over (my high-school grammar teacher is very offended at that last sentence, sorry).</p>
<p><span id="more-44"></span></p>
<p>In describing query-based FOR loop, the PostgreSQL <a title="documentation" href="http://developer.postgresql.org/pgdocs/postgres/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING">documentation</a> states the following:</p>
<blockquote><p>The <tt><em>query</em></tt> used in this type of <tt>FOR</tt> statement can be any SQL command that returns rows to the caller: <tt>SELECT</tt> is the most common case, but you can also use <tt>INSERT</tt>, <tt>UPDATE</tt>, or <tt>DELETE</tt> with a <tt>RETURNING</tt> clause. Some utility commands such as <tt>EXPLAIN</tt> will work too.</p></blockquote>
<p><strong>Looping over a SELECT statement</strong></p>
<p>The query-based FOR loop is most commonly used to process the result set produced by a SELECT statement, for example:<br />
<span style="font-family:Consolas, Monaco, 'Courier New', Courier, monospace;line-height:18px;font-size:12px;white-space:pre;">DECLARE</span></p>
<pre>    employee  emp%ROWTYPE;
BEGIN
    FOR employee IN SELECT * FROM emp LOOP
        RAISE NOTICE 'employee - %', employee.empno;
        RAISE NOTICE 'name     - %', employee.ename; 
        RAISE NOTICE 'salary   - %', employee.sal;
    END LOOP;
END;</pre>
<p>When you execute a query-based FOR loop over a SELECT statement, PL/pgSQL evaluates the query (SELECT * FROM emp) and then executes the loop body (the three RAISE statements) once for each row returned by the query.  Each time control passes through body of the loop, PL/pgSQL assigns a row to the iterator (the row-shaped variable named &#8216;employee&#8217;).  No big surprises here (although see <a href="http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%E2%80%93-part-2/">part 2</a> for details about how PL/pgSQL handles an iterator whose shape does not match the shape of the result set).  If the query returns 0 rows, PL/pgSQL sets each attribute in employee to NULL (PL/pgSQL does not leave employee untouched, it sets each attribute to NULL).</p>
<p><strong>Looping over an INSERT statement</strong></p>
<p>You can also loop over the result set produced by an INSERT&#8230;RETURNING statement:</p>
<pre>DECLARE
    employeeName emp.ename%TYPE;
BEGIN
    CREATE TEMP TABLE tempEmp(LIKE emp);

    FOR employeeName IN INSERT INTO tempEmp SELECT * FROM emp RETURNING ename LOOP
        RAISE NOTICE 'copied %', employeeName;
    END LOOP;
END;</pre>
<p>In this example, we execute a single INSERT statement (this one happens to copy rows from an existing table into a temporary table) and we have included a RETURNING clause to specify that we want the ename value for each row copied. Remember that a query-based FOR loop iterates through a result set.  In the case of an INSERT statement, the shape of the result set is determined by the RETURNING clause; you can specify a one or more column names, (or &#8216;*&#8217; to return all columns), or you can specify one or more expressions (such as &#8216;salary * 1.10&#8242;).  The result set will contain one row for each row added to the target table.</p>
<p>As I described in <a href="http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%E2%80%93-part-2/">part 2</a> of this series, you can specify the loop iterator (employeeName in the example above) in any of the following forms:</p>
<ul>
<li>A comma-separated list of variables</li>
<li>A row variable</li>
<li>A record variable</li>
</ul>
<p>So what happens if you write the iterator as a comma-separated list of variables but you forget to specify one or more columns in the RETURNING clause?  PL/pgSQL matches the attributes in the iterator against the attributes in the result set, from left to right; if the result set contains more attributes than you&#8217;ve specified in the iterator, the extra attributes are ignored.  If the iterator contains more attributes that the result set, the extra iterator variables are set to NULL.  If such an iterator variable is defined as NOT NULL, PL/pgSQL will throw a hissy fit.</p>
<p><strong>Looping over a DELETE statement</strong></p>
<p>Looping over a DELETE statement is similar to looping over an INSERT statement:</p>
<pre>DECLARE
    employeeName emp.ename%TYPE;
BEGIN
    FOR employeeName IN DELETE FROM emp RETURNING ename LOOP
        RAISE NOTICE 'copied %', employeeName;
    END LOOP;
END;</pre>
<p>When you loop over a DELETE statement, the result set processed by the FOR loop contains one row for each row you delete.</p>
<p><strong>Looping over an UPDATE statement</strong></p>
<p>Like INSERT and DELETE, you can convince UPDATE to return a result set:</p>
<pre>DECLARE
    employeeName emp.ename%TYPE;
    newSalary    emp.sal%TYPE; 
BEGIN
    FOR employeeName IN UPDATE emp SET sal = sal * 1.10 RETURNING ename, sal LOOP
        RAISE NOTICE '% - new salary = %', employeeName, newSalary;
    END LOOP;
END;</pre>
<p>As described in the PostgreSQL <a href="http://developer.postgresql.org/pgdocs/postgres/sql-update.html">documentation</a>, the result set produced by an UPDATE statement is based on the <span style="text-decoration:underline;">new</span> values.  As far as I know, there is no way to return the original values.</p>
<p><strong>Looping over other&#8230; stuff</strong></p>
<p>I quoted part of the PostgreSQL documentation at the start of this article &#8211; that documentation states that you can loop over an EXPLAIN statement as well as some of the utility commands.  The following example shows how to loop over an EXPLAIN statement:</p>
<pre>DECLARE
  planStep RECORD;
BEGIN
  FOR planStep IN EXPLAIN SELECT * FROM emp e, dept d WHERE d.deptno = e.deptno LOOP
    RAISE NOTICE '%', planStep."QUERY PLAN";
  END LOOP;
END;</pre>
<p>When you loop over an EXECUTE statement, the result set contains one row for each step in the plan and a single TEXT column (named &#8220;QUERY PLAN&#8221;) that contains the (properly indented) step.</p>
<p>Now what about those other mysterious utility commands mentioned in the PostgreSQL documentation?  Which commands can you loop over?</p>
<p>It turns out that you can loop over three utility commands:  EXPLAIN, SHOW, and FETCH.</p>
<p>As you probably know, the SHOW command produces a list of configuration parameters (aka GUC variables).  The result set produced by a SHOW command contains one row for each GUC variable, each composed of three TEXT columns: name, setting, and description:</p>
<pre>DECLARE
  parameter RECORD;
BEGIN
  FOR parameter IN SHOW ALL LOOP
    RAISE NOTICE '% - % - %', parameter.name, parameter.setting, parameter.description;
  END LOOP;
END;</pre>
<p>Is that useful?  Probably not &#8211; you can get the same result set by selecting from the pg_settings system view; actually, you can get much more information from pg_settings.</p>
<p>Finally, you can loop over a FETCH statement.  You probably knew that you could loop over a cursor, but you may not have known that you can loop over a FETCH statement:</p>
<pre>DECLARE
  employee   emp%ROWTYPE;
  executives CURSOR FOR SELECT * FROM emp WHERE mgr IS NULL;
BEGIN
  OPEN executives;

  FOR employee IN FETCH 20 FROM executives LOOP
    RAISE NOTICE '%', employee;
  END LOOP;
END;</pre>
<p>In this  last example, we have declared a cursor (executives) and opened that cursor.  Next, we managed to find a new and inefficient way to emulate the LIMIT clause &#8211; we execute a FETCH statement to read the first 20 rows from the cursor and then loop through each of those rows.  You can loop over all variants of the FETCH statement; you cannot loop over a MOVE statement.</p>
<p>Is that useful?  I can&#8217;t imagine a scenario where you would want to loop over a FETCH statement when you could simply loop over the cursor instead.  But hey, I&#8217;m not here to judge.</p>
<p>(Reference: QueryReturnsTuples(), UtilityReturnsTuples())</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/unfetteredblather.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/unfetteredblather.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/unfetteredblather.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/unfetteredblather.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/unfetteredblather.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/unfetteredblather.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/unfetteredblather.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/unfetteredblather.wordpress.com/44/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=44&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-part-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fc4ad43e7d7d8c35c45149126b7e33df?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">korryd</media:title>
		</media:content>
	</item>
		<item>
		<title>Anatomy of a FOR loop – part 2</title>
		<link>http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%e2%80%93-part-2/</link>
		<comments>http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%e2%80%93-part-2/#comments</comments>
		<pubDate>Sun, 22 Aug 2010 17:40:25 +0000</pubDate>
		<dc:creator>korryd</dc:creator>
				<category><![CDATA[PL/pgSQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[FOR loops]]></category>

		<guid isPermaLink="false">http://unfetteredblather.wordpress.com/?p=35</guid>
		<description><![CDATA[In the first part of this series (see here), I discussed the integer-based FOR loop and described some of the nitty-gritty details that you really should know to ensure that your code works properly in a world festering with NULL values. I promised that part 2 (this article) would describe how to loop through the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=35&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In the first part of this series (see <a title="here" href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-1/" target="_self">here</a>), I discussed the integer-based FOR loop and described some of the nitty-gritty details that you really should know to ensure that your code works properly in a world festering with NULL values.</p>
<p>I promised that part 2 (this article) would describe how to loop through the result sets produced a SELECT, INSERT, and UPDATE statements (along with a few oddities).</p>
<p>I lied.</p>
<p>Part 2 will instead provide a mini-reference (or perhaps, a supplement to the <a href="http://developer.postgresql.org/pgdocs/postgres/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING">official documentation</a>) for the query-based FOR loop, the cursor-based FOR loop, and the EXECUTE-based FOR loop.</p>
<p>Part 3 (which is already <a href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-3/">here</a>) will discuss what I had originally planned to cover in part 2 &#8211; looping through SELECT, INSERT, UPDATE, DELETE, and other utility commands.</p>
<p><span id="more-35"></span></p>
<p>As I mentioned in part 1, the FOR loop comes in a number of different flavors.  In this article, we will examine three of those flavors:  the query-based FOR loop, the cursor-based FOR loop, and the EXECUTE-based FOR loop.  In each case, PL/pgSQL executes a statement and then iterates through the results produced by that statement, executing the body of the loop once for each row.</p>
<p><strong>Query-based FOR loop</strong></p>
<p>The general form of the query-based FOR loop is:</p>
<pre>FOR <em>iterator</em> IN <em>query</em> LOOP
    <em>loopBody</em>
END LOOP [ <em>label</em> ];</pre>
<p>The query-based FOR loop executes the <em>loopBody</em> once for each row returned by the given <em>query</em> (unless the loop is terminated by an EXIT, scoped CONTINE, RETURN, or unhandled exception).</p>
<p>Unlike an <a href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-1/">integer-based FOR loop</a>, PL/pgSQL does <span style="text-decoration:underline;">not</span> declare the <em>iterator</em> for you – you must declare <em>iterator</em> before executing the FOR loop.  <em>iterator </em>can be any of the following:</p>
<ul>
<li>A comma-separated list of variables</li>
<li>A row variable</li>
<li>A record variable</li>
</ul>
<p>When PL/pgSQL executes a query-based FOR loop, it evaluates the given <em>query</em> and loops through the result set, assigning one row to the <em>iterator</em> for each iteration.</p>
<p>Now, what happens if the shape of the result set does not match the shape of the <em>iterator</em>? PL/pgSQL matches up the columns in the result set against the columns in the <em>iterator</em> proceeding from left to right (in other words, the first column in the result set gets copied into the first column in the <em>iterator</em>).  If the <em>iterator</em> contains more attributes than the <em>query</em> returns, the FOR loop sets the extra <em>iterator</em> variables to NULL.  If the <em>query</em> returns more attributes than you&#8217;ve defined in the <em>iterator</em>, the extra values (returned by the <em>query</em>) are simply ignored.  Of course, if the <em>iterator</em> is a record variable, the shape of the record variable will exactly match the shape of the row returned by the <em>query</em>.  The data type of each column in the <em>iterator</em> must be compatible with the data type of the corresponding column in the result set (&#8220;compatible&#8221; means that the types must be identical or that an implicit cast is defined that will convert the type in the result set to the type in the <em>iterator</em>).</p>
<p>PL/pgSQL will <em>pre-fetch</em> from the query to improve performance.  When the FOR loop begins, PL/pgSQL fetches up to 10 rows from the result set and then iterates through those values. After processing the first 10 rows, PL/pgSQL fetches the remainder of the result set 50 rows at a time.</p>
<p>When the loop terminates, PL/pgSQL sets the FOUND pre-defined variable to TRUE if the <em>query</em> returns at least one row; otherwise FOUND is set to FALSE.  After the loop terminates, the variables in the <em>iterator</em> contain the values in the last row of the result set.  If the <em>query</em> returns no data, the variables in the <em>iterator </em>are set to NULL. Remember that PL/pgSQL will raise an exception (22004 - null_value_not_allowed) if you try to assign NULL to a variable declared as NOT NULL.</p>
<p>Like any other loop construct, you can terminate the loop early by executing an EXIT statement, a RETURN statement, a scoped CONTINUE statement, or an unhandled exception.</p>
<p><strong>Cursor-based FOR loop</strong></p>
<p>The general form of the cursor-based FOR loop is:</p>
<pre>FOR <em>iterator</em> IN <em>cursorVar</em> [ ( <em>argument_values</em> ) ] LOOP
    <em>loopBody
</em>END LOOP [ <em>label</em> ];</pre>
<p>When PL/pgSQL executes a cursor-based FOR loop, it opens the cursor and loops through the result set, assigning one row to the <em>iterator</em> for each iteration.   When the loop terminates, PL/pgSQL closes the cursor.</p>
<p>The <em>cursorVar</em> must specify the name of a <em>bound</em> cursor variable (that is, the cursor variable must be declared for a specific query). If the query bound to the <em>cursorVar</em> is a parameterized query, you may specify the query arguments in the argument_values clause.  <em>cursorVar</em> is typically bound to a SELECT query, but it may, in fact, be bound to any command which returns a result set.  For example, you can bind <em>cursorVar </em>to an INSERT&#8230;RETURNING, DELETE&#8230;RETURNING, or UPDATE&#8230;RETURNING statement (see <a href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-3/">part 3</a> of this series).</p>
<p>You can refer to the cursor (within the loop) in an UPDATE&#8230;WHERE CURRENT OF <em>cursorVar</em> statement or a DELETE&#8230;WHERE CURRENT OF <em>cursorVar</em> statement.  Note that, unlike the query-based and EXECUTE-based FOR loops, PL/pgSQL will <span style="text-decoration:underline;">not</span> prefetch from a cursor-based FOR loop.</p>
<p>The cursor must not be open at the time the FOR loop begins (the FOR loop will automatically open the cursor on your behalf).</p>
<p>PL/pgSQL automatically declares the <em>iterator</em> for you &#8211; the <em>iterator</em> is a record variable accessible only within the <em>loopBody</em>. The data type and name of each attribute in the <em>iterator</em> is determined by the types and names in the cursor&#8217;s result set.  By default, PostgreSQL assigns the name &#8220;?column?&#8221; to each computed column in the result set; you may want to alias computed columns. If you don&#8217;t, you may end up with more than one &#8220;?column?&#8221; in the result set and you won&#8217;t be able to access any computed column other than the left-most.</p>
<p>When the loop terminates, PL/pgSQL sets the FOUND pre-defined variable to TRUE if the cursor returns at least one row; otherwise FOUND is set to FALSE.</p>
<p>You can terminate the loop early by executing an EXIT statement, a RETURN statement, a scoped CONTINUE statement, or an unhandled exception. In each case, PL/pgSQL closes the cursor.</p>
<p><strong>EXECUTE-based FOR loop</strong></p>
<p>The general form of the EXECUTE-based FOR loop is:</p>
<pre>FOR <em>iterator</em> IN EXECUTE <em>expression</em> [ USING <em>expression</em> [, ... ] ] LOOP
    <em>loopBody</em>
END LOOP [ <em>label</em> ];</pre>
<p>An execute-based FOR loop is similar to a query-based FOR loop; each loop evaluates a query and executes the <em>loopBody</em> once for each row returned by the query.  The primary difference between an execute-based FOR loop and a query-based FOR loop is that you use an execute-based FOR loop to iterate through the result set of a dynamically generated query.  In an execute-based FOR loop, you specify the query in the form of a string (or an expression of string type).  If the <em>expression </em>contains parameter markers ($1, $2, &#8230;), you must specify a USING clause that includes one value for each marker.</p>
<p>The <em>iterator </em>may be any of the following:</p>
<ul>
<li>A comma-separated list of variables</li>
<li>A row variable</li>
<li>A record variable</li>
</ul>
<p>If the <em>iterator</em> contains more attributes than the <em>query</em> returns, the FOR loop sets the extra <em>iterator</em> variables to NULL.  If the <em>query</em> returns more attributes than you&#8217;ve defined in the <em>iterator</em>, the extra values (returned by the <em>query</em>) are simply ignored.  If the <em>iterator</em> is a record variable, the shape of the record variable will exactly match the shape of the row returned by the query.</p>
<p>PL/pgSQL will <em>pre-fetch</em> from the query to improve performance.  When the FOR loop begins, PL/pgSQL fetches up to 10 rows from the result set and then iterates through those values. After processing the first 10 rows, PL/pgSQL fetches the remainder of the result set 50 rows at a time.</p>
<p>When the loop terminates, PL/pgSQL sets the FOUND pre-defined variable to TRUE if the <em>query</em> returns at least one row; otherwise FOUND is set to FALSE.  After the loop terminates, the variables in the <em>iterator</em> contain the values in the last row of the result set.  If the <em>query</em> returns no data, the variables in the <em>iterator </em>are set to NULL. Remember that PL/pgSQL will raise an exception (22004 &#8211; null_value_not_allowed) if you try to assign NULL to a variable declared as NOT NULL.</p>
<p>You can terminate the loop early by executing an EXIT statement, a RETURN statement, a scoped CONTINUE statement, or an unhandled exception.</p>
<p><strong>Next Time</strong></p>
<p>In the <a href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-3/">last part</a> of this series, I will show you how to loop over the result sets produced by SELECT, INSERT, UPDATE, DELETE, and even FETCH statements.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/unfetteredblather.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/unfetteredblather.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/unfetteredblather.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/unfetteredblather.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/unfetteredblather.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/unfetteredblather.wordpress.com/35/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/unfetteredblather.wordpress.com/35/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/unfetteredblather.wordpress.com/35/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=35&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%e2%80%93-part-2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fc4ad43e7d7d8c35c45149126b7e33df?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">korryd</media:title>
		</media:content>
	</item>
		<item>
		<title>Anatomy of a FOR loop &#8211; part 1</title>
		<link>http://unfetteredblather.wordpress.com/2010/08/01/anatomy-of-a-for-loop-part-1/</link>
		<comments>http://unfetteredblather.wordpress.com/2010/08/01/anatomy-of-a-for-loop-part-1/#comments</comments>
		<pubDate>Sun, 01 Aug 2010 23:35:55 +0000</pubDate>
		<dc:creator>korryd</dc:creator>
				<category><![CDATA[PL/pgSQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[FOR loops]]></category>

		<guid isPermaLink="false">http://unfetteredblather.wordpress.com/?p=15</guid>
		<description><![CDATA[The FOR loop is one of 10 control-of-flow statements in PL/pgSQL (the others being IF, CASE, LOOP, WHILE, EXIT, CONTINUE, BREAK, RETURN, and RAISE). Most of us are familiar with two or three of the most common ways to write a FOR loop, but few of use have explored all of the subtleties of this [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=15&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h2><span style="font-weight:normal;font-size:13px;">The FOR loop is one of 10 control-of-flow statements in PL/pgSQL (the others being IF, CASE, LOOP, WHILE, EXIT, CONTINUE, BREAK, RETURN, and RAISE). Most of us are<span style="color:#000000;"> familiar with two or three of the most common ways to write a FOR loop, but few of use have explored all of the subtleties of this humble statement.</span></span></h2>
<h3><span id="more-15"></span>FOR Loop Flavors</h3>
<p>The FOR loop comes in a variety of different flavors.  Which flavor you get is determined by the way you specify the <em>range</em>.</p>
<p>The general form of the FOR loop is:</p>
<pre>  FOR <em>iterator</em> IN <em>range</em> LOOP
    <em>loopBody
</em>  END LOOP [ <em>label</em> ];</pre>
<p>In this post, I&#8217;ll explain some of the intricacies of the integer FOR loop.  In future posts, I will cover cursor-based FOR loops, dynamic FOR loops, and query-based FOR loops.</p>
<h3><strong>Integer-based FOR Loops</strong></h3>
<p>The most familiar flavor is the integer-based FOR loop.  In an integer-based for loop, the <em>range</em> specifies two integer values and the loop executes once for each integer between those two values (inclusive):</p>
<pre>  FOR <em>iterator</em> IN [REVERSE] <em>start-value .. end-value</em> [BY <em>increment</em>] LOOP
   <em> </em><em>loopBody</em>
  END LOOP [label];</pre>
<p>Where <em>iterator</em> is the name of an integer variable and <em>start-value,</em> <em>end-value, </em>and<em> increment</em> are expressions which return an integer value (or a value which can be coerced to an integer).</p>
<p>You should understand that the integer-based FOR loop creates a spanking new <em>iterator</em> variable for you.  If you have variable of the same name in an outer scope, that variable will not be modified by the FOR loop &#8211; the loop iterator is a new variable automatically declared in an inner scope.  Within the body of the loop, you can refer to the current value of <em>iterator</em> as <em>iterator</em> or as <em>label.iterator</em> (if you have assigned a label to the FOR loop).  To refer to a variable with the same name declared in an outer scope, you must write <em>outerLabel.iterator</em>, where <em>outerLabel</em> is the label assigned to the outer scope.  Here&#8217;s an example that demonstrates how to refer to variables in different scopes:</p>
<pre style="padding-left:30px;">DO $$
&lt;&lt;outer_scope&gt;&gt; DECLARE
  month  INTEGER := 12;
BEGIN
  RAISE NOTICE 'month = %', month;

  FOR month IN 1..3 LOOP
    RAISE NOTICE 'inner month = %, outer month = %', month, outer_scope.month;
  END LOOP;

  RAISE NOTICE 'month = %', month;
END;
$$;</pre>
<p>This anonymous block would give the following result:</p>
<pre style="padding-left:30px;">NOTICE:  month = 12
NOTICE:  inner month = 1, outer month = 12
NOTICE:  inner month = 2, outer month = 12
NOTICE:  inner month = 3, outer month = 12
NOTICE:  month = 12</pre>
<p>Notice that month variable in the outer scope is unchanged by the loop (of course, you could assign a new value to outer_scope.month yourself, but the FOR loop never touches it).</p>
<p>When executing an integer FOR loop, PL/pgSQL begins by evaluating the <em>start-v</em><em>alue</em>, <em>end-v</em><em>alue</em>, and <em>increment </em>expressions (which must be of type INTEGER<em> </em>or of a type which can be implicitly coerced to INTEGER)<em>.</em> If <em><em>start-v</em><em>alue</em>, <em>end-v</em><em>alue</em>, <span style="font-style:normal;">or </span><em>increment </em></em>evaluates to NULL, PL/pgSQL raises an exception (22004 &#8211; null_value_not_allowed).  The increment expression must evaluate to an integer value greater than zero. otherwise, PL/pgSQL raises an exception (22023 &#8211; invalid_parameter_value).</p>
<p>It is important to note that PL/pgSQL keeps an internal copy of the <em>end-value</em>, <em>increment</em>, and <em>iterator</em>.  You can change any of those values in the body of the loop, but your change will not affect how many times the <em>loopBody</em> executes.</p>
<p>Once PL/pgSQL has made a copy of the <em>end-value</em> and <em>increment</em> values, it assigns <em>start-value</em> to the loop <em>iterator</em>. If <em>iterator </em>is less than or equal to <em>end-value</em>, PL/pgSQL executes the statements within the <em>loopBody</em>.  That&#8217;s an important point &#8211; the <em>loopBody</em> only executes if <em>start-value</em> &lt;= <em>end-value</em>.</p>
<p>If the flow of control reaches the END LOOP (remember, an EXIT, scoped CONTINUE, RETURN, or unhandled exception may cause an early termination), PL/pgSQL adds the <em>increment</em> value to <em>iterator</em> and again compares the <em>iterator</em> to the stored copy of <em>end-Value</em>.  If the new value of <em>iterator</em> is less than or equal  to <em>end-Value</em>, PL/pgSQL repeats the loop.</p>
<p><strong>Reverse Integer-based FOR Loops</strong></p>
<p>A plain-vanilla integer-based FOR loop counts up (by <em>increment</em>) from <em>start-value</em> to <em>end-value</em>.  If you want to count down instead (that is, you want want the iterator to decrease in value with each trip through the loop), you must specify the REVERSE keyword.  In a REVERSE for loop, PL/pgSQL subtracts the <em>increment</em> from the <em>iterator</em> before repeating the loop.  The <em>loopBody</em> executes only if <em>iterator</em> is greater than or equal to <em>end-value</em>.  Like a forward integer-based FOR loop, <em>increment</em> must evaluate to a value greater than zero.</p>
<h3>Terminating a FOR Loop</h3>
<p>PL/pgSQL executes the loop body once for each value (or set of values) found in the given <em>range</em>.  For each trip through the loop body, PL/pgSQL assigns a new value (or set of values) to the <em>iterator</em>.  The loop terminates when any of the following occurs:</p>
<ul>
<li>The range is exhausted (in other words, the loop has processed every value in the given range)</li>
<li>An exception is raised within the loop body and is not handled</li>
<li>The loop body executes an EXIT statement</li>
<li>The loop body executes a scoped CONTINUE statement (scoped meaning that the CONTINUE statement refers to a label outside of the loop body)</li>
<li>The loop body executes a RETURN statement</li>
</ul>
<p>When the loop terminates, PL/pgSQL assigns TRUE or FALSE to a pre-defined variable named FOUND to indicate whether any values were found within the given range (FOUND = TRUE means that PL/pgSQL executed the loop body at least once; FOUND = FALSE means that no values were found in the given range).</p>
<h3>Summary</h3>
<p>To summarize the rules:</p>
<ul>
<li>The <em>start-value</em>, <em>end-value</em>, and <em>increment</em> expressions must not evaluate to NULL</li>
<li>The <em>increment</em> expression (if present) must evaluate to an integer value greater than zero (even for a REVERSE loop).</li>
<li>In  a forward loop, PL/pgSQL will execute the loop body as long as the <em>iterator</em> is less than or equal to the <em>end-value</em></li>
<li>In a forward loop,  <em>start-value</em> must be less than or equal to <em>end-value</em> (assuming that you actually want to execute the <em>loopBody</em>)</li>
<li>In a reverse loop, PL/pgSQL will execute the loop body as long as the <em>iterator</em> is greater than or equal to the <em>end-value</em></li>
<li>In a reverse loop,  <em>start-value</em> must be greater than or equal to <em>end-value</em> (assuming that you actually want to execute the <em>loopBody</em>)</li>
<li>PL/pgSQL automatically creates a new variable (distinct from other variables with the same name declared in an outer scope) to hold the <em>iterator</em></li>
<li>You can change the <em>end-value</em> and <em>increment</em> within the loop, but your changes will not affect the number of trips through the loop</li>
<li>There are five different ways to terminate a loop</li>
</ul>
<h3>Next Time</h3>
<p>You probably know that you can write a FOR loop that iterates through the result set of a SELECT statement, but do you know what other kinds of statements you can loop through?  In the next installment of this series, I&#8217;ll show you how to walk through all of the rows modified by an UPDATE statement, all rows removed by a DELETE statement, plus a few oddities of questionable use.</p>
<p>Parts 2 and 3 are <a href="http://unfetteredblather.wordpress.com/2010/08/22/anatomy-of-a-for-loop-%E2%80%93-part-2/">here</a> and <a href="http://unfetteredblather.wordpress.com/anatomy-of-a-for-loop-part-3/">here</a>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/unfetteredblather.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/unfetteredblather.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/unfetteredblather.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/unfetteredblather.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/unfetteredblather.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/unfetteredblather.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/unfetteredblather.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/unfetteredblather.wordpress.com/15/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=15&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://unfetteredblather.wordpress.com/2010/08/01/anatomy-of-a-for-loop-part-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fc4ad43e7d7d8c35c45149126b7e33df?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">korryd</media:title>
		</media:content>
	</item>
		<item>
		<title>Having a tough day&#8230;</title>
		<link>http://unfetteredblather.wordpress.com/2009/01/15/having-a-tough-day/</link>
		<comments>http://unfetteredblather.wordpress.com/2009/01/15/having-a-tough-day/#comments</comments>
		<pubDate>Thu, 15 Jan 2009 14:42:37 +0000</pubDate>
		<dc:creator>korryd</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://unfetteredblather.wordpress.com/2009/01/15/having-a-tough-day/</guid>
		<description><![CDATA[<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=9&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<div class="wp-caption alignnone" style="width: 330px"><img alt="Nyahhh" src="http://images.icanhascheezburger.com/completestore/2009/1/12/128762849807143657.jpg" width="320" height="228" /><p class="wp-caption-text">Nyahhh</p></div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/unfetteredblather.wordpress.com/9/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/unfetteredblather.wordpress.com/9/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/unfetteredblather.wordpress.com/9/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/unfetteredblather.wordpress.com/9/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/unfetteredblather.wordpress.com/9/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/unfetteredblather.wordpress.com/9/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/unfetteredblather.wordpress.com/9/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/unfetteredblather.wordpress.com/9/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=unfetteredblather.wordpress.com&amp;blog=4992620&amp;post=9&amp;subd=unfetteredblather&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://unfetteredblather.wordpress.com/2009/01/15/having-a-tough-day/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/fc4ad43e7d7d8c35c45149126b7e33df?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">korryd</media:title>
		</media:content>

		<media:content url="http://images.icanhascheezburger.com/completestore/2009/1/12/128762849807143657.jpg" medium="image">
			<media:title type="html">Nyahhh</media:title>
		</media:content>
	</item>
	</channel>
</rss>
