<?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: Arpit Bangre</title>
    <description>The latest articles on DEV Community by Arpit Bangre (@arpitmbangre).</description>
    <link>https://dev.to/arpitmbangre</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4098480%2Fe1af65b9-1b84-4dd0-89b0-40a4e0775032.png</url>
      <title>DEV Community: Arpit Bangre</title>
      <link>https://dev.to/arpitmbangre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arpitmbangre"/>
    <language>en</language>
    <item>
      <title>Why Using FLOAT for Financial Pipelines is a Silent $100k Trap (and How PostgreSQL NUMERIC Saves Your Ledger)</title>
      <dc:creator>Arpit Bangre</dc:creator>
      <pubDate>Tue, 08 Sep 2026 09:27:41 +0000</pubDate>
      <link>https://dev.to/arpitmbangre/why-using-float-for-financial-pipelines-is-a-silent-100k-trap-and-how-postgresql-numeric-saves-199c</link>
      <guid>https://dev.to/arpitmbangre/why-using-float-for-financial-pipelines-is-a-silent-100k-trap-and-how-postgresql-numeric-saves-199c</guid>
      <description>&lt;p&gt;Here is a simple SQL query that should return 0.3:&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FLOAT4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FLOAT4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In PostgreSQL, MySQL, and most relational SQL engines, the result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.30000001192092896
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you calculate sales tax, loan interest, or wallet balances across &lt;strong&gt;10,000,000 transactions a day&lt;/strong&gt;, those tiny fractional drifts accumulate into real cash discrepancies during month-end ledger reconciliation.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 Why Does Binary Floating-Point Drift Happen?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hardware Implementation:&lt;/strong&gt; Modern computer CPUs represent &lt;code&gt;FLOAT&lt;/code&gt; and &lt;code&gt;DOUBLE PRECISION&lt;/code&gt; using binary floating-point numbers (IEEE 754 standard).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base-2 vs. Base-10 Math:&lt;/strong&gt; In base-10, fractions like 0.1 (1/10) and 0.2 (2/10) look clean and simple. But in base-2 binary, 0.1 is an &lt;strong&gt;infinite recurring fraction&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   0.000110011001100110011... (binary)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because hardware registers have finite bits (32-bit for &lt;code&gt;FLOAT4&lt;/code&gt;, 64-bit for &lt;code&gt;FLOAT8&lt;/code&gt;), the value is truncated, introducing a tiny approximation error on every calculation.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ How PostgreSQL NUMERIC Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;Unlike &lt;code&gt;FLOAT&lt;/code&gt;, PostgreSQL's &lt;code&gt;NUMERIC&lt;/code&gt; (or &lt;code&gt;DECIMAL&lt;/code&gt;) data type does &lt;strong&gt;NOT&lt;/strong&gt; use IEEE 754 binary floating-point hardware representation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────┐
│ PostgreSQL NUMERIC Internal Memory Representation                      │
│ 1. Header (4 Bytes): Sign, weight, display scale, digit count          │
│ 2. Digits Array: Stores exact base-10000 integer chunks (0000 to 9999) │
│ ➔ 100% Exact Arbitrary-Precision Base-10 Arithmetic                    │
└────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It stores exact decimal digits in memory using &lt;strong&gt;base-10000 arithmetic&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There is &lt;strong&gt;ZERO&lt;/strong&gt; floating-point drift.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;10.50 + 20.25&lt;/code&gt; is always 100% exactly &lt;code&gt;30.75&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 The Senior Data Engineer Production Standard
&lt;/h2&gt;

&lt;p&gt;When designing production DDL schemas for transactional, warehousing, or financial pipelines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never use &lt;code&gt;FLOAT&lt;/code&gt;, &lt;code&gt;REAL&lt;/code&gt;, or &lt;code&gt;DOUBLE PRECISION&lt;/code&gt;&lt;/strong&gt; for:

&lt;ul&gt;
&lt;li&gt;Product pricing (&lt;code&gt;unit_price&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Account balances (&lt;code&gt;wallet_balance&lt;/code&gt;, &lt;code&gt;available_funds&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Tax &amp;amp; GST calculations (&lt;code&gt;tax_amount&lt;/code&gt;, &lt;code&gt;discount_rate&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always enforce &lt;code&gt;NUMERIC(precision, scale)&lt;/code&gt;&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;customer_orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;order_id&lt;/span&gt;        &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;customer_id&lt;/span&gt;     &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;order_amount&lt;/span&gt;    &lt;span class="nb"&gt;NUMERIC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&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;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="n"&gt;created_at&lt;/span&gt;      &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;precision = 12&lt;/code&gt;: Total digits allowed (supports up to ₹9,999,999,999.99 / ~999 Crore).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scale = 2&lt;/code&gt;: Exactly 2 digits reserved after the decimal point (paise/cents).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏆 Key Takeaway for System Design &amp;amp; Interviews
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;FLOAT&lt;/code&gt; / &lt;code&gt;REAL&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;NUMERIC&lt;/code&gt; / &lt;code&gt;DECIMAL&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hardware IEEE 754 Binary&lt;/td&gt;
&lt;td&gt;Software Base-10000 Digit Array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Calculation Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extremely fast (native CPU ALU)&lt;/td&gt;
&lt;td&gt;Slightly slower (software math)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precision&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approximate (drifts on fractions)&lt;/td&gt;
&lt;td&gt;100% Exact (penny-perfect)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Machine learning embeddings, GPS coordinates, physics simulations&lt;/td&gt;
&lt;td&gt;Financial ledgers, billing, invoices, banking, e-commerce&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;💡 &lt;em&gt;What is your team's strict rule for financial columns in production DDL? Drop your thoughts below!&lt;/em&gt;&lt;br&gt;&lt;br&gt;
💼 &lt;em&gt;Let's connect:&lt;/em&gt; &lt;a href="https://www.linkedin.com/in/arpitmbangre/" rel="noopener noreferrer"&gt;linkedin.com/in/arpitmbangre&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>datascience</category>
      <category>database</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>SQL DATEDIFF Internals: Why 2 Seconds Can Equal 1 Full Day (Boundary Traps)</title>
      <dc:creator>Arpit Bangre</dc:creator>
      <pubDate>Fri, 04 Sep 2026 12:00:13 +0000</pubDate>
      <link>https://dev.to/arpitmbangre/sql-datediff-internals-why-2-seconds-can-equal-1-full-day-boundary-traps-24fa</link>
      <guid>https://dev.to/arpitmbangre/sql-datediff-internals-why-2-seconds-can-equal-1-full-day-boundary-traps-24fa</guid>
      <description>&lt;p&gt;&lt;code&gt;DATEDIFF(DAY, '2026-08-31 23:59:59', '2026-09-01 00:00:01')&lt;/code&gt; returns &lt;strong&gt;1 day&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Only &lt;strong&gt;2 seconds&lt;/strong&gt; passed in real life.&lt;/p&gt;

&lt;p&gt;Yet the SQL engine says: &lt;strong&gt;"1 day difference."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why? Because this is &lt;strong&gt;NOT a bug&lt;/strong&gt;. It is 100% by architectural design.&lt;/p&gt;

&lt;p&gt;Here is the deep relational engine truth that trips up data engineers in production pipelines.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔍 The Engine Mechanics: Boundary Lines vs Elapsed Time
&lt;/h3&gt;

&lt;p&gt;Most engineers assume &lt;code&gt;DATEDIFF&lt;/code&gt; calculates elapsed chronological time.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;NOT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DATEDIFF&lt;/code&gt; counts &lt;strong&gt;how many calendar boundary lines were crossed&lt;/strong&gt; between two timestamps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;23:59:59 (Day 1)  ─────────| MIDNIGHT BOUNDARY |─────────&amp;gt;  00:00:01 (Day 2)
                             [+1 Boundary Crossed]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Between 11:59:59 PM and 12:00:01 AM, exactly &lt;strong&gt;one midnight boundary&lt;/strong&gt; was crossed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;DATEDIFF(DAY)&lt;/code&gt; = 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact same rule applies to YEARS:&lt;br&gt;
&lt;code&gt;DATEDIFF(YEAR, '2025-12-31 23:59:59', '2026-01-01 00:00:01')&lt;/code&gt; returns &lt;strong&gt;1 YEAR&lt;/strong&gt;, even though only 2 seconds passed!&lt;/p&gt;


&lt;h3&gt;
  
  
  💥 Where This Silently Corrupts Production Pipelines
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SLA Monitoring:&lt;/strong&gt; A pipeline running from 11:59 PM to 12:02 AM looks like it took a full 24-hour day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Financial Interest &amp;amp; Billing:&lt;/strong&gt; Charging a customer for a full day of rental or interest for a 5-minute transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Retention &amp;amp; Churn Analytics:&lt;/strong&gt; Falsely computing consecutive-day streaks for users active at 11:58 PM and 12:02 AM.&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  🛠️ The Senior Fix: How to Measure True Elapsed Time
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. SQL Server &amp;amp; Snowflake Fix (Second-Level Precision):
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- ❌ DANGEROUS: Counts boundaries crossed, breaks SLAs&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;DATEDIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delivery_timestamp&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;days_taken&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- ✅ THE ARCHITECT FIX: Calculate exact elapsed seconds and convert&lt;/span&gt;
&lt;span class="k"&gt;SELECT&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;order_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;delivery_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Exact fractional days elapsed based on 86,400 seconds/day&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATEDIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SECOND&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delivery_timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&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;AS&lt;/span&gt; &lt;span class="n"&gt;true_elapsed_days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Exact hours elapsed&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATEDIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SECOND&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delivery_timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&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;AS&lt;/span&gt; &lt;span class="n"&gt;true_elapsed_hours&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  2. PostgreSQL Native Interval Arithmetic:
&lt;/h4&gt;

&lt;p&gt;PostgreSQL calculates true intervals natively:&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="c1"&gt;-- 🐘 PostgreSQL returns exact elapsed interval (e.g. 00:00:02)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delivery_timestamp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;order_timestamp&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;true_elapsed_interval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EPOCH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delivery_timestamp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;order_timestamp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;true_elapsed_days&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🎯 Senior Architect Takeaway
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;DATEDIFF&lt;/code&gt; answers: &lt;em&gt;"How many boundary lines did we step over?"&lt;/em&gt;&lt;br&gt;&lt;br&gt;
It does &lt;strong&gt;NOT&lt;/strong&gt; answer: &lt;em&gt;"How much time actually ticked on the clock?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Always know your engine's date arithmetic rules before designing SLAs, churn metrics, or billing aggregations.&lt;/p&gt;




&lt;p&gt;💼 &lt;em&gt;Connect on LinkedIn:&lt;/em&gt; &lt;a href="https://www.linkedin.com/in/arpitmbangre/" rel="noopener noreferrer"&gt;linkedin.com/in/arpitmbangre&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>dataengineering</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Window Functions Demystified: ROW_NUMBER vs RANK vs DENSE_RANK (Execution Internals)</title>
      <dc:creator>Arpit Bangre</dc:creator>
      <pubDate>Tue, 01 Sep 2026 07:00:02 +0000</pubDate>
      <link>https://dev.to/arpitmbangre/window-functions-demystified-rownumber-vs-rank-vs-denserank-execution-internals-2ld4</link>
      <guid>https://dev.to/arpitmbangre/window-functions-demystified-rownumber-vs-rank-vs-denserank-execution-internals-2ld4</guid>
      <description>&lt;p&gt;In high-scale enterprise data engineering, ranking events, deduplicating records, and calculating leaderboards are everyday tasks.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;ROW_NUMBER()&lt;/code&gt;, &lt;code&gt;RANK()&lt;/code&gt;, and &lt;code&gt;DENSE_RANK()&lt;/code&gt; look similar on the surface, choosing the wrong one can corrupt your financial aggregations or deduplication pipelines.&lt;/p&gt;

&lt;p&gt;Here is the exact execution breakdown and when to use each.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔍 The 3 Ranking Functions at a Glance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Tie Handling&lt;/th&gt;
&lt;th&gt;Gaps in Sequence?&lt;/th&gt;
&lt;th&gt;Primary Production Use-Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ROW_NUMBER()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Assigns arbitrary distinct sequence (1, 2, 3, 4)&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;No Gaps&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Strict Deduplication &amp;amp; Pagination&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;RANK()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Assigns same rank to ties, skips next (1, 2, 2, 4)&lt;/td&gt;
&lt;td&gt;⚠️ &lt;strong&gt;Has Gaps&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Competition Leaderboards &amp;amp; Percentiles&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;DENSE_RANK()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Assigns same rank to ties, no skips (1, 2, 2, 3)&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;No Gaps&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Finding N-th Highest Salary / Top Tiers&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  💻 Visual SQL Execution
&lt;/h3&gt;

&lt;p&gt;Imagine an &lt;code&gt;Employees&lt;/code&gt; table with salaries: &lt;code&gt;[100k, 90k, 90k, 80k]&lt;/code&gt;.&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;EmployeeID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Salary&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="k"&gt;DESC&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;row_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RANK&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="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="k"&gt;DESC&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;rnk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DENSE_RANK&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="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="k"&gt;DESC&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;dense_rnk&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  📊 Result Set:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Salary | ROW_NUMBER | RANK | DENSE_RANK
-------+------------+------+------------
100k   |     1      |  1   |     1
90k    |     2      |  2   |     2   &amp;lt;-- Tie!
90k    |     3      |  2   |     2   &amp;lt;-- Tie!
80k    |     4      |  4   |     3   &amp;lt;-- Notice RANK jumped to 4, DENSE_RANK went to 3!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ⚠️ The #1 Production Interview Trap: "2nd Highest Salary"
&lt;/h3&gt;

&lt;p&gt;If you use &lt;code&gt;RANK()&lt;/code&gt; or &lt;code&gt;LIMIT 1 OFFSET 1&lt;/code&gt; to find the 2nd highest salary, and multiple employees tie for the 1st highest salary (e.g. two people earn 100k):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RANK() = 2&lt;/code&gt; returns &lt;strong&gt;0 rows&lt;/strong&gt;!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DENSE_RANK() = 2&lt;/code&gt; &lt;strong&gt;guarantees the correct 2nd highest salary&lt;/strong&gt; every single time.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-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;RankedSalaries&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="n"&gt;EmployeeID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DENSE_RANK&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="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="k"&gt;DESC&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;rnk&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&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;Salary&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;RankedSalaries&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rnk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🏆 Summary Rule for Data Engineers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need to delete duplicates?&lt;/strong&gt; -&amp;gt; Always use &lt;code&gt;ROW_NUMBER() = 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need Top N distinct salary tiers?&lt;/strong&gt; -&amp;gt; Always use &lt;code&gt;DENSE_RANK()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need competition rankings with point ties?&lt;/strong&gt; -&amp;gt; Always use &lt;code&gt;RANK()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;💡 &lt;em&gt;Have you ever run into a tie-ranking bug in production? Drop your thoughts below!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;💼 &lt;em&gt;Let's connect:&lt;/em&gt; &lt;a href="https://www.linkedin.com/in/arpitmbangre/" rel="noopener noreferrer"&gt;linkedin.com/in/arpitmbangre&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>dataengineering</category>
      <category>career</category>
    </item>
    <item>
      <title>Subqueries vs CTEs: Query Optimizer Internals &amp; Memory Spooling Explained</title>
      <dc:creator>Arpit Bangre</dc:creator>
      <pubDate>Sat, 29 Aug 2026 06:38:48 +0000</pubDate>
      <link>https://dev.to/arpitmbangre/subqueries-vs-ctes-query-optimizer-internals-memory-spooling-explained-4mlm</link>
      <guid>https://dev.to/arpitmbangre/subqueries-vs-ctes-query-optimizer-internals-memory-spooling-explained-4mlm</guid>
      <description>&lt;p&gt;Many engineers believe Common Table Expressions (CTEs) are always faster than subqueries. &lt;/p&gt;

&lt;p&gt;In modern SQL Server (and PostgreSQL), &lt;strong&gt;that is a myth&lt;/strong&gt;. Here is what actually happens under the hood:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Inlining &amp;amp; The Query Optimizer
&lt;/h3&gt;

&lt;p&gt;By default, the SQL optimizer treats standard CTEs and derived tables (subqueries) almost identically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The engine expands both into the same relational tree.&lt;/li&gt;
&lt;li&gt;They generate the &lt;strong&gt;exact same execution plan and I/O cost&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Pattern A: Derived Table (Subquery)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;DeptID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EmpName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Salary&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;DeptID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EmpName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;DENSE_RANK&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="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;DeptID&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;Salary&lt;/span&gt; &lt;span class="k"&gt;DESC&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;rnk&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RankedData&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rnk&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Pattern B: Common Table Expression (CTE)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;RankedData&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="n"&gt;DeptID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EmpName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;DENSE_RANK&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="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;DeptID&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;Salary&lt;/span&gt; &lt;span class="k"&gt;DESC&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;rnk&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&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;DeptID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EmpName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;RankedData&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rnk&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. When CTEs Truly Win:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Readability &amp;amp; Pipeline Stacking:&lt;/strong&gt; You can chain 5 CTEs sequentially without deeply nested pyramid brackets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Place Deduplication:&lt;/strong&gt; In SQL Server, you can run &lt;code&gt;DELETE&lt;/code&gt; directly on a CTE, and it deletes duplicate rows straight from the real underlying table!
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-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;DuplicateCleaner&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="n"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Email&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;RegistrationDate&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;rn&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DuplicateCleaner&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rn&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- ✅ Clean in-place deletion!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The Big Trap (Spooling Overhead):
&lt;/h3&gt;

&lt;p&gt;If you reference the &lt;strong&gt;same CTE multiple times&lt;/strong&gt; in a query (e.g. &lt;code&gt;CTE_A JOIN CTE_A&lt;/code&gt;), SQL Server may execute the underlying CTE query multiple times or create a Lazy Spool in &lt;code&gt;tempdb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;-&amp;gt; &lt;strong&gt;Fix:&lt;/strong&gt; For heavy multi-million row reuse, use a Temporary Table (&lt;code&gt;#TempTable&lt;/code&gt;) with an explicit Clustered Index instead!&lt;/p&gt;




&lt;p&gt;💡 &lt;em&gt;How do you choose between CTEs, Temp Tables, and Subqueries in your pipelines?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
💼 &lt;em&gt;Connect on LinkedIn:&lt;/em&gt; &lt;a href="https://www.linkedin.com/in/arpitmbangre/" rel="noopener noreferrer"&gt;linkedin.com/in/arpitmbangre&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>dataengineering</category>
      <category>performance</category>
    </item>
    <item>
      <title>SQL Execution Order Internals: Why WHERE Fails on Aliases but ORDER BY Succeeds</title>
      <dc:creator>Arpit Bangre</dc:creator>
      <pubDate>Fri, 28 Aug 2026 07:08:52 +0000</pubDate>
      <link>https://dev.to/arpitmbangre/sql-execution-order-internals-why-where-fails-on-aliases-but-order-by-succeeds-1774</link>
      <guid>https://dev.to/arpitmbangre/sql-execution-order-internals-why-where-fails-on-aliases-but-order-by-succeeds-1774</guid>
      <description>&lt;p&gt;Ever wondered why this query fails in SQL?&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;department_id&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;emp_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;emp_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;-- ❌ Error: Invalid column name 'emp_count'&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;department_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 6-Stage Execution Engine:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;FROM &amp;amp; JOIN&lt;/strong&gt; — Load source tables &amp;amp; evaluate join conditions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WHERE&lt;/strong&gt; — Filter raw rows before grouping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GROUP BY&lt;/strong&gt; — Aggregate rows into buckets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HAVING&lt;/strong&gt; — Filter aggregated buckets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SELECT&lt;/strong&gt; — Compute expressions &amp;amp; column aliases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ORDER BY&lt;/strong&gt; — Sort final output&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Why It Fails:
&lt;/h3&gt;

&lt;p&gt;Because &lt;strong&gt;WHERE&lt;/strong&gt; executes at &lt;strong&gt;Stage 2&lt;/strong&gt;, the &lt;code&gt;emp_count&lt;/code&gt; alias created at &lt;strong&gt;Stage 5 (SELECT)&lt;/strong&gt; does not exist in memory yet!&lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;ORDER BY&lt;/strong&gt; runs at &lt;strong&gt;Stage 6 (after SELECT)&lt;/strong&gt;, which is why &lt;code&gt;ORDER BY emp_count DESC&lt;/code&gt; works seamlessly.&lt;/p&gt;




&lt;h3&gt;
  
  
  How to Fix:
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;HAVING&lt;/strong&gt; for aggregated filtering:&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;department_id&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;emp_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&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;department_id&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;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;💡 &lt;em&gt;What's your favorite SQL execution order quirk? Drop your thoughts below!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;💼 &lt;em&gt;Let's connect:&lt;/em&gt; &lt;a href="https://www.linkedin.com/in/arpitmbangre/" rel="noopener noreferrer"&gt;linkedin.com/in/arpitmbangre&lt;/a&gt;&lt;/p&gt;

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