<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>article Highly selective: SQL refined beyond the WHERE in Technical Blog</title>
    <link>https://community.databricks.com/t5/technical-blog/highly-selective-sql-refined-beyond-the-where/ba-p/64459</link>
    <description>&lt;P class="lia-align-center"&gt;&lt;FONT face="comic sans ms,sans-serif"&gt;Inuktitut, the language of the Inuit, has 50 words for snow and ice.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="https://readable.com/blog/do-inuits-really-have-50-words-for-snow/" target="_self"&gt;That’s - as they say - fake news&lt;/A&gt;, but the point made is metaphorical:&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;FONT face="comic sans ms,sans-serif"&gt;When something is important to a people, their language finds a way to become efficient on that topic.&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;SQL is all about transforming and filtering data. So it should come as no surprise that SQL has sprouted a whole lot more ways to eliminate datapoints than the humble &lt;STRONG&gt;&lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt;&lt;/STRONG&gt;. In this post we broaden our palate to speak SQL a bit more … refined.&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="filterserge.png" style="width: 351px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/6864i711C5CDE2E6CFC53/image-size/medium?v=v2&amp;amp;px=400" role="button" title="filterserge.png" alt="filterserge.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H1&gt;Warm Up&lt;/H1&gt;
&lt;P data-unlink="true"&gt;&lt;STRONG&gt;Note:&lt;/STRONG&gt;&lt;SPAN&gt;&amp;nbsp;You can find all examples to run&amp;nbsp;&lt;/SPAN&gt;&lt;A href="https://github.com/srielau/notebooks/blob/main/Highly%20selective_%20SQL%20Refined%20beyond%20the%20WHERE.sql" target="_self"&gt;here&lt;/A&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;The obvious and most SQL way to filter rows from a table is the &lt;STRONG&gt;&lt;FONT face="andale mono,times"&gt;WHERE&amp;nbsp;&lt;/FONT&gt;&lt;/STRONG&gt;clause.&lt;/P&gt;
&lt;DIV&gt;
&lt;PRE&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;DROP&lt;/FONT&gt; &lt;FONT color="#008000"&gt;TABLE IF EXISTS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#008000"&gt;CREATE&lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT color="#008000"&gt; &lt;SPAN&gt;TABLE&lt;/SPAN&gt;&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;&lt;SPAN&gt;products&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;FONT color="#FF9900"&gt;&lt;SPAN&gt;name&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt; STRING&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt; &lt;FONT color="#008000"&gt;STRING,&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; &lt;/SPAN&gt;&lt;FONT color="#008000"&gt;&lt;SPAN&gt;INTEGER&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;,&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt; &lt;FONT color="#008000"&gt;INTEGER,&lt;/FONT&gt; &lt;/SPAN&gt;&lt;FONT color="#FF9900"&gt;&lt;SPAN&gt;description&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN&gt; &lt;FONT color="#008000"&gt;STRING);&lt;/FONT&gt;&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&lt;FONT color="#008000"&gt;INSERT INTO&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt; &lt;/SPAN&gt;&lt;FONT color="#008000"&gt;&lt;SPAN&gt;VALUES&lt;/SPAN&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#000000"&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'colander'&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN&gt;           , &lt;/SPAN&gt;&lt;SPAN&gt;'housewares'&lt;/SPAN&gt;&lt;SPAN&gt;         ,  &lt;/SPAN&gt;&lt;SPAN&gt;10&lt;/SPAN&gt;&lt;SPAN&gt;,  1, &lt;/SPAN&gt;&lt;SPAN&gt;'Bowl with large holes to drain water from past and vegetables'&lt;/SPAN&gt;&lt;SPAN&gt;),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'coffee filter'&lt;/SPAN&gt;&lt;SPAN&gt;      , &lt;/SPAN&gt;&lt;SPAN&gt;'house supplies'     &lt;/SPAN&gt;&lt;SPAN&gt;,   &lt;/SPAN&gt;&lt;SPAN&gt;4&lt;/SPAN&gt;&lt;SPAN&gt;, 20, &lt;/SPAN&gt;&lt;SPAN&gt;'Holds ground coffee'&lt;/SPAN&gt;&lt;SPAN&gt;                                          ),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'skimmer'&lt;/SPAN&gt;&lt;SPAN&gt;            , &lt;/SPAN&gt;&lt;SPAN&gt;'pool supplies'&lt;/SPAN&gt;&lt;SPAN&gt;      , &lt;/SPAN&gt;&lt;SPAN&gt;100&lt;/SPAN&gt;&lt;SPAN&gt;,  1, &lt;/SPAN&gt;&lt;SPAN&gt;'Drain filtering leaves from the water surface'&lt;/SPAN&gt;&lt;SPAN&gt;                ),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'diatomaceous earth'&lt;/SPAN&gt;&lt;SPAN&gt; , &lt;/SPAN&gt;&lt;SPAN&gt;'pool supplies'&lt;/SPAN&gt;&lt;SPAN&gt;      ,  &lt;/SPAN&gt;&lt;SPAN&gt;30&lt;/SPAN&gt;&lt;SPAN&gt;,  0, &lt;/SPAN&gt;&lt;SPAN&gt;'Coats pool filter to filter impurities from water'&lt;/SPAN&gt;&lt;SPAN&gt;            ),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'strainer'&lt;/SPAN&gt;&lt;SPAN&gt;           , &lt;/SPAN&gt;&lt;SPAN&gt;'house wares'&lt;/SPAN&gt;&lt;SPAN&gt;        ,  &lt;/SPAN&gt;&lt;SPAN&gt;15&lt;/SPAN&gt;&lt;SPAN&gt;,  1, &lt;/SPAN&gt;&lt;SPAN&gt;'Bowl with a handle and fine holes'&lt;/SPAN&gt;&lt;SPAN&gt;                            ),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'tea infuser'&lt;/SPAN&gt;&lt;SPAN&gt;        , &lt;/SPAN&gt;&lt;SPAN&gt;'house wares'&lt;/SPAN&gt;&lt;SPAN&gt;        ,   &lt;/SPAN&gt;&lt;SPAN&gt;5&lt;/SPAN&gt;&lt;SPAN&gt;,  1, &lt;/SPAN&gt;&lt;SPAN&gt;'Holds loose tea leaves for infusion'&lt;/SPAN&gt;&lt;SPAN&gt;                          ),&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;(&lt;/SPAN&gt;&lt;SPAN&gt;'electrostatic air filter'&lt;/SPAN&gt;&lt;SPAN&gt;, &lt;/SPAN&gt;&lt;SPAN&gt;'HVAC hardware'&lt;/SPAN&gt;&lt;SPAN&gt; ,  &lt;/SPAN&gt;&lt;SPAN&gt;10&lt;/SPAN&gt;&lt;SPAN&gt;,  1, &lt;/SPAN&gt;&lt;SPAN&gt;'Air filter for HVAC systems'&lt;/SPAN&gt;&lt;SPAN&gt;                                  &lt;FONT color="#008000"&gt;);&lt;/FONT&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; * &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt; &lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt; = 'pool supplies';&lt;BR /&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name        &lt;/U&gt; &lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;diatomaceous&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;/DIV&gt;
&lt;P&gt;But even &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; has some properties that can trip some users up. First, the &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause applies before the &lt;FONT face="andale mono,times"&gt;SELECT&lt;/FONT&gt; list items are executed. That has consequences:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause protects expressions in the &lt;FONT face="andale mono,times"&gt;SELECT&lt;/FONT&gt; list from runtime errors, such as division by zero.&lt;/LI&gt;
&lt;/UL&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;name&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; / &lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt; &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;unitprice&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#008000"&gt;  FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#008000"&gt;  WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt; = 'pool supplies' &lt;FONT color="#008000"&gt;AND&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt; != 0;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name  &lt;/U&gt;  &lt;U&gt;unitprice&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer       100&lt;/FONT&gt;&lt;/PRE&gt;
&lt;UL&gt;
&lt;LI&gt;The &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause cannot reference the results (i.e. the column aliases) of the &lt;FONT face="andale mono,times"&gt;SELECT&lt;/FONT&gt; list.&lt;BR /&gt;Where “lateral column alias” within the select is handy, it is outright wrong when applied to the &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause.&lt;/LI&gt;
&lt;/UL&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;name, price&lt;/FONT&gt; / nullif(&lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt;, 0) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;unitprice&lt;BR /&gt;&lt;/FONT&gt;&lt;FONT color="#008000"&gt;  FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;BR /&gt;&lt;/FONT&gt;&lt;FONT color="#008000"&gt;  WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;unitprice&lt;/FONT&gt; &lt;FONT color="#008000"&gt;IS NULL&lt;FONT color="#000000"&gt;;&lt;/FONT&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#000000"&gt;[UNRESOLVED_COLUMN.WITH_SUGGESTION]&lt;/FONT&gt; A column, variable, or function parameter with name `unitprice` cannot be resolved.&lt;BR /&gt;Did you mean one of the following? [`price`, `units`, `name`, `category`, `description`]. SQLSTATE: 42703&lt;/PRE&gt;
&lt;P&gt;Second, the &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause is not the first filter to be applied.The first filter to be applied is the &lt;FONT face="andale mono,times"&gt;&lt;STRONG&gt;ON&lt;/STRONG&gt;&lt;/FONT&gt; clause of any joins. In many cases this doesn’t matter ...&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;name, price&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;number&lt;/FONT&gt; &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;total&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;NATURAL JOIN VALUES&lt;/FONT&gt;('skimmer' , 1),&lt;BR /&gt;                     ('colander', 2) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;orders&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;name&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;number&lt;/FONT&gt;)&lt;BR /&gt;  &lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt; = 'housewares';&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name   &lt;/U&gt;  &lt;U&gt;total&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;colander    20&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;... until it does. Most systems, including Databricks, are rather cavalier on that matter.&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;name&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; * &lt;FONT color="#FF9900"&gt;number&lt;/FONT&gt; &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;total&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;NATURAL JOIN VALUES&lt;/FONT&gt;('skimmer' , 1),&lt;BR /&gt;                     ('colander', 2) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;orders&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;name&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;number&lt;/FONT&gt;)&lt;BR /&gt;  &lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; / &lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt; &amp;gt; 1;&lt;BR /&gt;[DIVIDE_BY_ZERO] Division by zero. Use `try_divide` to tolerate divisor being 0 and return NULL instead. If necessary set "spark.sql.ansi.enabled" to "false" to bypass this error. SQLSTATE: 22012&lt;BR /&gt;== SQL (line 5, position 7) ==&lt;BR /&gt;WHERE price / units &amp;gt; 1&lt;BR /&gt;      ^^^^^^^^^^^^^&lt;/PRE&gt;
&lt;P&gt;&lt;BR /&gt;Third, there is no promise of order of execution within the &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause itself. There isn’t even a promise that every expression within a boolean expression is in fact executed!&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;HAVING&lt;/H1&gt;
&lt;P&gt;As far as row filtering is concerned the &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause is really all SQL needs. Thanks to nesting of subqueries we can easily answer a question like:&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;FONT face="comic sans ms,sans-serif"&gt;Give me all categories with more than one product.&lt;/FONT&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; *&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM (SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;, count(*) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;num_items&lt;/FONT&gt;&lt;BR /&gt;         &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;         &lt;FONT color="#008000"&gt;GROUP&lt;/FONT&gt; &lt;FONT color="#008000"&gt;BY ALL&lt;/FONT&gt;)&lt;BR /&gt;  &lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;num_items&lt;/FONT&gt; &amp;gt; 1;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;num_items&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares            2&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies         2&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&amp;nbsp;This pattern is so frequent, that SQL sprouted its first extension in order to make this query more succinct:&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;, count(*) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;num_items&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#008000"&gt;  HAVING&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;num_items&lt;/FONT&gt; &amp;gt; 1;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;num_items&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares            2&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies         2&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;FONT face="andale mono,times"&gt;HAVING&lt;/FONT&gt; clause is nothing more than syntactic sugar for:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;pushing a subquery&lt;/LI&gt;
&lt;LI&gt;selecting all of the subquery's columns, and&lt;/LI&gt;
&lt;LI&gt;adding a new &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; clause.&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;As far as bedrock ANSI SQL 92 and row filtering is concerned: &lt;FONT face="andale mono,times"&gt;ON&lt;/FONT&gt;, &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt;, and &lt;FONT face="andale mono,times"&gt;HAVING&lt;/FONT&gt; are pretty much it. There is of course &lt;FONT face="andale mono,times"&gt;LIMIT&lt;/FONT&gt; (called&amp;nbsp;&lt;FONT face="andale mono,times"&gt;FETCH FIRST n ROWS&lt;/FONT&gt;&amp;nbsp;in ANSI)... hold that thought.&lt;/P&gt;
&lt;P&gt;A more interesting area to look at next is filtering on column values, or more precisely aggregates.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;Aggregate level filtering&lt;/H1&gt;
&lt;P&gt;By default, aggregate functions operate on all rows within a group. For example, we can count products per category.&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;, count(*) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;num_items&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt; &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;num_items&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;HVAC hardware         1&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares            4&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies         2&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;If we want to differentiate between consumables (items we need to keep buying) and non-consumables we need to add that column to the &lt;FONT face="andale mono,times"&gt;GROUP BY&lt;/FONT&gt;:&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;, count(*) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;num_items&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt; &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;num_items&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies false              1&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares    false              3&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies true               1&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;HVAC hardware false              1&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares    true               1&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;We get two rows per category: One for consumables, one for non-consumables. However, this may not be what we wanted. We may want one line per category and separate columns for consumable and non-consumable.&lt;/P&gt;
&lt;P&gt;We can pivot the table, but that’s heavy handed. An easier way is to control the aggregation itself:&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;,&lt;BR /&gt;       sum(&lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt; &lt;FONT color="#008000"&gt;THEN&lt;/FONT&gt; 1 &lt;FONT color="#008000"&gt;ELSE&lt;/FONT&gt; 0 &lt;FONT color="#008000"&gt;END)  AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;,&lt;BR /&gt;       &lt;FONT color="#008000"&gt;&lt;FONT color="#000000"&gt;sum(&lt;/FONT&gt;CASE WHEN&lt;/FONT&gt; !&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt; &lt;FONT color="#008000"&gt;THEN&lt;/FONT&gt; 1 &lt;FONT color="#008000"&gt;ELSE&lt;/FONT&gt; 0 &lt;FONT color="#008000"&gt;END) AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;non_consumable&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;non_consumable&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;HVAC hardware          0              1&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares             1              3&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies          1              1&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;There are any number of variations other than &lt;FONT face="andale mono,times"&gt;CASE&lt;/FONT&gt;.&lt;/P&gt;
&lt;P&gt;This pattern of conditional counting is so frequently used that a dedicated aggregation function is available to do this with less keystrokes:&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;,&lt;BR /&gt;       count_if(&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;)  &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;,&lt;BR /&gt;       count_if(!&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;non_consumable&lt;/FONT&gt;&lt;BR /&gt; &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt; &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;non_consumable&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;HVAC hardware          0              1&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares             1              3&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies          1              1&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;Great! But we left all those other aggregations out in the cold. What if we wanted to also aggregate the price of these items?&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;,&lt;BR /&gt;       count_if(&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;,&lt;BR /&gt;       sum(&lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt; &lt;FONT color="#008000"&gt;THEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; &lt;FONT color="#008000"&gt;ELSE&lt;/FONT&gt; 0 &lt;FONT color="#008000"&gt;END&lt;FONT color="#000000"&gt;)&lt;/FONT&gt; AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable_price&lt;/FONT&gt;,&lt;BR /&gt;       count_if(!&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;non_consumable&lt;/FONT&gt;,&lt;BR /&gt;       sum(&lt;FONT color="#008000"&gt;CASE WHEN&lt;/FONT&gt; !&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt; &lt;FONT color="#008000"&gt;THEN&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; &lt;FONT color="#008000"&gt;ELSE&lt;/FONT&gt; 0 &lt;FONT color="#008000"&gt;END&lt;FONT color="#000000"&gt;)&lt;/FONT&gt; AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;non_consumable_price&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;consumable_price&lt;/U&gt; &lt;U&gt;non_consumable&lt;/U&gt; &lt;U&gt;non_consumable_price&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;HVAC hardware          0                0              1                   10&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares             1                4              3                   30&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies          1               30              1                  100&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;The language could have spawned &lt;FONT face="andale mono,times"&gt;sum_if()&lt;/FONT&gt;,&lt;FONT face="andale mono,times"&gt; avg_if()&lt;/FONT&gt; etc… just like Databricks added&amp;nbsp;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-functions-builtin-alpha.html" target="_self"&gt;&lt;FONT face="andale mono,times"&gt;try_*&lt;/FONT&gt; functions&lt;/A&gt;.&lt;BR /&gt;Do we really want to introduce:&amp;nbsp;&lt;FONT face="andale mono,times"&gt;try_sum_if()&lt;/FONT&gt;?&lt;/P&gt;
&lt;P&gt;It seems any aggregate function could potentially need some sort of postfix that controls whether a value within the group should or should not contribute to the aggregation. There are two choices to here beyond exploding the number of functions:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Add an optional boolean parameter to each aggregate function&lt;/LI&gt;
&lt;LI&gt;Add a SQL syntax clause&lt;/LI&gt;
&lt;/OL&gt;
&lt;P&gt;Option 2 has won out and &lt;FONT face="andale mono,times"&gt;FILTER&lt;/FONT&gt; was born.&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;,&lt;BR /&gt;       count(1)   &lt;FONT color="#008000"&gt;FILTER&lt;FONT color="#000000"&gt;(&lt;/FONT&gt;WHERE &lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;,&lt;BR /&gt;       sum(&lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;FILTER&lt;FONT color="#000000"&gt;(&lt;/FONT&gt;WHERE &lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable_price&lt;/FONT&gt;,&lt;BR /&gt;       count(1)   &lt;FONT color="#008000"&gt;FILTER&lt;/FONT&gt;(&lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; !&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;noon_consumable&lt;/FONT&gt;,&lt;BR /&gt;       sum(&lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;FILTER&lt;/FONT&gt;(&lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; !&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;non_consumable_price&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;GROUP&lt;/FONT&gt; &lt;FONT color="#008000"&gt;BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;consumable_price&lt;/U&gt; &lt;U&gt;noon_consumable&lt;/U&gt; &lt;U&gt;non_consumable_price&lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;HVAC hardware          0                                1 &lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;housewares             1                4               3                    4&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;pool supplies          1               30               1                   30&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;Unlike the &lt;FONT face="andale mono,times"&gt;CASE&lt;/FONT&gt; expression pattern though, &lt;FONT face="andale mono,times"&gt;FILTER&lt;/FONT&gt; does not rely on the existence of a neutral input argument. It is truly more expressive.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Note&lt;/STRONG&gt;: It's easy to predict that eventually there will be a subquery level &lt;FONT face="andale mono,times"&gt;FILTER&lt;/FONT&gt; clause, just like the repetitive nature of the window aggregation resulted in the&amp;nbsp;&lt;FONT face="andale mono,times"&gt;WINDOW&lt;/FONT&gt; clause.&lt;/P&gt;
&lt;P&gt;Talking of window clauses, let's return to row filtering.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;No LIMITs&lt;/H1&gt;
&lt;P&gt;Earlier we glossed over &lt;FONT face="andale mono,times"&gt;LIMIT;&amp;nbsp;&lt;/FONT&gt;let’s now have a closer look.&lt;/P&gt;
&lt;P&gt;Aside from the obvious&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;FONT face="comic sans ms,sans-serif"&gt;Give me a page's worth of results only, I’m not going to look at the rest anyway!&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;use case,&amp;nbsp; there is a common pattern to retrieve to “maximum row".&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;FONT face="comic sans ms,sans-serif"&gt;What is the priciest product?&amp;nbsp; And give me that entire row!&lt;/FONT&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; *&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;ORDER BY&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; &lt;FONT color="#008000"&gt;DESC LIMIT&lt;/FONT&gt; 1;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name  &lt;/U&gt;  &lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;price&lt;/U&gt; &lt;U&gt;units&lt;/U&gt; &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;description                                  &lt;/U&gt; &lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer pool supplies   100     1 false      Drain filtering leaves from the water surface&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;Alas, things get tricky if we want to return the priciest product by category, and we want more than just the price. The classic solution is use an&lt;FONT face="andale mono,times"&gt; EXISTS&lt;/FONT&gt; and a self join:&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; p.*&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt; &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;p&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;WHERE EXISTS&lt;/FONT&gt; (&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; 1 &lt;BR /&gt;                  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt; &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;m&lt;/FONT&gt;&lt;BR /&gt;                 &lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;m.category&lt;/FONT&gt; = &lt;FONT color="#FF9900"&gt;p.category&lt;/FONT&gt;&lt;BR /&gt;                &lt;FONT color="#008000"&gt;HAVING&lt;/FONT&gt; max(&lt;FONT color="#FF9900"&gt;m.price&lt;/FONT&gt;) =&lt;FONT color="#FF9900"&gt; p.price&lt;/FONT&gt;);&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name                   &lt;/U&gt;  &lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;price&lt;/U&gt; &lt;U&gt;units&lt;/U&gt; &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;description                                  &lt;/U&gt; &lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer                  pool supplies   100     1 false      Drain filtering leaves from the water surface&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;strainer                 housewares       15     1 false      Bowl with a handle and fine holes&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;electrostatic air filter HVAC hardware    10     1 false      Air filter for HVAC systems&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;BR /&gt;Another solution, without the self join, is to use the &lt;FONT face="andale mono,times"&gt;max_by()&lt;/FONT&gt; aggregate function which returns a value based on another value's max.&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; max_by(&lt;FONT color="#FF9900"&gt;name&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;name&lt;/FONT&gt;,&lt;BR /&gt;       &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt;,&lt;BR /&gt;       max(&lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;,&lt;BR /&gt;       max_by(&lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;units&lt;/FONT&gt;,&lt;BR /&gt;       max_by(&lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;consumable&lt;/FONT&gt;,&lt;BR /&gt;       max_by(&lt;FONT color="#FF9900"&gt;description&lt;/FONT&gt;, &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt;) &lt;FONT color="#008000"&gt;AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;description&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;GROUP BY ALL&lt;/FONT&gt;;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name                    &lt;/U&gt; &lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;price&lt;/U&gt; &lt;U&gt;units&lt;/U&gt; &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;description                                  &lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;electrostatic air filter HVAC hardware    10     1 false      Air filter for HVAC systems&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;strainer                 housewares       15     1 false      Bowl with a handle and fine holes&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer                  pool supplies   100     1 false      Drain filtering leaves from the water surface&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;There is a problem though: price can hardly be assumed to be unique by category. We are walking on thin ice assuming &lt;FONT face="andale mono,times"&gt;max_by()&lt;/FONT&gt; will not deliver us a chimera of a product mixing different values from products at the same price.&lt;/P&gt;
&lt;P&gt;Yet another, safer, way is to employ window aggregation. In this approach we rank each product within its category.&lt;BR /&gt;Then we filter out only the winners per category, and remove the rank itself.&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; * &lt;FONT color="#008000"&gt;EXCEPT&lt;/FONT&gt;(&lt;FONT color="#FF9900"&gt;rank&lt;/FONT&gt;)&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; (&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; row_number() &lt;FONT color="#008000"&gt;OVER&lt;/FONT&gt; (&lt;FONT color="#008000"&gt;PARTITION BY&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt; &lt;FONT color="#008000"&gt;ORDER BY&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; &lt;FONT color="#008000"&gt;DESC) AS&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;rank&lt;/FONT&gt;,&lt;BR /&gt;               *&lt;BR /&gt;          &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;)&lt;BR /&gt;         &lt;FONT color="#008000"&gt;WHERE&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;rank&lt;/FONT&gt; = 1;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name                   &lt;/U&gt;  &lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;price&lt;/U&gt; &lt;U&gt;units&lt;/U&gt; &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;description                                  &lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;electrostatic air filter HVAC hardware    10     1 false      Air filter for HVAC systems&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;strainer                 housewares       15     1 false      Bowl with a handle and fine holes&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer                  pool supplies   100     1 false      Drain filtering leaves from the water surface&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;Thank goodness for the &lt;FONT face="andale mono,times"&gt;EXCEPT&lt;/FONT&gt; syntax, which made this manageable. But we had to push a subquery, just like we had to do at the beginning of the post when we "invented" &lt;FONT face="andale mono,times"&gt;HAVING&lt;/FONT&gt;.&lt;/P&gt;
&lt;P&gt;Again, this pattern is popular enough that SQL found a way. It's called the &lt;FONT face="andale mono,times"&gt;QUALIFY&lt;/FONT&gt; clause and it operates on expressions involving window functions only.&lt;/P&gt;
&lt;PRE&gt;&lt;FONT color="#008000"&gt;SELECT&lt;/FONT&gt; *&lt;BR /&gt;  &lt;FONT color="#008000"&gt;FROM&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;products&lt;/FONT&gt;&lt;BR /&gt;  &lt;FONT color="#008000"&gt;QUALIFY&lt;/FONT&gt; row_number() &lt;FONT color="#008000"&gt;OVER&lt;/FONT&gt;(&lt;FONT color="#008000"&gt;PARTITION BY&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;category&lt;/FONT&gt; &lt;FONT color="#008000"&gt;ORDER BY&lt;/FONT&gt; &lt;FONT color="#FF9900"&gt;price&lt;/FONT&gt; &lt;FONT color="#008000"&gt;DESC)&lt;/FONT&gt; = 1;&lt;BR /&gt;&lt;FONT color="#993366"&gt;&lt;U&gt;name                   &lt;/U&gt;  &lt;U&gt;category    &lt;/U&gt;  &lt;U&gt;price&lt;/U&gt; &lt;U&gt;units&lt;/U&gt; &lt;U&gt;consumable&lt;/U&gt; &lt;U&gt;description                                  &lt;/U&gt;&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;electrostatic air filter HVAC hardware    10     1 false      Air filter for HVAC systems&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;strainer                 housewares       15     1 false      Bowl with a handle and fine holes&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT color="#993366"&gt;skimmer                  pool supplies   100     1 false      Drain filtering leaves from the water surface&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;The trick with &lt;FONT face="andale mono,times"&gt;QUALIFY&lt;/FONT&gt; is not so much that it does allow the window function, but that it executes after the &lt;FONT face="andale mono,times"&gt;SELECT&lt;/FONT&gt; list, just like &lt;FONT face="andale mono,times"&gt;HAVING&lt;/FONT&gt;.&lt;/P&gt;
&lt;P&gt;We can make another prediction: Sooner rather than later &lt;FONT face="andale mono,times"&gt;QUALIFY&lt;/FONT&gt; will be generalized to allow any predicate operating on any column in the &lt;FONT face="andale mono,times"&gt;SELECT&lt;/FONT&gt; list.&lt;/P&gt;
&lt;P&gt;Wouldn't that be nice?&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;H1&gt;Conclusion&lt;/H1&gt;
&lt;P&gt;SQL provides a whole lot more than just &lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt; to filter data:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT face="andale mono,times"&gt;HAVING&lt;/FONT&gt; filters on groups&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face="andale mono,times"&gt;FILTER&lt;/FONT&gt; refines aggregation&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face="andale mono,times"&gt;QUALIFY&lt;/FONT&gt; sieves through selected rows.&lt;/LI&gt;
&lt;/UL&gt;
&lt;H1 id="toc-hId-1508844637"&gt;Related&lt;/H1&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-named-window.html" target="_self" rel="nofollow noreferrer"&gt;SQL Ref: WINDOW clause&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-limit.html" target="_self" rel="nofollow noreferrer"&gt;SQL Ref: LIMIT clause&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-where.html" target="_self" rel="nofollow noreferrer"&gt;SQL Ref: WHERE clause&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-having.html" target="_self" rel="nofollow noreferrer"&gt;SQL Ref: HAVING clause&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/sql-ref-window-functions.html" target="_self"&gt;SQL Ref: window functions&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="https://docs.databricks.com/en/sql/language-manual/functions/count_if.html" target="_self"&gt;SQL Ref: count_if function&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Fri, 29 Mar 2024 15:51:36 GMT</pubDate>
    <dc:creator>SergeRielau</dc:creator>
    <dc:date>2024-03-29T15:51:36Z</dc:date>
    <item>
      <title>Highly selective: SQL refined beyond the WHERE</title>
      <link>https://community.databricks.com/t5/technical-blog/highly-selective-sql-refined-beyond-the-where/ba-p/64459</link>
      <description>&lt;P&gt;It is said that the Inuktitut, the language of the Inuit has 52 words for snow and ice.&lt;BR /&gt;That’s, as they say, fake news, but the point is metaphorical:&lt;BR /&gt;When something is important to a people, their language finds a way to become efficient on that topic.&lt;BR /&gt;SQL is all about transforming and filtering data. So it should come as no surprise that SQL language has sprouted a whole lot more ways to eliminate datapoints than the humble &lt;STRONG&gt;&lt;FONT face="andale mono,times"&gt;WHERE&lt;/FONT&gt;&lt;/STRONG&gt;.&lt;BR /&gt;In this time we broaden our tongue to speak SQL a bit more … refined.&lt;/P&gt;</description>
      <pubDate>Fri, 29 Mar 2024 15:51:36 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/highly-selective-sql-refined-beyond-the-where/ba-p/64459</guid>
      <dc:creator>SergeRielau</dc:creator>
      <dc:date>2024-03-29T15:51:36Z</dc:date>
    </item>
  </channel>
</rss>

