<?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: Manu Garcia</title>
    <description>The latest articles on DEV Community by Manu Garcia (@manudevelopia).</description>
    <link>https://dev.to/manudevelopia</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%2F15729%2Fd2fe04aa-2452-47cb-a9aa-948c07364157.jpg</url>
      <title>DEV Community: Manu Garcia</title>
      <link>https://dev.to/manudevelopia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manudevelopia"/>
    <language>en</language>
    <item>
      <title>SQL - Tuple Comparison</title>
      <dc:creator>Manu Garcia</dc:creator>
      <pubDate>Sat, 08 Feb 2025 09:45:00 +0000</pubDate>
      <link>https://dev.to/manudevelopia/sql-tuple-comparison-2lgh</link>
      <guid>https://dev.to/manudevelopia/sql-tuple-comparison-2lgh</guid>
      <description>&lt;p&gt;Sometimes you need to compare multiple columns at once across different tables. &lt;em&gt;Tuple comparison&lt;/em&gt; allows you to group several columns and compare them as a unit in a concise and readable manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;tuple&lt;/strong&gt; is essentially a set of values. You can compare a tuple from one part of a query to a tuple from other part. This is useful when you want to ensure that multiple column values match simultaneously, rather than comparing each column individually.&lt;/p&gt;

&lt;p&gt;Tuple comparison works by wrapping the columns you want to compare in parentheses, treating them as a single entity, and then comparing that entity to another tuple.&lt;/p&gt;

&lt;h2&gt;
  
  
  When?
&lt;/h2&gt;

&lt;p&gt;Tuple comparison is especially helpful when you need to match multiple fields between a table and the result of a subquery or another table. Instead of writing separate comparisons for each column, you can do it all in one go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Selecting latest Order for each Customer
&lt;/h2&gt;

&lt;p&gt;Let’s say we have a table &lt;code&gt;orders&lt;/code&gt; that tracks customer orders, and we want to find the most recent order shipped for each customer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table: &lt;code&gt;orders&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;order_date&lt;/th&gt;
&lt;th&gt;order_status&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;101&lt;/td&gt;
&lt;td&gt;2024-01-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;2024-02-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2024-02-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2024-02-10&lt;/td&gt;
&lt;td&gt;waiting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2024-01-15&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2024-02-15&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;2024-02-15&lt;/td&gt;
&lt;td&gt;waiting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Our goal is to retrieve the most recent order for each customer where the order status is &lt;code&gt;'shipped'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Tuple Comparison
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IN&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;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shipped'&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Query in details:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inner Subquery&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The subquery finds the most recent &lt;code&gt;order_date&lt;/code&gt; for each &lt;code&gt;customer_id&lt;/code&gt; where the &lt;code&gt;order_status&lt;/code&gt; is &lt;code&gt;'shipped'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It returns a tuple for each customer containing the &lt;code&gt;customer_id&lt;/code&gt; and the &lt;code&gt;MAX(order_date)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tuple Comparison in &lt;code&gt;WHERE&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main query compares the tuple &lt;code&gt;(o.customer_id, o.order_date)&lt;/code&gt; with the tuples returned by the subquery. This means it checks both the &lt;code&gt;customer_id&lt;/code&gt; and the &lt;code&gt;order_date&lt;/code&gt; to find the most recent order for each customer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Results:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;order_date&lt;/th&gt;
&lt;th&gt;order_status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;2024-02-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2024-02-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2024-02-15&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concise&lt;/strong&gt;: Compact and readable SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient&lt;/strong&gt;: Optimized by the database engine.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;JOIN&lt;/code&gt; Instead of Tuple Comparison
&lt;/h2&gt;

&lt;p&gt;Same result can be achieved using a &lt;code&gt;JOIN&lt;/code&gt;. Instead of comparing tuples, we can join the &lt;code&gt;orders&lt;/code&gt; table with a subquery that retrieves the most recent &lt;code&gt;order_date&lt;/code&gt; for each customer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;JOIN&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;JOIN&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;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_date&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;max_order_date&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shipped'&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;recent_orders&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recent_orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recent_orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_order_date&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shipped'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Query in details:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Subquery (&lt;code&gt;recent_orders&lt;/code&gt;)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The subquery selects each &lt;code&gt;customer_id&lt;/code&gt; and the maximum &lt;code&gt;order_date&lt;/code&gt; (&lt;code&gt;MAX(order_date)&lt;/code&gt;) where the &lt;code&gt;order_status&lt;/code&gt; is &lt;code&gt;'shipped'&lt;/code&gt;. This gives us the most recent shipped order date for each customer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JOIN&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;JOIN&lt;/code&gt; matches the &lt;code&gt;customer_id&lt;/code&gt; and &lt;code&gt;order_date&lt;/code&gt; from the main &lt;code&gt;orders&lt;/code&gt; table to the &lt;code&gt;customer_id&lt;/code&gt; and &lt;code&gt;max_order_date&lt;/code&gt; from the subquery. This ensures that we only retrieve the most recent shipped order for each customer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;WHERE Clause&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main query ensures that only rows where &lt;code&gt;order_status = 'shipped'&lt;/code&gt; are returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Results:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;order_date&lt;/th&gt;
&lt;th&gt;order_status&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;101&lt;/td&gt;
&lt;td&gt;2024-01-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;2024-02-01&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2024-02-15&lt;/td&gt;
&lt;td&gt;shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why use &lt;code&gt;JOIN&lt;/code&gt;?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: &lt;code&gt;JOIN&lt;/code&gt; queries are flexible and can be adapted to more complex scenarios. If you need to return or filter additional columns from either table, a &lt;code&gt;JOIN&lt;/code&gt; can handle this more easily than a tuple comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Depending on indexing and data distribution, a &lt;code&gt;JOIN&lt;/code&gt; might be optimized better by the query planner in some cases, especially for large datasets.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Tuple comparison and &lt;code&gt;JOIN&lt;/code&gt;s are both powerful tools for comparing and retrieving data across multiple columns. Tuple comparison allows for a more compact syntax, while &lt;code&gt;JOIN&lt;/code&gt;s offer greater flexibility in more complex queries.&lt;/p&gt;

&lt;p&gt;In the example above, both approaches return the same result: the most recent shipped order for each customer.&lt;/p&gt;

&lt;p&gt;Now you can choose between Tuple comparison and &lt;code&gt;JOIN&lt;/code&gt; to compare multiple columns! Feel free to experiment with both methods and find out which works best for your scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  One more thing...
&lt;/h3&gt;

&lt;p&gt;Wanna play around? On this &lt;a href="https://sqlfiddle.com/postgresql/online-compiler?id=34d7de80-11e5-4b8a-93cf-4e7bc26c6c51" rel="noopener noreferrer"&gt;sqlfiddle&lt;/a&gt; you can find this example.&lt;/p&gt;




&lt;p&gt;Header image from &lt;a href="https://unsplash.com/photos/brown-wooden-drawer-lRoX0shwjUQ" rel="noopener noreferrer"&gt;Jan Antonin Kolar&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
