<?xml version="1.0" encoding="UTF-8"?><!-- generator="wordpress/2.2.2" -->
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
	<title>Comments on: How to simulate optional parameters in SQL</title>
	<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/</link>
	<description>Stay curious!</description>
	<pubDate>Sat, 30 Aug 2008 04:37:41 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.2.2</generator>

	<item>
		<title>By: Frederick Lin : Optional Parameters in SQL</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-13562</link>
		<author>Frederick Lin : Optional Parameters in SQL</author>
		<pubDate>Tue, 23 Oct 2007 01:01:47 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-13562</guid>
		<description>[...] certainly be using this more often.&#160; I found an article the other day explaining how to simulate optional parameters in SQL without duplicating a single SELECT statement several times, each with a different WHERE [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] certainly be using this more often.&nbsp; I found an article the other day explaining how to simulate optional parameters in SQL without duplicating a single SELECT statement several times, each with a different WHERE [&#8230;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Xaprb</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-3569</link>
		<author>Xaprb</author>
		<pubDate>Mon, 29 Jan 2007 16:57:22 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-3569</guid>
		<description>&lt;p&gt;There can be.  If you enter SMIT% then the database server can do a prefix match, which can use indexes.  If you enter %MIT% it becomes a full-length substring search, which is not indexable.  So yes, there is always a potential for a bad query to result.&lt;/p&gt;</description>
		<content:encoded><![CDATA[<p>There can be.  If you enter SMIT% then the database server can do a prefix match, which can use indexes.  If you enter %MIT% it becomes a full-length substring search, which is not indexable.  So yes, there is always a potential for a bad query to result.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Locusta</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-3568</link>
		<author>Locusta</author>
		<pubDate>Mon, 29 Jan 2007 16:51:29 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-3568</guid>
		<description>&lt;p&gt;Hello,

Is there a performance penalty between these 2 statements:&lt;/p&gt;

&lt;pre&gt;select * from table1
where (@param1 is null or col1 = @param1)
    and (@param2 is null or col2 = @param2)&lt;/pre&gt;

&lt;pre&gt;select * from table1
where (@param1 is null or col1 &lt;strong&gt;like&lt;/strong&gt; @param1)
    and (@param2 is null or col2 &lt;strong&gt;like&lt;/strong&gt; @param2)&lt;/pre&gt;

&lt;p&gt;This would allow the user to key in e.g. SMIT% to search for a customer name starting with SMIT, but if he keys in SMITH, then only the exact name would appear in the result list.

Is this good practice, or should I avoid?

Thanks for this great article!&lt;/p&gt;</description>
		<content:encoded><![CDATA[<p>Hello,</p>
<p>Is there a performance penalty between these 2 statements:</p>
<pre>select * from table1
where (@param1 is null or col1 = @param1)
    and (@param2 is null or col2 = @param2)</pre>
<pre>select * from table1
where (@param1 is null or col1 <strong>like</strong> @param1)
    and (@param2 is null or col2 <strong>like</strong> @param2)</pre>
<p>This would allow the user to key in e.g. SMIT% to search for a customer name starting with SMIT, but if he keys in SMITH, then only the exact name would appear in the result list.</p>
<p>Is this good practice, or should I avoid?</p>
<p>Thanks for this great article!</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Xaprb</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1500</link>
		<author>Xaprb</author>
		<pubDate>Tue, 15 Aug 2006 11:13:16 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1500</guid>
		<description>&lt;p&gt;DOH!  You are right!  I'll amend the article.&lt;/p&gt;</description>
		<content:encoded><![CDATA[<p>DOH!  You are right!  I&#8217;ll amend the article.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: core</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1499</link>
		<author>core</author>
		<pubDate>Tue, 15 Aug 2006 10:49:31 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1499</guid>
		<description>&lt;p&gt;Heya,&lt;/p&gt;

&lt;p&gt;Shouldn't this:&lt;/p&gt;

&lt;pre&gt;select * from table1
where (col1 is null or col1 = @param1)
    and (col2 is null or col2 = @param2)&lt;/pre&gt;

&lt;p&gt;read:&lt;/p&gt;

&lt;pre&gt;select * from table1
where (@param1 is null or col1 = @param1)
    and (@param2 is null or col2 = @param2)&lt;/pre&gt;

&lt;p&gt;? Cheers, Will&lt;/p&gt;</description>
		<content:encoded><![CDATA[<p>Heya,</p>
<p>Shouldn&#8217;t this:</p>
<pre>select * from table1
where (col1 is null or col1 = @param1)
    and (col2 is null or col2 = @param2)</pre>
<p>read:</p>
<pre>select * from table1
where (@param1 is null or col1 = @param1)
    and (@param2 is null or col2 = @param2)</pre>
<p>? Cheers, Will</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Xaprb</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1078</link>
		<author>Xaprb</author>
		<pubDate>Tue, 11 Jul 2006 14:56:06 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1078</guid>
		<description>&lt;p&gt;That is true.  It has exactly the same logical result.  But there are two important differences which may or may not matter:&lt;/p&gt;

&lt;p&gt;One, &lt;code&gt;COALESCE()&lt;/code&gt; (or &lt;a href="http://www.xaprb.com/blog/2005/12/04/sql-server-2000-date-and-time-puzzler/"&gt;similar but not identical functions&lt;/a&gt; such as &lt;code&gt;ISNULL()&lt;/code&gt; or &lt;code&gt;IFNULL()&lt;/code&gt;, which I've seen used as well) still results in a potentially large value being compared, which might be optimized out with &lt;code&gt;IS NULL&lt;/code&gt;.  It's also a function call, which has overhead.&lt;/p&gt;

&lt;p&gt;Two, because it's a function call it won't be optimized out in the optimizer phase, if applicable.  When using an interface which sends the commands in plain text, the optimizer can completely remove parts of the query, as I mentioned above in MySQL.  If you have any inclination to see that in action, it's kind of fun to do.&lt;/p&gt;

&lt;p&gt;Theoretically, an optimizer could detect the query will be &lt;code&gt;col1 = COALESCE(NULL, col1)&lt;/code&gt; and know that can be rewritten as &lt;code&gt;col1 = col1&lt;/code&gt;, which can be optimized out altogether, but in practice, that's a heck of a lot smarter and harder-to-write optimizer than I'd expect to find.  I only have access to MySQL 5.x right now, but I expect other optimizers are no smarter than this:&lt;/p&gt;

&lt;pre&gt;mysql&#62; explain extended select col1 from tbl1 where col1 = coalesce(@param1, col1)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: col1
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 397
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql&#62; show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select sql_no_cache `test`.`tbl1`.`col1` AS `col1` from `test`.`tbl1` where (`test`.`tbl1`.`col1` = coalesce((@param1),`test`.`tbl1`.`col1`))
1 row in set (0.01 sec)

mysql&#62; explain extended select col1 from tbl1 where col1 = coalesce(null, col1)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl1
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 397
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql&#62; show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select `test`.`tbl1`.`col1` AS `col1` from `test`.`tbl1` where (`test`.`tbl1`.`col1` = coalesce(NULL,`test`.`tbl1`.`col1`))
1 row in set (0.00 sec)&lt;/pre&gt;

&lt;p&gt;In that query @param1 is undefined, and you can see it results in the same query plan as using NULL.  MySQL &lt;em&gt;does&lt;/em&gt; consider the current value of variables when generating an optimized query plan, as you can see here:&lt;/p&gt;

&lt;pre&gt;mysql&#62; explain extended select col1 from tbl1 where col1 = @param1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Impossible WHERE noticed after reading const tables&lt;/pre&gt;

&lt;p&gt;The function call is defeating the optimization.  To optimize that out, the optimizer would have to know the behavior of every function, which isn't its job.  In general, function calls can defeat a lot of optimizations.&lt;/p&gt;

&lt;p&gt;On the other hand, as I said before:&lt;/p&gt;

&lt;pre&gt;mysql&#62; explain extended select col1 from tbl1 where (@param1 is null or col1 = @param1)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl1
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 397
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

mysql&#62; show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select sql_no_cache `test`.`tbl1`.`col1` AS `col1` from `test`.`tbl1`
1 row in set (0.00 sec)&lt;/pre&gt;

&lt;p&gt;The optimizer removed the &lt;code&gt;WHERE&lt;/code&gt; clause completely.&lt;/p&gt;

&lt;p&gt;Stored procedures, and other situations where a query plan might be cached and re-used later, are always a factor to consider.  In Microsoft SQL Server, for example, the first time a stored procedure is executed its query plan is cached for re-use (mostly... this can be defeated intentionally or with bad practices), which avoids the costs associated with generating a query plan.  That can be a fairly significant optimization, as generating a query plan involves looking at index statistics, etc etc.  But it also means whatever parameter is supplied for the first invocation of the stored procedure influences the cached query plan, so the query might be well-optimized for &lt;code&gt;@param1&lt;/code&gt; but not at all for &lt;code&gt;@param2&lt;/code&gt; -- and it won't get re-evaluated for &lt;code&gt;@param2&lt;/code&gt;.&lt;/p&gt;</description>
		<content:encoded><![CDATA[<p>That is true.  It has exactly the same logical result.  But there are two important differences which may or may not matter:</p>
<p>One, <code>COALESCE()</code> (or <a href="http://www.xaprb.com/blog/2005/12/04/sql-server-2000-date-and-time-puzzler/">similar but not identical functions</a> such as <code>ISNULL()</code> or <code>IFNULL()</code>, which I&#8217;ve seen used as well) still results in a potentially large value being compared, which might be optimized out with <code>IS NULL</code>.  It&#8217;s also a function call, which has overhead.</p>
<p>Two, because it&#8217;s a function call it won&#8217;t be optimized out in the optimizer phase, if applicable.  When using an interface which sends the commands in plain text, the optimizer can completely remove parts of the query, as I mentioned above in MySQL.  If you have any inclination to see that in action, it&#8217;s kind of fun to do.</p>
<p>Theoretically, an optimizer could detect the query will be <code>col1 = COALESCE(NULL, col1)</code> and know that can be rewritten as <code>col1 = col1</code>, which can be optimized out altogether, but in practice, that&#8217;s a heck of a lot smarter and harder-to-write optimizer than I&#8217;d expect to find.  I only have access to MySQL 5.x right now, but I expect other optimizers are no smarter than this:</p>
<pre>mysql&gt; explain extended select col1 from tbl1 where col1 = coalesce(@param1, col1)G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: col1
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 397
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql&gt; show warningsG
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select sql_no_cache `test`.`tbl1`.`col1` AS `col1` from `test`.`tbl1` where (`test`.`tbl1`.`col1` = coalesce((@param1),`test`.`tbl1`.`col1`))
1 row in set (0.01 sec)

mysql&gt; explain extended select col1 from tbl1 where col1 = coalesce(null, col1)G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl1
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 397
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

mysql&gt; show warningsG
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select `test`.`tbl1`.`col1` AS `col1` from `test`.`tbl1` where (`test`.`tbl1`.`col1` = coalesce(NULL,`test`.`tbl1`.`col1`))
1 row in set (0.00 sec)</pre>
<p>In that query @param1 is undefined, and you can see it results in the same query plan as using NULL.  MySQL <em>does</em> consider the current value of variables when generating an optimized query plan, as you can see here:</p>
<pre>mysql&gt; explain extended select col1 from tbl1 where col1 = @param1G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Impossible WHERE noticed after reading const tables</pre>
<p>The function call is defeating the optimization.  To optimize that out, the optimizer would have to know the behavior of every function, which isn&#8217;t its job.  In general, function calls can defeat a lot of optimizations.</p>
<p>On the other hand, as I said before:</p>
<pre>mysql&gt; explain extended select col1 from tbl1 where (@param1 is null or col1 = @param1)G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl1
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 397
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

mysql&gt; show warningsG
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select sql_no_cache `test`.`tbl1`.`col1` AS `col1` from `test`.`tbl1`
1 row in set (0.00 sec)</pre>
<p>The optimizer removed the <code>WHERE</code> clause completely.</p>
<p>Stored procedures, and other situations where a query plan might be cached and re-used later, are always a factor to consider.  In Microsoft SQL Server, for example, the first time a stored procedure is executed its query plan is cached for re-use (mostly&#8230; this can be defeated intentionally or with bad practices), which avoids the costs associated with generating a query plan.  That can be a fairly significant optimization, as generating a query plan involves looking at index statistics, etc etc.  But it also means whatever parameter is supplied for the first invocation of the stored procedure influences the cached query plan, so the query might be well-optimized for <code>@param1</code> but not at all for <code>@param2</code> &#8212; and it won&#8217;t get re-evaluated for <code>@param2</code>.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Eric K.</title>
		<link>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1077</link>
		<author>Eric K.</author>
		<pubDate>Tue, 11 Jul 2006 14:36:45 +0000</pubDate>
		<guid>http://www.xaprb.com/blog/2005/12/11/optional-parameters-in-the-where-clause/#comment-1077</guid>
		<description>&lt;p&gt;Another way is to use &lt;code&gt;COALESCE&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;SELECT * FROM table1
WHERE col1= COALESCE(@param1,col1)
AND col2 = COALESCE(@param2,col2)&lt;/pre&gt;
   
&lt;p&gt;&lt;code&gt;COALESCE&lt;/code&gt; returns the first non-null value in the list, so if &lt;code&gt;@param1&lt;/code&gt; is supplied, &lt;code&gt;col1&lt;/code&gt; is compared to it.  If not, &lt;code&gt;col1&lt;/code&gt; is compared to *itself* which always matches.  The resulting behavior is that if a parameter is supplied, your code filters by it, otherwise it returns all rows.&lt;/p&gt;</description>
		<content:encoded><![CDATA[<p>Another way is to use <code>COALESCE</code>:</p>
<pre>SELECT * FROM table1
WHERE col1= COALESCE(@param1,col1)
AND col2 = COALESCE(@param2,col2)</pre>
<p><code>COALESCE</code> returns the first non-null value in the list, so if <code>@param1</code> is supplied, <code>col1</code> is compared to it.  If not, <code>col1</code> is compared to *itself* which always matches.  The resulting behavior is that if a parameter is supplied, your code filters by it, otherwise it returns all rows.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
