<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Hunt</title>
    <description>The latest articles on DEV Community by David Hunt (@david).</description>
    <link>https://dev.to/david</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F32365%2F49ec5a21-20dc-42c2-a86c-bc3ed577e105.jpg</url>
      <title>DEV Community: David Hunt</title>
      <link>https://dev.to/david</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/david"/>
    <language>en</language>
    <item>
      <title>Using SQL's TRIM and REPLACE to tidy up your data</title>
      <dc:creator>David Hunt</dc:creator>
      <pubDate>Fri, 06 Mar 2020 20:11:22 +0000</pubDate>
      <link>https://dev.to/david/using-sql-s-trim-and-replace-to-tidy-up-your-data-3g8m</link>
      <guid>https://dev.to/david/using-sql-s-trim-and-replace-to-tidy-up-your-data-3g8m</guid>
      <description>&lt;p&gt;Have you ever come across a pesky bug caused by leading, trailing, or otherwise unwanted spaces? Perhaps a lowercase "l" masquerading as a big "I". How long did you sit staring at the expected output and the corrupted value before you noticed the intruder? Probably too long.&lt;/p&gt;

&lt;p&gt;Don't worry, we can sulk together.&lt;/p&gt;

&lt;p&gt;The better question is, how did you fix it? You likely wrote tests and merged a fix into your ETL pipeline. But there are always straggler records in need of the almighty and powerful raw SQL query.&lt;/p&gt;

&lt;p&gt;I present to you two such cases along with viable solutions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Removing unwanted characters with TRIM()
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;The TRIM() function removes the space character OR other specified characters from the start or end of a string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This function should look familiar to experienced programmers. You will find it built-in to most languages; in JavaScript, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;  Am I in the front?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; 'Am I in the front?'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It works mostly the same way in SQL. Assume we have ingested the following data&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;product&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;market&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;45.99&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;15.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;26.50&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While it all looks above aboard, some records aren't being returned when we think they should&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;only returns&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;product&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;market&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;45.99&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;26.50&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Weird. So we try a &lt;code&gt;LIKE&lt;/code&gt; search&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%a%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We get back all four results!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To save time, let's assume you print and/or inspect the suspicious records using your favorite GUI and discover that the actual value of size for those records is &lt;code&gt;' a'&lt;/code&gt; instead of &lt;code&gt;'a'&lt;/code&gt;. Let's also assume you debugged your ETL pipeline and shipped a fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let's update the corrupted data...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt;

&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%a%'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Boom! Our corrupted data is so fresh and so clean now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Replacing unwanted parts of a string with REPLACE()
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;The REPLACE() function replaces all occurrences of a substring within a string, with a new substring.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hello, old friend.&lt;/p&gt;

&lt;p&gt;Replace is another familiar function. In JavaScript, replace looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My name is Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dave&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// =&amp;gt; 'My name is Dave'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have a few shoes for sale and for some reason, one of our suppliers likes to add decimals to whole numbers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;shoe&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;market&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;45.99&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;10.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;15.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;11.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;26.50&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This isn't good, and like the problem we &lt;code&gt;TRIM&lt;/code&gt;ed away, this makes it difficult to query our data correctly. First, we go and fix our ETL pipeline. Next, we come back to SQL for additional cleanup.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We could totally use a feature of &lt;code&gt;TRIM&lt;/code&gt; to fix our latest issue, but we're learning and using &lt;code&gt;REPLACE&lt;/code&gt; feels more familiar.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our new query goes like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt;

&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Take a look at our data, now, it cleans up so well:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;shoe&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;market&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;45.99&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;15.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;11.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;26.50&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Woo, hopefully you learned something neat about SQL. Like I said in &lt;a href="https://dev.to/david/rownumber-partition-other-reasons-sql-is-beautiful-1ji"&gt;my previous post&lt;/a&gt;, using raw SQL gives me a boost of confidence; the more I do it, the more I enjoy it.&lt;/p&gt;

&lt;p&gt;Do you have any memorable cases solved by an interesting SQL query? I'd love to hear about it below!&lt;/p&gt;

&lt;p&gt;Feel the SQL ❤️ !!&lt;/p&gt;

</description>
      <category>sql</category>
    </item>
    <item>
      <title>ROW_NUMBER, PARTITION, &amp; other reasons SQL is beautiful</title>
      <dc:creator>David Hunt</dc:creator>
      <pubDate>Wed, 04 Mar 2020 21:16:28 +0000</pubDate>
      <link>https://dev.to/david/rownumber-partition-other-reasons-sql-is-beautiful-1ji</link>
      <guid>https://dev.to/david/rownumber-partition-other-reasons-sql-is-beautiful-1ji</guid>
      <description>&lt;p&gt;The more I work with raw SQL, the more I enjoy it. Not only do I get a professional confidence boost, I also take back a bit of power over my data from whatever ORM happens to proliferate our codebase.&lt;/p&gt;

&lt;p&gt;For example, we needed the lowest-priced offer per product for each marketplace.&lt;/p&gt;

&lt;p&gt;Attempt #1&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;

&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That returns the best offer for each product in each marketplace. How? &lt;code&gt;r&lt;/code&gt; refers to the "row number" of each record within its own partition i.e. grouped by product and market. Our &lt;code&gt;WHERE&lt;/code&gt; clause allows us to only return the top offer. Results look something like:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;r&lt;/th&gt;
&lt;th&gt;product&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;market&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;45.99&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;15.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;26.50&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Great. Next, we wanted to consider the user's size preferences.&lt;/p&gt;

&lt;p&gt;The first solution was adding a &lt;code&gt;WHERE&lt;/code&gt; clause to only include sizes that are in the user's preferences. But that's no good because it leaves us without any offer at all for the product when the user's size is unavailable. Instead, we can use a few cool features of SQL - &lt;code&gt;WITH&lt;/code&gt;, arbitrarily selecting a raw value &amp;amp; conditional statements.&lt;/p&gt;

&lt;p&gt;Let's create a "virtual property" in our offers selection. Let's also move it outside of our main query using &lt;code&gt;WITH&lt;/code&gt; for readability. Then we'll sort by both our "virtual attribute" and price:&lt;/p&gt;

&lt;p&gt;Attempt #2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;offers_a&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;size_pref&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;size_pref&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;offers_a&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;

&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;offers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, for each product, the query will return the lowest-priced offer in the user's preferred size or the lowest-priced offer in any size. Results now look something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;r&lt;/th&gt;
&lt;th&gt;product&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;market&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;th&gt;size_pref&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;49.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;45.99&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;c&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;15.00&lt;/td&gt;
&lt;td&gt;walmart&lt;/td&gt;
&lt;td&gt;d&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;26.50&lt;/td&gt;
&lt;td&gt;amazon&lt;/td&gt;
&lt;td&gt;e&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first product's offers swapped positions because &lt;code&gt;size_pref&lt;/code&gt; takes precedence in our sorting.&lt;/p&gt;




&lt;p&gt;We are able to feed other API endpoints with only minor alterations to our query. Primarily, we can remove the &lt;code&gt;market&lt;/code&gt; partition wherever we load a product and its offers. That allows us to always display the most attractive, relevant price using a single query.&lt;/p&gt;

&lt;p&gt;Feel the SQL ❤️ !!&lt;/p&gt;

</description>
      <category>sql</category>
      <category>api</category>
      <category>orm</category>
    </item>
  </channel>
</rss>
