<?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>MemeRocket &#187; SQL</title>
	<atom:link href="http://memerocket.com/category/sql/feed/" rel="self" type="application/rss+xml" />
	<link>http://memerocket.com</link>
	<description>Bill Burcham's Launch Platform</description>
	<lastBuildDate>Sun, 19 Feb 2012 03:56:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='memerocket.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/048c0b3b1b3b5279e320104a4b5d0bb0?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>MemeRocket &#187; SQL</title>
		<link>http://memerocket.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://memerocket.com/osd.xml" title="MemeRocket" />
	<atom:link rel='hub' href='http://memerocket.com/?pushpress=hub'/>
		<item>
		<title>:has_many Associations with Complex Joins (Kind of)</title>
		<link>http://memerocket.com/2007/07/06/has_many-associations-with-complex-joins-kind-of/</link>
		<comments>http://memerocket.com/2007/07/06/has_many-associations-with-complex-joins-kind-of/#comments</comments>
		<pubDate>Sat, 07 Jul 2007 01:17:03 +0000</pubDate>
		<dc:creator>Bill Burcham</dc:creator>
				<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.meme-rocket.com/2007/07/06/has_many-associations-with-complex-joins-kind-of/</guid>
		<description><![CDATA[In this episode, your fearless Jedi delves within the dark tree of associations, with_scope, inner joins, duck typing and meta-programming all in an effort to keep DRY&#8230; In Joyomi I create an Omi when I lend you a book. I &#8230; <a href="http://memerocket.com/2007/07/06/has_many-associations-with-complex-joins-kind-of/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=memerocket.com&#038;blog=5432592&#038;post=78&#038;subd=memerocket&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-full wp-image-196" title="duck-vader" src="http://memerocket.files.wordpress.com/2007/07/duck-vader.jpg?w=520" alt="duck-vader"   />In this episode, your fearless Jedi delves within the dark tree of associations, with_scope, inner joins, duck typing and meta-programming all in an effort to keep DRY&#8230;</p>
<p>In <a title="joyomi" href="http://joyomi.com">Joyomi</a> I create an Omi when I lend you a book. I can optionally share that Omi with you. If I do, then the Omi shows up in the &#8220;Omis Shared With Me&#8221; tab in your dashboard. Sharing. Mkay. Social networking 101.</p>
<p>Now the query for a Joyomi user to find his own Omi&#8217;s (one&#8217;s he&#8217;s created) is trivial. The user model simply has_many omis. No brainer. On the other hand, going in the opposite direction and finding all the Omi&#8217;s that my friends have shared with me is decidedly non-trivial. In fact it&#8217;s downright tricky. It involves a big long series of joins. This is further complicated by the fact that in order to address potential SPAMming we don&#8217;t show you Omi&#8217;s that were created by folks who are not in your contact list. Got that? Good.</p>
<p><span id="more-78"></span></p>
<p>So I set out to implement the &#8220;Omis Shared With Me&#8221; tab hoping to reuse a lot of stuff from the &#8220;Outstanding Omis&#8221; and &#8220;Archived Omis&#8221; tabs. In particular I&#8217;ve got a nice clean little pagination routine that takes an association as a parameter and then proceeds to paginate the Omis in that association. Here&#8217;s the routine:</p>
<pre class="textmate-source twilight"><span class="linenum">    1</span> <span class="source source_ruby"><span class="declaration declaration_function declaration_function_method declaration_function_method_with-arguments declaration_function_method_with-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">paginate_omis</span>(<span class="variable variable_parameter"> association, order</span>)</span>
<span class="linenum">    2</span>     <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@omis_pages</span> = <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Paginator</span>.<span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">new</span> <span class="constant constant_language constant_language_pseudo-variable constant_language_pseudo-variable_ruby">self</span>, association.count, <span class="constant constant_numeric constant_numeric_ruby">10</span>, params[<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:page</span>]
<span class="linenum">    3</span>     <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@omis</span> = association.find(
<span class="linenum">    4</span>       <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>,
<span class="linenum">    5</span>       <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:order</span> =&gt; order,
<span class="linenum">    6</span>       <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:limit</span> =&gt; <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@omis_pages</span>.items_per_page,
<span class="linenum">    7</span>       <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:offset</span> =&gt; <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@omis_pages</span>.current.offset)
<span class="linenum">    8</span>   <span class="keyword keyword_control keyword_control_ruby">end</span>
</span></pre>
<p>(the order parameter lets you send in nice little SQL ordering clauses like <a title="Put Nulls Last" href="/2006/12/02/put-nulls-last-on-mysql/">&#8216;-expected_at DESC&#8217;</a> ) You can see this routine relies on the association parameter responding to count and find. Since &#8220;outstanding&#8221; and &#8220;archived&#8221; Omis are associations:</p>
<pre class="textmate-source twilight"><span class="linenum">    1</span> <span class="source source_ruby">  has_many <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:outstanding_omis</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:class_name</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'Omi'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; <span class="string string_quoted string_quoted_double string_quoted_double_ruby">"`archived_at` is null"</span>
<span class="linenum">    2</span>   has_many <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:archived_omis</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:class_name</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'Omi'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; <span class="string string_quoted string_quoted_double string_quoted_double_ruby">"`archived_at` is not null"</span>
</span></pre>
<p>This works fine. So when it came time to implement &#8220;Omis Shared With Me&#8221; I thought great, I&#8217;ll just add another association for that. Granted this is a more complex association, but from reading about the :joins option  to <a title="find" href="http://caboo.se/doc/classes/ActiveRecord/Base.html#M006434">ActiveRecord::Base#find</a> it seemed doable. I&#8217;d just use that :joins option on my new association and everything would Just Workâ„¢. Boy was I wrong.</p>
<p>You may wonder why I would think that just because :joins was documented under find, that it would also work for has_many. Well it&#8217;s because those two methods mostly implement the same options.</p>
<h3>Options Implemented by Both find and has_many</h3>
<ul>
<li>:conditions</li>
<li>:order</li>
<li>:group</li>
<li>:limit</li>
<li>:offset</li>
<li>:include</li>
<li>:select</li>
</ul>
<p>But as it turns out there are a handful of discrepancies. Most of the discrepancies make sense. For instance, it wouldn&#8217;t make sense for find to support a :foreign_key option. But there are a few discrepancies that do not really make sense.</p>
<h3>Options <em>Implemented by find</em> but not by has_many</h3>
<ul>
<li><em>:joins</em></li>
<li>:readonly</li>
<li>:lock</li>
</ul>
<p>Hum, it seems to me that all three of these options would be useful on has_many. I groveled around the Rails source and I don&#8217;t see any particular reason why these aren&#8217;t supported by has_many. In fact it turns out that the find and has_many sources have many similarities. They just happen to have very little reuse going on.</p>
<p>It occured to me while reading the Rails source and seeing all the duplication between find and associations that there was a missing class, perhaps it should be called a &#8220;relation&#8221;. I&#8217;d like to instantiate a &#8220;relation&#8221; as a first class object. If such an object existed then both find and has_many could certainly use one. But that is the subject of a different post. I digress.</p>
<p>So finding myself in need of an association with :joins I did what any self-respecting Ruby programmer would do &#8212; I made one myself. Now before you freak out, I didn&#8217;t implement all 23-odd methods of has_many associations. I implemented only the two I needed in my pagination method (find and count). Remember, in Ruby, if it quacks like a duck that&#8217;s good enough. And I knew exactly which duck sounds this &#8220;association&#8221; would be called upon to emit.</p>
<p>So here is what the has_many would look like if has_many actually supported :joins:</p>
<pre class="textmate-source twilight"><span class="linenum">    1</span> <span class="source source_ruby source_ruby_rails"><span class="support support_function support_function_activerecord support_function_activerecord_rails">has_many</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:omis_shared_with_me</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:class_name</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'Omi'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:joins</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'inner join identities as i2 on i2.id = omis.identity_id inner join email_addresses as e1 on e1.address = i2.name inner join contacts as c1 on c1.id = e1.contact_id inner join contacts as c2 on omis.counterparty_id = c2.id inner join email_addresses as e2 on c2.id = e2.contact_id'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'identities.id = c1.owner_id and e2.address = identities.name'</span>
</span></pre>
<p>My workaround was to define a method on the user class (model). That method would return an instance of my new &#8220;association&#8221; class. Here is the method:</p>
<pre class="textmate-source twilight"><span class="linenum">    1</span> <span class="source source_ruby"><span class="declaration declaration_function declaration_function_method declaration_function_method_with-arguments declaration_function_method_with-arguments_ruby">  <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">omis_shared_with_me</span>(<span class="variable variable_parameter"> *args</span>)</span>
<span class="linenum">    2</span>     proxy = <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Object</span>.<span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">new</span>
<span class="linenum">    3</span>     instance_eval &lt;&lt;<span class="keyword keyword_control keyword_control_ruby">END</span>
<span class="linenum">    4</span> <span class="declaration declaration_class declaration_class_ruby">    <span class="keyword keyword_control keyword_control_class keyword_control_class_ruby">class</span> <span class="entity entity_name entity_name_class entity_name_class_ruby"><span class="variable variable_other variable_other_object variable_other_object_ruby">&lt;&lt;proxy</span></span></span>
<span class="linenum">    5</span> <span class="declaration declaration_function declaration_function_method declaration_function_method_with-arguments declaration_function_method_with-arguments_ruby">      <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">find</span>(<span class="variable variable_parameter"> *args</span>)</span>
<span class="linenum">    6</span>         with_scope <span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block">do
</span><span class="linenum">    7</span>           <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Omi</span>.find( <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>, *args)
<span class="linenum">    8</span>         <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="linenum">    9</span>       <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="linenum">   10</span> <span class="declaration declaration_function declaration_function_method declaration_function_method_with-arguments declaration_function_method_with-arguments_ruby">      <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">count</span>(<span class="variable variable_parameter"> *args</span>)</span>
<span class="linenum">   11</span>         with_scope <span class="keyword keyword_control keyword_control_ruby keyword_control_ruby_start-block">do
</span><span class="linenum">   12</span>           <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Omi</span>.count( *args)
<span class="linenum">   13</span>         <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="linenum">   14</span>       <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="linenum">   15</span>       <span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">private</span>
<span class="linenum">   16</span> <span class="declaration declaration_function declaration_function_method declaration_function_method_without-arguments declaration_function_method_without-arguments_ruby">      <span class="keyword keyword_control keyword_control_def keyword_control_def_ruby">def</span> <span class="entity entity_name entity_name_function entity_name_function_ruby">with_scope</span></span>
<span class="linenum">   17</span>         <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># without :select omis.* you get superfluous columns an the Omi objects aren't right (description is often NULL)</span>
<span class="linenum">   18</span>         <span class="variable variable_other variable_other_constant variable_other_constant_ruby">Omi</span>.with_scope(<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:find</span> =&gt; {<span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:select</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'omis.*'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:joins</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'inner join identities as i2 on i2.id = omis.identity_id inner join email_addresses as e1 on e1.address = i2.name inner join contacts as c1 on c1.id = e1.contact_id inner join identities as i1 on i1.id = c1.owner_id inner join contacts as c2 on omis.counterparty_id = c2.id inner join email_addresses as e2 on c2.id = e2.contact_id'</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:conditions</span> =&gt; [<span class="string string_quoted string_quoted_single string_quoted_single_ruby">'e2.address = i1.name and :identity_id = i1.id'</span>, {<span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:identity_id</span> =&gt; <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby">#{id}}]}) do</span>
<span class="linenum">   19</span>           <span class="keyword keyword_control keyword_control_pseudo-method keyword_control_pseudo-method_ruby">yield</span>
<span class="linenum">   20</span>         <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class="linenum">   21</span>       <span class="keyword keyword_control keyword_control_ruby">end</span> <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># def</span>
<span class="linenum">   22</span>     <span class="keyword keyword_control keyword_control_ruby">end</span> <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># &lt;&lt;proxy</span>
<span class="linenum">   23</span> <span class="keyword keyword_control keyword_control_ruby">END</span>
<span class="linenum">   24</span>     proxy
<span class="linenum">   25</span>   <span class="keyword keyword_control keyword_control_ruby">end</span> <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># def</span></span></pre>
<p>This code bears some explanation. (darn tootin!) The most obvious way to define the association class would be to simply define a new Ruby class at the top level or maybe within the scope of the user class. If we did that however, we wouldn&#8217;t have access, in instances of that class, to the id attribute of the user. And we need that id when the association is called upon to do its thing.</p>
<p>Now we could add an instance variable to our association class to hold the id and we could initialize that thing on construction but that wouldn&#8217;t be very educational now would it? Nope, no instance variables for us &#8212; we&#8217;re going to use a little meta-programming instead.</p>
<p>So instead of defining the new class at the top level or in the context of the user <em>class</em>, we define an object-specific class (specific to the proxy object) and we evaluate that definition in the context of the user <em>object</em>. In this way, the reference to id in the private with_scope method will reference the id attribute of the appropriate user at runtime.</p>
<p>So that&#8217;s it. We are leveraging find to create an association class that supports the :joins option in 25 lines of code. This approach could easily be extended to add some of the other methods of has_many depending on your project needs. Also, I&#8217;m sure some Ruby Rock Star could DRY it up more.</p>
<p><img title="The Thing With Two Heads" src="http://www.meme-rocket.com/wp-content/uploads/2007/07/two-heads.jpg" alt="The Thing With Two Heads" align="right" />On the other hand, doing a whole lot more to turn this <a title="Kiczales' Hematoma Metaphor" href="http://www2.parc.com/csl/groups/sda/projects/oi/workshop-94/foil/main.html">hematoma</a> into a <a title="Thing With Two Heads" href="http://www.imdb.com/title/tt0069372/">second head</a> is not a very wise use of resources. It&#8217;d be interesting to go in and steal (factor out) the best bits of find and has_many and build that general purpose &#8216;relation&#8217; class I spoke of earlier, DRY-ing up find and associations, normalizing the options between them and giving us first-class &#8216;relations&#8217; to boot. Anyone of you ActiveRecord warriors out there interested in some deep hacking?</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/memerocket.wordpress.com/78/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/memerocket.wordpress.com/78/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/memerocket.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/memerocket.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/memerocket.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/memerocket.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/memerocket.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/memerocket.wordpress.com/78/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/memerocket.wordpress.com/78/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/memerocket.wordpress.com/78/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=memerocket.com&#038;blog=5432592&#038;post=78&#038;subd=memerocket&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://memerocket.com/2007/07/06/has_many-associations-with-complex-joins-kind-of/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/a88a8da103044de18418f303bf0c1507?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">bburcham</media:title>
		</media:content>

		<media:content url="http://memerocket.files.wordpress.com/2007/07/duck-vader.jpg" medium="image">
			<media:title type="html">duck-vader</media:title>
		</media:content>

		<media:content url="http://www.meme-rocket.com/wp-content/uploads/2007/07/two-heads.jpg" medium="image">
			<media:title type="html">The Thing With Two Heads</media:title>
		</media:content>
	</item>
		<item>
		<title>Put NULLS Last on MySQL</title>
		<link>http://memerocket.com/2006/12/02/put-nulls-last-on-mysql/</link>
		<comments>http://memerocket.com/2006/12/02/put-nulls-last-on-mysql/#comments</comments>
		<pubDate>Sat, 02 Dec 2006 20:34:06 +0000</pubDate>
		<dc:creator>Bill Burcham</dc:creator>
				<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.meme-rocket.com/2006/12/02/put-nulls-last-on-mysql/</guid>
		<description><![CDATA[NULLs are a wonderful but strange feature of SQL. They are the source of some deep coolness but also some lost hair. One place where NULLs can bite you is when you are sorting. The way you sort in SQL &#8230; <a href="http://memerocket.com/2006/12/02/put-nulls-last-on-mysql/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=memerocket.com&#038;blog=5432592&#038;post=49&#038;subd=memerocket&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>NULLs are a wonderful but strange feature of SQL.  They are the source of some deep coolness but also some lost hair.  One place where NULLs can bite you is when you are sorting. The way you sort in SQL is by appending the <code>ORDER BY</code> clause to the end of a <code>SELECT</code> statement.  The problem is that the SQL standards say that for the purpose of sorting, NULL = NULL but they don&#8217;t specify how NULL values should be ordered relative to non-NULL ones.  Doh!</p>
<p>In this post I describe the solution to this problem in the context of MySQL and Ruby on Rails.<br />
<span id="more-49"></span></p>
<p>Now imagine you&#8217;ve got a Ruby on Rails app that manages <i>shipments</i> and those shipments have an attribute called <code>expected_at</code>:</p>
<pre class="textmate-source twilight"><span class='linenum'>    1</span> <span class="source source_ruby">      t.column <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:expected_at</span>, <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:datetime</span></span></pre>
<p>And then let&#8217;s say you&#8217;d like to present a list of shipments ordered by their expected time.  You&#8217;d like shipments expected soonest to appear before shipments expected later.  You might have a core snippet of code like this:</p>
<pre class="textmate-source twilight"><span class='linenum'>    1</span> <span class="source source_ruby source_ruby_rails"><span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@sorted_shipments</span> = association.find(
<span class='linenum'>    2</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>,
<span class='linenum'>    3</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:order</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'expected_at ASC'</span>,
<span class='linenum'>    4</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:limit</span> =&gt; <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@shipment_pages</span>.items_per_page,
<span class='linenum'>    5</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:offset</span> =&gt; <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@shipment_pages</span>.current.offset)
</span></pre>
<p>Now what happens if you don&#8217;t know the expected time for a shipment?  Well the natural thing to do in SQL is to leave the value NULL.  Now the problem arises.  If for instance you&#8217;re running on MySQL, shipments with NULL <code>expected_at</code> times will appear <i>before</i> those with non-NULL ones.  So what can you do?</p>
<p>Well my first thought was to have two separate associations &#8212; one for shipments that had NULL <code>expected_at</code> times and a second one for those that didn&#8217;t.  The problem with that approach (if you&#8217;re using the Ruby on Rails Paginator framework) is that it leaves you to do a bunch of bookkeeping in application code.  You really don&#8217;t want to hand code pagination over multiple associations.  Or I should say &#8212; <i>I</i> certainly don&#8217;t want to.</p>
<p>My second thought was that I was missing some key knowledge of Ruby on Rails.  When in doubt &#8212; assume that Rails has thought of it.  But a fair amount of spelunking turned up nothing.  Rails is mute on this issue.</p>
<p>So where to turn next?  The database of course!  An initial search for &#8220;sql sort null&#8221;  led me to an Oracle <a href="http://www.xquery.com/white_papers/generating_sql/sorting-data.html">reference</a> that mentioned Oracle&#8217;s <code>NULLS LAST</code> clause.  When I tried <code>NULLS LAST</code> on MySQL, naturally it failed. MySQL doesn&#8217;t support it.  A subsequent search for &#8220;mysql nulls last&#8221; yielded <a href="http://troels.arvin.dk/db/rdbms/">gold</a>.  Turns out there is an obscure syntax available in MySQL which you can use to control the relative ordering of NULL and non-NULL values:</p>
<blockquote><p>NULLs are considered lower than any non-NULL value, except if a &#8211; (minus) character is added before the column name and ASC is changed to DESC, or DESC to ASC; this minus-before-column-name feature seems undocumented.</p></blockquote>
<p>Clear enough.  Er.  Not very.  If you&#8217;re like me you need an example.  To make the previous example do what we want, here&#8217;s what the code looks like now:</p>
<pre class="textmate-source twilight"><span class='linenum'>    1</span> <span class="source source_ruby source_ruby_rails"><span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@sorted_shipments</span> = association.find(
<span class='linenum'>    2</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:all</span>,
<span class='linenum'>    3</span>   <span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"># this is the same as doing 'expected_at ASC NULLS LAST' on Oracle</span>
<span class='linenum'>    4</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:order</span> =&gt; <span class="string string_quoted string_quoted_single string_quoted_single_ruby">'-expected_at DESC'</span>,
<span class='linenum'>    5</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:limit</span> =&gt; <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@shipment_pages</span>.items_per_page,
<span class='linenum'>    6</span>   <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby">:offset</span> =&gt; <span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby">@shipment_pages</span>.current.offset)</span></pre>
<p>So that&#8217;s&#8230; stick a minus in front of the column name and invert the apparent sense of the ordering.  Now you&#8217;ll get earlier arrivals before later ones and you&#8217;ll get NULL&#8217;s (indeterminate arrivals) at the very end.  Whew.</p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/memerocket.wordpress.com/49/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/memerocket.wordpress.com/49/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/memerocket.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/memerocket.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/memerocket.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/memerocket.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/memerocket.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/memerocket.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/memerocket.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/memerocket.wordpress.com/49/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=memerocket.com&#038;blog=5432592&#038;post=49&#038;subd=memerocket&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://memerocket.com/2006/12/02/put-nulls-last-on-mysql/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/a88a8da103044de18418f303bf0c1507?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">bburcham</media:title>
		</media:content>
	</item>
	</channel>
</rss>
