<?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: Yuri Pontes</title>
    <description>The latest articles on DEV Community by Yuri Pontes (@yurilbrok).</description>
    <link>https://dev.to/yurilbrok</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%2F3720311%2Fdd7ae350-e16e-427f-a44c-7ea97578c297.png</url>
      <title>DEV Community: Yuri Pontes</title>
      <link>https://dev.to/yurilbrok</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yurilbrok"/>
    <language>en</language>
    <item>
      <title>Many people confuse rank() with dense_rank().</title>
      <dc:creator>Yuri Pontes</dc:creator>
      <pubDate>Mon, 26 Jan 2026 16:23:14 +0000</pubDate>
      <link>https://dev.to/yurilbrok/many-people-confuse-rank-with-denserank-1ii7</link>
      <guid>https://dev.to/yurilbrok/many-people-confuse-rank-with-denserank-1ii7</guid>
      <description>&lt;p&gt;A technique I use to study a new clause is to "invent" translations for what the clause executes or the result it delivers.&lt;/p&gt;

&lt;p&gt;I remember when I was studying this clause, the example in the image was the first analogy that came to mind, and I never forgot it.&lt;/p&gt;

&lt;p&gt;When I'm having difficulty consolidating a topic, the first thing I do is write the query or syntax on a board or paper and make mind maps, "translate" the clause and fit it into a sentence to read the query.&lt;/p&gt;

&lt;p&gt;This might be a good tip for anyone starting with SQL or any other language. It works very well for me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9nb51bxivuouxgpucej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9nb51bxivuouxgpucej.png" alt="RANK vs DENSE_RANK" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>postgres</category>
      <category>beginners</category>
    </item>
    <item>
      <title>SQL - PostgreSQL: Execution Order</title>
      <dc:creator>Yuri Pontes</dc:creator>
      <pubDate>Tue, 20 Jan 2026 20:18:46 +0000</pubDate>
      <link>https://dev.to/yurilbrok/sql-postgresql-execution-order-30pk</link>
      <guid>https://dev.to/yurilbrok/sql-postgresql-execution-order-30pk</guid>
      <description>&lt;h2&gt;
  
  
  Memorizing vs. Understanding
&lt;/h2&gt;

&lt;p&gt;During my studies transitioning to data engineering – in SQL language, from the beginning it intrigued me to know how everything worked, why it worked, why "this" has to be "here" and "that" has to be "there".&lt;/p&gt;

&lt;p&gt;So when I was studying the basics, this thought came to my mind. I believe that normally when someone is starting, the first thing the person wants is to memorize the order to write a query, because it is common to get confused, like using ORDER BY before GROUP BY, or sometimes getting psyched and writing WHERE before FROM.&lt;/p&gt;

&lt;p&gt;When we memorize that a query is written in the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&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;DISTINCT&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;HAVING&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We begin to ask: "Why this order? Is the code executed in this same order?".&lt;/p&gt;




&lt;h2&gt;
  
  
  Write order ≠ Execution order
&lt;/h2&gt;

&lt;p&gt;And it is not quite like that, the execution order is completely different from the write order, and understanding this made me more assertive when writing a query, no matter how basic it is and especially the more complex ones. I particularly start writing a query by the execution order, because it is easier to maintain the reasoning when writing a more complex query, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;HAVING&lt;/span&gt;
&lt;span class="mi"&gt;5&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;DISTINCT&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First PostgreSQL reads the table data and loads it into memory (buffer pool), then the row, then groups everything to be able to filter, then calculates and selects what was filtered, after that eliminates duplicate rows, puts the table reading in order and limits the rows that will be displayed to show the query.&lt;/p&gt;

&lt;p&gt;I learned in practice that we should always limit the amount of rows to be displayed in a query with a large database, the amount of memory used to process all this, depending on the number of records, is very large and limiting this saves you time.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this changes everything
&lt;/h2&gt;

&lt;p&gt;It happens that, the write order will be compiled by PostgreSQL and transformed into an execution plan. Next, this plan will be executed to process the data according to the query logic, and the table data will be read into the memory of the server on which the database is running (we can visualize this plan using the EXPLAIN ANALYZE command). Finally, it will start processing the data.&lt;/p&gt;

&lt;p&gt;Therefore, when it comes to execution, it will start with the FROM clause, the data will be read from the table into memory and then it will apply the conditions of the WHERE clause.&lt;/p&gt;

&lt;p&gt;If the table has 1,000 rows and PostgreSQL performs a sequential scan (no indexes), all 1,000 rows will be read into memory. After that, the data will be filtered and the intermediate results will be stored in memory again. It does not matter the amount of records: if we are querying 3 records in the WHERE clause, these 3 records will be placed in memory; if the same condition returns 100 out of 1,000 records from the table, these 100 records will be stored in memory again.&lt;/p&gt;

&lt;p&gt;However, if there are indexes on the WHERE columns, PostgreSQL may use an index scan to read only the necessary rows directly, which is much more efficient.&lt;/p&gt;

&lt;p&gt;So, all filtered data will be made available to us in memory after the WHERE clause, which will execute everything inside GROUP BY.&lt;/p&gt;

&lt;p&gt;Normally as part of GROUP BY, we specify the keys (PK or FK) on which the data should be grouped in the SELECT clause, we can also have aggregation functions in the SELECT clause in addition to executing the logic in a GROUP BY that will also execute what is in the SELECT clause.&lt;/p&gt;

&lt;p&gt;As there will often be dependencies between the two, PostgreSQL will process in an integrated way: GROUP BY prepares the groups, and SELECT calculates the aggregations (COUNT, SUM, AVG, etc) on top of these groups, from there, PostgreSQL executes the additional filters (HAVING) or goes straight to the ordering of the results (ORDER BY), depending on the query we are doing.&lt;/p&gt;

&lt;p&gt;It sorts the data again after grouping by the key to perform the aggregation, so we have ORDER BY classifying the output and returning the results according to the logic.&lt;/p&gt;

&lt;p&gt;It is good to remember that filters also have an order, the WHERE clause filters the rows before grouping, the HAVING expression filters after grouping. Understanding this gives us a processing advantage, because filtering the rows (WHERE -&amp;gt; FILTER -&amp;gt; GROUP BY) is faster than filtering a grouping (GROUP BY -&amp;gt; AGGREGATE -&amp;gt; HAVING: filters groups).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxny61a6f2lb4dv4k5w12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxny61a6f2lb4dv4k5w12.png" alt=" " width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Full Mind Map
&lt;/h2&gt;

&lt;p&gt;For an interactive and detailed view of the entire SQL execution order, check out the complete mind map:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mindmeister.com/3919214140/sql-postgresql-execution-order" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20kgjgef0x2q48lwe1y3.png" alt="Full Mind Map: SQL - PostgreSQL Execution Order" width="486" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Click on the image to open the interactive map in MindMeister&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical example
&lt;/h2&gt;

&lt;p&gt;Suppose we want to see the count of orders from a store by date, whose order status are "COMPLETE" and "CLOSED":&lt;br&gt;
&lt;/p&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;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;order_count&lt;/span&gt; &lt;span class="c1"&gt;-- Using aliases is not mandatory&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="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'COMPLETE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'CLOSED'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;-- Column (order_date)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- Column (order_count)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl984ycc0466pcy15n9o2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl984ycc0466pcy15n9o2.png" alt=" " width="800" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or orders are "COMPLETE" and "CLOSED" above a certain value:&lt;br&gt;
&lt;/p&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;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;order_count&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="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'COMPLETE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'CLOSED'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="c1"&gt;-- Filter for values above 120 (example)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To alias a column, you do not necessarily need to use the AS keyword (count). To summarize the query above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;we filter the count of orders from the orders table (FROM orders);&lt;/li&gt;
&lt;li&gt;we identify the necessary rows, which filters only the records that exactly meet the criteria defined in parentheses, before performing the count (WHERE status IN ('COMPLETE', 'CLOSED'));&lt;/li&gt;
&lt;li&gt;we group this filter according to the first column of SELECT (GROUP BY 1);&lt;/li&gt;
&lt;li&gt;we order the results according to the second column of SELECT in descending order (ORDER BY 2 DESC;); and&lt;/li&gt;
&lt;li&gt;we select the base column(s) of our query and temporarily create a new column that will show the result, with an alias (SELECT date, count(*) count).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reason I used "1" and "2" in GROUP BY and ORDER BY is to simplify for me, normally we write the column names for greater clarity. "1" represents the first column of SELECT (date) and "2" the second column (count).&lt;/p&gt;

&lt;p&gt;Understanding the concepts behind all code writing allows us to create ways to read, interpret, simplify, and if we can simplify something, it means we understand, and if we understand, we will not forget.&lt;/p&gt;




&lt;h2&gt;
  
  
  Detailed practical example (EXPLAIN ANALYZE)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4ustcep0xxp0gphwsmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4ustcep0xxp0gphwsmc.png" alt=" " width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summarizing the EXPLAIN ANALYZE command (image):
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan: PostgreSQL read the entire table (38428 rows)
Filter: Applied WHERE, removed unwanted rows
HashAggregate: Grouped by order_date
Filter (HAVING): Removed 342 groups with count &amp;gt;= 120
Total time: ~21ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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