<?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: Judy</title>
    <description>The latest articles on DEV Community by Judy (@esproc_spl).</description>
    <link>https://dev.to/esproc_spl</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%2F1191782%2Fdac3272f-c56a-41d9-914e-8f8fba86506b.jpg</url>
      <title>DEV Community: Judy</title>
      <link>https://dev.to/esproc_spl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/esproc_spl"/>
    <language>en</language>
    <item>
      <title>SQLazy: Identify Whether Differences Within Groups Come from Brand or Type Problem Description</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Fri, 07 Aug 2026 06:53:47 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazy-identify-whether-differences-within-groups-come-from-brand-or-type-problem-description-4hj8</link>
      <guid>https://dev.to/esproc_spl/sqlazy-identify-whether-differences-within-groups-come-from-brand-or-type-problem-description-4hj8</guid>
      <description>&lt;p&gt;The ID field of table tbl represents car categories, with each category further divided into Brand and Type. The task is to group by ID and determine the source of differences within each group: if a group has multiple brands, Difference is "Brand"; if a group has multiple types, Difference is "Type". The same ID may produce multiple records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Data&lt;/strong&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffebn2unanq74907oxfhm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffebn2unanq74907oxfhm.png" alt="Source Data" width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Result&lt;/strong&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhcaoex82jphxvpg2vqq3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhcaoex82jphxvpg2vqq3.png" alt="Expected Result" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ID=1 has both Honda and Jeep as brands, as well as Coupe and SUV as types, so Difference produces both Brand and Type.&lt;/p&gt;

&lt;p&gt;ID=2 only has Ford as a brand, but has Sedan and Crossover as types, so only Type is produced.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQLazy Step-by-Step Implementation
&lt;/h2&gt;

&lt;p&gt;Core idea: First use summarize to group by ID and count distinct brands (cntBrand) and types (cntType), then use expand to cross-join the results with the dimensions ("Brand" and "Type"), and finally use filter to keep only the rows that satisfy the conditions.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ta95giji6lnivvs2f1l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ta95giji6lnivvs2f1l.png" alt="SQLazy Step-by-Step Implementation" width="799" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://www.sqlazy.com/?4fJ" rel="noopener noreferrer"&gt;Click to run this example online&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;The steps are explained below.&lt;/p&gt;

&lt;p&gt;Step 1: Group by ID and count distinct brands and types&lt;/p&gt;

&lt;p&gt;summarize Brand icount as cntBrand, Type icount as cntType; group ID&lt;/p&gt;

&lt;p&gt;Use summarize to group by ID. The icount function counts distinct Brand and Type values within each group, recorded as cntBrand and cntType respectively. This step compresses each row of detail data into one row per ID, containing distinct counts.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb308otc8muripixxrxpt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb308otc8muripixxrxpt.png" alt="containing distinct" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Expand the dimension list into rows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;expand ["Brand","Type"] as Difference&lt;/p&gt;

&lt;p&gt;The expand function unfolds the constant list ["Brand","Type"] into rows, cross-joining with each upstream row to generate the new column Difference. Each ID gets two rows: Difference="Brand" and Difference="Type", while retaining cntBrand and cntType fields.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmss3fq4uwjqe09recxsc.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmss3fq4uwjqe09recxsc.png" alt="cntType fields" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Filter dimensions that meet the conditions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;filter (if ((Difference = "Brand") then cntBrand&amp;gt;1; (Difference = "Type") then cntType&amp;gt;1))&lt;/p&gt;

&lt;p&gt;Use filter with conditional branching syntax: for rows where Difference="Brand", check cntBrand&amp;gt;1; for rows where Difference="Type", check cntType&amp;gt;1. Only rows with counts greater than 1 are kept. A single filter statement expresses different conditions for different branches, much more intuitive than SQL's nested CASE WHEN.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F92oq0a3jgx8lezmv2sgx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F92oq0a3jgx8lezmv2sgx.png" alt="CASE WHEN" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Select the required columns from the result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;derive ID, Difference&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ddijkk7wm093rqb61h5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ddijkk7wm093rqb61h5.png" alt="derive ID, Difference" width="799" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generated SQL&lt;/strong&gt;&lt;br&gt;
After confirming the above steps, the SQLazy compiler automatically generates native SQL (PostgreSQL syntax):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH Value1 AS (
  SELECT
    ID,
    COUNT(DISTINCT Brand) AS cntBrand,
    COUNT(DISTINCT Type) AS cntType
  FROM (
    SELECT ID, Brand, Type FROM tbl
  ) tbl
  GROUP BY ID
),
Value2 AS (
  SELECT
    Value1.ID, Value1.cntBrand, Value1.cntType, Difference
  FROM Value1
  CROSS JOIN (
    SELECT 'Brand' AS Difference
    UNION ALL
    SELECT 'Type'
  ) T_1
)
SELECT ID, Difference FROM Value2
WHERE CASE
  WHEN (Difference = 'Brand') THEN cntBrand &amp;gt; 1
  WHEN (Difference = 'Type') THEN cntType &amp;gt; 1
  ELSE NULL
END
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLazy lets you describe logic in business language instead of writing nested SQL queries. The solution process is divided into 4 steps, each can independently verify intermediate results, reducing the probability of errors in complex logic. The icount function of summarize can directly count distinct values, replacing SQL's COUNT(DISTINCT ...). The expand function expands a constant list into rows, completing in one line what requires CROSS JOIN with UNION ALL in SQL. The filter function's conditional branching syntax (if ... then ...; ... then ...) makes multi-condition filtering logic clear at a glance, clearer than SQL's nested CASE WHEN.&lt;/p&gt;

&lt;p&gt;Official Links&lt;br&gt;
SQLazy Online Experience: &lt;a href="https://sqlazy.com/" rel="noopener noreferrer"&gt;sqlazy.com&lt;/a&gt; (free, no registration required)&lt;br&gt;
SQLazy Repository: &lt;a href="https://github.com/SPLWare/SQLazy" rel="noopener noreferrer"&gt;github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SQLazy：Merge Multiple Tables into Single Rows by Common ID</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:57:22 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazymerge-multiple-tables-into-single-rows-by-common-id-266e</link>
      <guid>https://dev.to/esproc_spl/sqlazymerge-multiple-tables-into-single-rows-by-common-id-266e</guid>
      <description>&lt;h2&gt;
  
  
  Problem Description
&lt;/h2&gt;

&lt;p&gt;Merge multiple structurally similar tables with different column names into a wide table using full outer joins by common ID. Four tables have similar structures, each with two fields. The fields have the same meaning but different names (id, id2, id3, id4 all represent ID). The goal is to merge the four tables into single rows by ID, with each ID appearing in exactly one row. When an ID is absent in a table, the corresponding columns take NULL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Data&lt;/strong&gt;&lt;br&gt;
T1 table:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmh2axsllnq7hhvg6h8qp.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmh2axsllnq7hhvg6h8qp.png" alt="T1 table" width="800" height="81"&gt;&lt;/a&gt;&lt;br&gt;
T2 table:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnxjhc565diiqvnwgxjkn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnxjhc565diiqvnwgxjkn.png" alt="T2 table" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;T3 table:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmp74wj3ny9b9qa2iwfn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmp74wj3ny9b9qa2iwfn.png" alt="T3 table" width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;T4 table:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fykzv7mipgqik6ztkb01k.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fykzv7mipgqik6ztkb01k.png" alt="T4 table" width="800" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Expected Result&lt;br&gt;
*&lt;/em&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1zqgcq3dl9wfkifoekjm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1zqgcq3dl9wfkifoekjm.png" alt="Expected Result" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, ID=555 appears in both T1 and T2 but not in T3 or T4, so id, colA, id2, colB have values, while id3/colC/id4/colD are NULL.&lt;/p&gt;

&lt;p&gt;ID=222 only appears in T3, so only id3 and colC have values; all other columns are NULL.&lt;/p&gt;

&lt;p&gt;ID=10 appears in T2 and T4 but not in T1 or T3, so id2, colB, id4, colD have values; all other columns are NULL.&lt;/p&gt;
&lt;h2&gt;
  
  
  SQLazy Step-by-Step Implementation
&lt;/h2&gt;

&lt;p&gt;Core Idea: First use derive to unify the ID column names of each table to ID_main, making subsequent merging easier. Then start from the first table and perform full outer joins one by one: use join to full outer join the current result with the next table on ID_main, then use derive and nvl to merge the new ID into the ID_main column, appending tables one by one to get the final result.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8go35s8hu0ehkyerkcof.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8go35s8hu0ehkyerkcof.png" alt="final result" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://www.sqlazy.com/?3q4" rel="noopener noreferrer"&gt;Click to run this example online&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;The steps are explained below.&lt;/p&gt;

&lt;p&gt;Steps 1-4: Unify ID Column Names Across Tables&lt;/p&gt;

&lt;p&gt;derive id as ID_main, id, colA&lt;/p&gt;

&lt;p&gt;Use derive on T1-T4 to rename their respective ID column names (id/id2/id3/id4) uniformly to ID_main.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9lt77aj26cq5u6kw5gg6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9lt77aj26cq5u6kw5gg6.png" alt="T1-T4" width="800" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 5: Full Outer Join T1 and T2&lt;/p&gt;

&lt;p&gt;join ID_main; with t2; ID_main; take id2, colB; full&lt;/p&gt;

&lt;p&gt;Use the join function to full outer join t1 and t2 on ID_main.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa9d2sbe43rph3th30n0y.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa9d2sbe43rph3th30n0y.png" alt=" T1 and T2" width="798" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 6: Merge NULLs in ID Column&lt;/p&gt;

&lt;p&gt;derive nvl(ID_main, id2) as ID_main, id, colA, id2, colB&lt;/p&gt;

&lt;p&gt;If a record comes from t2 but is not present in t1, its ID_main is NULL. This step assigns t2.id2 to ID_main in such records, ensuring the ID_main column always has a value. Different SQL implementations use different syntax for this step - for example, Oracle uses nvl and SQL Server uses COALESCE - while NSPL uniformly uses the nvl function, which is automatically translated into the corresponding dialect during compilation based on the database type.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsrfbd36rpt2b96j5t5ru.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsrfbd36rpt2b96j5t5ru.png" alt="Step 6" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Steps 7-8: Append T3&lt;/p&gt;

&lt;p&gt;join ID_main; with t3; ID_main; take id3, colC; full&lt;/p&gt;

&lt;p&gt;derive nvl(ID_main, id3) as ID_main, id, colA, id2, colB, id3, colC&lt;/p&gt;

&lt;p&gt;Repeat the join + derive pattern to full outer join T3 into the current result. After appending each table, use ifn to merge the new ID into the unified ID_main column.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffd6iu7qew3eq63xma5v6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffd6iu7qew3eq63xma5v6.png" alt="Append T3" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Steps 9-10: Append T4 to Get Final Result&lt;/p&gt;

&lt;p&gt;join ID_main; with t4; ID_main; take id4, colD; full&lt;/p&gt;

&lt;p&gt;derive nvl(ID_main, id4) as ID_main, id, colA, id2, colB, id3, colC, id4, colD&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw3t2tpe3s7oxvvtgkt44.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw3t2tpe3s7oxvvtgkt44.png" alt="Steps 9-10" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generated SQL&lt;/strong&gt;&lt;br&gt;
After confirming the above steps, the SQLazy compiler automatically generates native SQL (SQL Server syntax):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH t1 AS (
        SELECT id AS ID_main, id, colA
        FROM T1
    ),
    t2 AS (
        SELECT id2 AS ID_main, id2, colB
        FROM T2
    ),
    r1 AS (
        SELECT t1.ID_main, t1.id, t1.colA, t2.id2, t2.colB
        FROM t1
            FULL JOIN t2 ON ID_main = t2.ID_main
    ),
    r1b AS (
        SELECT COALESCE(NULLIF(CAST(ID_main AS VARCHAR(10)), ''), NULLIF(CAST(id2 AS VARCHAR(10)), '')) AS ID_main
            , id, colA, id2, colB
        FROM r1
    ),
    t3 AS (
        SELECT id3 AS ID_main, id3, colC
        FROM T3
    ),
    r2 AS (
        SELECT r1b.ID_main, r1b.id, r1b.colA, r1b.id2, r1b.colB
            , t3.id3, t3.colC
        FROM r1b
            FULL JOIN t3 ON ID_main = t3.ID_main
    ),
    r2b AS (
        SELECT COALESCE(NULLIF(CAST(ID_main AS VARCHAR(10)), ''), NULLIF(CAST(id3 AS VARCHAR(10)), '')) AS ID_main
            , id, colA, id2, colB, id3
            , colC
        FROM r2
    ),
    t4 AS (
        SELECT id4 AS ID_main, id4, colD
        FROM T4
    ),
    r3 AS (
        SELECT r2b.ID_main, r2b.id, r2b.colA, r2b.id2, r2b.colB
            , r2b.id3, r2b.colC, t4.id4, t4.colD
        FROM r2b
            FULL JOIN t4 ON ID_main = t4.ID_main
    )
SELECT COALESCE(NULLIF(CAST(ID_main AS VARCHAR(10)), ''), NULLIF(CAST(id4 AS VARCHAR(10)), '')) AS ID_main
    , id, colA, id2, colB, id3
    , colC, id4, colD
FROM r3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLazy lets you describe logic in business language instead of writing nested SQL queries. SQLazy's step-by-step computation breaks multi-table merging into independent steps such as unifying column names, performing full outer joins one table at a time, and merging IDs - each step can be independently verified for intermediate results. The join function, combined with derive and nvl, flexibly handles post-merge operations such as renaming columns and merging NULL values. This strategy of appending tables one by one is much clearer and more maintainable than writing deeply nested FULL JOIN + COALESCE queries in SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official Links&lt;/strong&gt;&lt;br&gt;
SQLazy Online Experience: &lt;a href="https://sqlazy.com/" rel="noopener noreferrer"&gt;sqlazy.com&lt;/a&gt; (free, no registration required)&lt;br&gt;
SQLazy Repository: &lt;a href="https://github.com/SPLWare/SQLazy" rel="noopener noreferrer"&gt;github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>development</category>
      <category>programmers</category>
      <category>sqlazy</category>
    </item>
    <item>
      <title>SQLazy：Forward Fill NULL Values</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Fri, 31 Jul 2026 06:58:11 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazyforward-fill-null-values-1b0b</link>
      <guid>https://dev.to/esproc_spl/sqlazyforward-fill-null-values-1b0b</guid>
      <description>&lt;h2&gt;
  
  
  Problem Description
&lt;/h2&gt;

&lt;p&gt;A table records employee information with three fields: id (sort key), name, and dept (department). The dept column contains NULL values that need to be forward filled—each NULL should be replaced with the most recent non-NULL value in the same column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Data&lt;/strong&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftamqzsxs9i4e14rg476g.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftamqzsxs9i4e14rg476g.png" alt="Source Data" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Result&lt;/strong&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnc5ypiykuvdrhr33cr4h.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnc5ypiykuvdrhr33cr4h.png" alt="Expected Result" width="800" height="304"&gt;&lt;/a&gt;&lt;br&gt;
For example, id=4–5 (dept=NULL): take the dept value Sales from id=3.&lt;/p&gt;
&lt;h2&gt;
  
  
  SQLazy Step-by-Step Implementation
&lt;/h2&gt;

&lt;p&gt;Core idea: First create a logical grouping marker (grp) that increments each time dept is NOT NULL, grouping consecutive NULL rows with their preceding non-NULL row into the same partition. Then use grp as the partition key, taking the max dept value within each partition to forward fill the NULLs. This two-step strategy—create grouping marker first, then aggregate by partition—is SQLazy’s classic pattern for forward-fill problems.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa0kvo2zhe3uj8bsfkyl.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa0kvo2zhe3uj8bsfkyl.png" alt="Step-by-Step" width="799" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://www.sqlazy.com/?39E" rel="noopener noreferrer"&gt;Click to run this example online&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;The steps are explained below.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Step 1: Sort by id in ascending order&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
sort id asc&lt;/p&gt;

&lt;p&gt;Sort by id in ascending order to ensure records are processed in sequence; this is the prerequisite for subsequent grouping and filling.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1d1p6aydjqd6qv7zm8e.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy1d1p6aydjqd6qv7zm8e.png" alt="sort id asc" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create a logical grouping marker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;compute if ((dept notnull) then 1 else 0) cum as grp&lt;/p&gt;

&lt;p&gt;This is the most critical step. Use the computed column with the cum (running total) argument to cumulatively sum the condition if ((dept notnull) then 1 else 0). When dept is NOT NULL, it contributes 1 (starting a new group); when NULL, it contributes 0 (continuing the current group). The cumulative result grp increments by 1 each time dept is non-NULL, dividing the data into partitions.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4r2rcn2wtp3fswfd2wiv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4r2rcn2wtp3fswfd2wiv.png" alt="Create a logical" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Forward fill by partition&lt;/strong&gt;&lt;br&gt;
compute dept max as filled_dept; partition grp&lt;br&gt;
Within the grp partition, use the max aggregation to get the dept value. Each partition has only the first row’s dept as non-NULL, the rest are NULL. The MAX function automatically picks the non-NULL value, achieving forward fill.&lt;br&gt;
The partition grp ensures that fills in different partitions do not interfere with each other. This replaces NULL values with the preceding non-NULL value in id order.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcvrfrxy271v6mvkyztq6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcvrfrxy271v6mvkyztq6.png" alt="Forward fill by partition" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Use derive to select the output fields&lt;br&gt;
**&lt;br&gt;
**Generated SQL&lt;/strong&gt;&lt;br&gt;
After confirming the logic of the above 4 steps, the SQLazy compiler automatically generates native SQL (MySQL syntax used here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH t2 AS (
        SELECT id, name, dept
            , SUM(CASE
                WHEN dept IS NOT NULL THEN 1
                ELSE 0
            END) OVER (ORDER BY CASE
                WHEN id IS NULL THEN 1
                ELSE 0
            END, id ASC ROWS UNBOUNDED PRECEDING) AS grp
        FROM forwardFill
    )
    t3 AS (
        SELECT id, name, dept, grp
            , MAX(dept) OVER (PARTITION BY grp) AS filled_dept
        FROM t2
    )
SELECT id, name, filled_dept AS dept
FROM t3
ORDER BY CASE
    WHEN id IS NULL THEN 1
    ELSE 0
END, id ASC
;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLazy lets you describe logic in business language instead of writing nested SQL queries. Step-by-step calculation breaks forward fill into independent steps, each verifiable independently. The cum (conditional running total) automatically generates group numbers—this is cleaner than SQL’s window function approach for forward-fill problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Official Links
&lt;/h2&gt;

&lt;p&gt;SQLazy Online Experience: &lt;a href="https://sqlazy.com/" rel="noopener noreferrer"&gt;sqlazy.com&lt;/a&gt; (free, no registration required)&lt;br&gt;
SQLazy Repository: &lt;a href="https://sqlazy.com/" rel="noopener noreferrer"&gt;github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Code-as-Documentation: An SQL Development Paradigm for the AI Era</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Wed, 29 Jul 2026 06:11:47 +0000</pubDate>
      <link>https://dev.to/esproc_spl/code-as-documentation-an-sql-development-paradigm-for-the-ai-era-ff1</link>
      <guid>https://dev.to/esproc_spl/code-as-documentation-an-sql-development-paradigm-for-the-ai-era-ff1</guid>
      <description>&lt;h2&gt;
  
  
  A “runnable but unmaintainable” SQL query
&lt;/h2&gt;

&lt;p&gt;Let’s first look at the following SQL used in a production environment. The query aims to “merge all overlapping time intervals within each account”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT account_id, MIN(start_date) AS start_date, MAX(end_date) AS end_date
FROM (
  SELECT account_id, start_date, end_date, prev_max,
    SUM(CASE WHEN start_date &amp;gt; prev_max OR prev_max IS NULL THEN 1 ELSE 0 END) 
      OVER (PARTITION BY account_id ORDER BY start_date) AS gid
  FROM (
    SELECT account_id, start_date, end_date,
      MAX(end_date) OVER (
        PARTITION BY account_id 
        ORDER BY CASE WHEN account_id IS NOT NULL THEN 1 END, start_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
      ) AS prev_max
    FROM acc
  ) t1
) t2
GROUP BY account_id, gid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This SQL snippet uses window functions MAX()OVER and SUM() OVER for their cumulative-sum technique, along with ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING – syntax most developers would probably need to look up in the documentation. The syntax is completely correct, and the query runs and produces the expected output.&lt;/p&gt;

&lt;p&gt;Six months later, the requirement changes – the merging condition shifts from “overlapping alone” to “intervals of no more than 3 days”. Modifying the code to fit this new rule is extremely difficult, basically amounting to a rewrite. You’d need to work out the logic from scratch, mentally re-running the window functions’ computations, just to figure out what to change and how. The code runs, but the cost of understanding it is about the same as rewriting it from scratch.&lt;/p&gt;

&lt;p&gt;This is the reality of SQL development: code is written for machines to execute, not for humans to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why self-documentation is particularly hard for SQL
&lt;/h2&gt;

&lt;p&gt;SQL is a declarative language: it tells the database what to do, not how to do. This creates a problem –** business logic is encoded into the syntactic structure instead of being expressed explicitly.&lt;br&gt;
**&lt;br&gt;
A window function like LAG(amount, 1) OVER (PARTITION BY customer_id ORDER BY month) may correspond to the business concept of “calculating last month’s transaction amount”.&lt;/p&gt;

&lt;p&gt;A cumulative-sum snippet like SUM(CASE WHEN ... THEN 1 ELSE 0 END) OVER (...) may correspond to the logic of “conditional segmentation”.&lt;/p&gt;

&lt;p&gt;But these business semantics are implicit in SQL – readers need to infer for themselves what each snippet is actually doing.&lt;/p&gt;

&lt;p&gt;Even worse, comments cannot solve this problem. Code changes, but comments don’t always follow. Over time, comments become less reliable than the code itself. And SQL’s nested structure is inherently resistant to comments. Wedged between multiple layers of parentheses, they only make readability worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-assisted coding doesn’t solve this problem either.&lt;/strong&gt; You generate a SQL snippet with AI, execute and commit it – but you don’t put the prompts in the repository. All that’s left is the final SQL. When the requirement changes, you are back to the same SQL, and have to re-decode its logic from scratch. Even if the original spec written for AI is kept, there’s no guarantee the regeneration produces the same SQL – LLMs aren’t deterministic. Update the spec and regenerate, and you might get a completely different query, which means a re-audit is involved. That’s not a modification, that amounts to a rewrite.&lt;/p&gt;

&lt;p&gt;Therefore, AI-assisted coding doesn’t solve the root problem: documentation and code falling out of sync.&lt;/p&gt;

&lt;p&gt;Unless, spec and SQL are fused into one – permanently paired, so a spec change automatically updates the SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Code as documentation” is an ideal. SQL makes it further out of reach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One misconception needs clearing up here: “code as documentation” doesn’t mean “writing lots of comments in the code”. Comments are a supplement. True “code as documentation” means three things:&lt;/p&gt;

&lt;p&gt;The code structure itself expresses the logic. Reading the code is as clear as reading documentation – no extra explanation needed.&lt;/p&gt;

&lt;p&gt;Code and documentation are the same thing. There’s no such problem as “the code changed but the documentation didn’t”.&lt;/p&gt;

&lt;p&gt;When new hires take over, reading the code is enough to understand the business logic – no extra training and verbal handoffs from the last person needed.&lt;/p&gt;

&lt;p&gt;In the SQL world, this means SQL itself needs to be self-descriptive. Every line of code should tell the reader clearly “what this step is doing”, not “what this syntax does”. But native SQL can’t do this. Multi-layer nesting is everywhere, and readability suffers badly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, the solution is this: make the spec both readable and executable. Spec is documentation; spec is code.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SQLazy’s solution: workflow as documentation
&lt;/h2&gt;

&lt;p&gt;Here’s what SQLazy does: skip writing SQL directly. Instead, describe the logic as a workflow of steps. Each step is an atomic action. The steps combined are the complete business logic.&lt;/p&gt;

&lt;p&gt;Take the example of “merging overlapping intervals” again. Here’s how SQLazy’s workflow expresses it:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmvp2iivg0dwq7wiwy63s.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmvp2iivg0dwq7wiwy63s.png" alt="SQLazy’s workflow" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These lines of workflow read almost like natural language: sort, calculate the maximum end date so far, segment rows by condition, aggregate to get the earliest and latest dates. You can guess what most actions mean without ever having to learn them. Only “segment” needs a bit of explanation – it means “group the data by condition”. But paired with the visual step-by-step execution, one glance at the intermediate result makes it click instantly.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckio3fxfjpe7vg1uswhi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckio3fxfjpe7vg1uswhi.png" alt="intermediate result" width="799" height="428"&gt;&lt;/a&gt;&lt;br&gt;
Run this example online: &lt;a href="https://www.sqlazy.com/?4Bs" rel="noopener noreferrer"&gt;https://www.sqlazy.com/?4Bs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This compilable workflow is itself the documentation:&lt;/p&gt;

&lt;p&gt;Every step has clear business meaning: “sort” sorts, “compute” calculates helper values, “segment” groups the data by condition, and “summarize” aggregates.&lt;/p&gt;

&lt;p&gt;The order of steps is the order of thinking. First sort, then compute, then segment, then aggregate – exactly how you’d work through this problem manually.&lt;/p&gt;

&lt;p&gt;No extra comments are needed to explain “what this SQL is doing”. Every line of the workflow already says it clearly.&lt;/p&gt;

&lt;p&gt;Six months later, when the requirement changes, just open the workflow and edit the corresponding step directly.&lt;/p&gt;

&lt;p&gt;Code and documentation are one thing, not two. This is “code-as-documentation” actually made real.&lt;/p&gt;

&lt;p&gt;Here’s another example: group by account, and reset the sequence number whenever the interval between activities exceeds one hour. Here’s how SQLazy’s workflow expresses it:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvb1uilu7iyftmw36a0us.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvb1uilu7iyftmw36a0us.png" alt="Three lines of workflow" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three lines of workflow, business logic clear at a glance: “reset the sequence number whenever the interval exceeds one hour”. The equivalent hand-written SQL needs at least two layers of nested window functions, plus LAG-based time-gap calculations. With the workflow, you don’t need to take care of how the SQL is written – the compiler handles that for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The compiler guarantees: documentation and code will never get out of sync
&lt;/h2&gt;

&lt;p&gt;Under the traditional model, documentation and code are separate, kept in sync only by hand – and manual sync is error-prone. Documentation gets written, then the code changes without the docs catching up; or the code changes but the documentation isn’t updated.&lt;/p&gt;

&lt;p&gt;SQLazy’s model is this: workflow (documentation) → compiler → SQL (code).&lt;/p&gt;

&lt;p&gt;Compile the workflow above into SQL (switchable across different databases):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmfmejw3cqk1bnp4s67hx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmfmejw3cqk1bnp4s67hx.png" alt="into SQL " width="799" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Documentation is the source. Code is the compiled output. As long as the workflow stays the same, the generated SQL stays the same; when the workflow changes, the SQL regenerates automatically. There’s no more “documentation changed but code didn’t” or “code changed but documentation didn’t” – because they’re two sides of the same thing.&lt;/p&gt;

&lt;p&gt;When you switch databases, just change the target option – from MySQL to Oracle or from PostgreSQL to Snowflake – and the compiler automatically generates SQL in the matching dialect. No need to modify the workflow. No need to rewrite the documentation.&lt;/p&gt;

&lt;p&gt;In the SQL world, “Code-as-documentation” has always been a hard-to-reach ideal. Without a workflow mechanism like SQLazy’s, code and documentation stay two separate things. You write the SQL, then have to write separate documentation explaining what it does. Syncing the two falls entirely on manual work – and manual work means errors.&lt;/p&gt;

&lt;p&gt;SQLazy changes that. It breaks complex SQL into clear steps, which themselves are the documentation, and the compiler guarantees code and documentation stay in sync, permanently.&lt;/p&gt;

&lt;p&gt;This isn’t wrapping a shell around SQL. It’s changing the development paradigm from “writing code for machines to read” to “writing logic for humans to read and letting the machine translate it”. The workflow is for humans. The SQL is for the database to run. The compiler keeps the two in sync – no manual work required.&lt;/p&gt;

&lt;p&gt;Next time you open a SQL file written three months ago and can’t make sense of it, ask yourself: if you’d written it as a workflow instead, would you still be in this much pain?&lt;/p&gt;

</description>
      <category>code</category>
      <category>sql</category>
      <category>development</category>
      <category>sqlazy</category>
    </item>
    <item>
      <title>SQLazy：Conditional Running Total with Reset</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Mon, 27 Jul 2026 07:32:56 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazyconditional-running-total-with-reset-47oo</link>
      <guid>https://dev.to/esproc_spl/sqlazyconditional-running-total-with-reset-47oo</guid>
      <description>&lt;h2&gt;
  
  
  Problem Description
&lt;/h2&gt;

&lt;p&gt;Conditional running total with reset: restart accumulation when logic is 't'. An event table table_t1 records event sequences with three fields: id (sort key), logic (condition flag, values 't' or 'f'), and val (numeric value for accumulation). A computed column output needs to be added: when logic equals 't', output is set to 1; otherwise output equals the previous row's output plus the current row's val. This is a conditional running total that resets and restarts accumulation when logic is 't'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Data&lt;/strong&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flsctbhjjngxubctinc58.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flsctbhjjngxubctinc58.png" alt=" " width="800" height="303"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Expected Result&lt;/strong&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ivaqc718psgshirg1u7.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ivaqc718psgshirg1u7.png" alt=" " width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;id=1 (logic=t): Start of a new segment, output = 1&lt;/p&gt;

&lt;p&gt;id=2 to id=4 (logic=f): Accumulating row by row within the same segment, output = previous output + current val, yielding 3, 6, 10&lt;/p&gt;

&lt;p&gt;id=5 (logic=t): Reset condition met, start a new segment, output = 1&lt;/p&gt;
&lt;h2&gt;
  
  
  SQLazy Step-by-Step Implementation
&lt;/h2&gt;

&lt;p&gt;Core idea: First create a grouping marker, then accumulate by partition. Generate a logical grouping marker (logic_run) via a computed column; this marker increments each time logic is 't', dividing the data into independent segments. Then use logic_run as the partition key, applying the cum function for conditional accumulation within each partition. This two-step strategy—create grouping marker first, then accumulate by partition—is SQLazy's classic pattern for conditional reset accumulation problems.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97hrrgnt5lrkq5rwln5o.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97hrrgnt5lrkq5rwln5o.png" alt=" " width="798" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sqlazy.com/?2Kw" rel="noopener noreferrer"&gt;[Click to run this example online]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The steps are explained below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Sort by id in ascending order&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;sort id asc&lt;/p&gt;

&lt;p&gt;Sort the data by id in ascending order to ensure records are processed in chronological order; this is the prerequisite for subsequent segmentation and accumulation.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmffjwmewooznkh9vmip.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmffjwmewooznkh9vmip.png" alt=" " width="800" height="310"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Step 2: Create a logical grouping marker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;compute if (logic = 't' then 1 else 0) cum as logic_run&lt;/p&gt;

&lt;p&gt;This is the most critical step. Use the computed column with the cum (running total) argument to cumulatively sum the conditional expression if (logic=‘t’ then 1 else 0). When logic is ‘t’, it contributes 1 (indicating the start of a new segment); when logic is ‘f’, it contributes 0 (continuing the current segment). The cumulative result logic_run increments by 1 each time logic is ‘t’, dividing the data into segments: the first 4 records (id=1-4) have logic_run=1, the last 3 records (id=5-7) have logic_run=2.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcamecogef9249vk8k97l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcamecogef9249vk8k97l.png" alt=" " width="799" height="317"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Step 3: Calculate running total by partition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;compute if (logic = 't' then 1 else val) cum as op partition logic_run&lt;/p&gt;

&lt;p&gt;Within the logic_run partition, use the cum (running total) function to cumulatively sum the conditional expression if (logic=‘t’ then 1 else val). Each partition is calculated independently: when logic is ‘t’, the cumulative value resets to 1; when logic is ‘f’, the cumulative value is the previous row’s accumulation plus the current row’s val. The partition logic_run ensures that accumulations in different partitions do not interfere with each other. This achieves the conditional running total with reset.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fja48x1nmbnm6vwpxvdjn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fja48x1nmbnm6vwpxvdjn.png" alt=" " width="799" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unnecessary fields can be removed later using the derive function.&lt;/p&gt;

&lt;p&gt;Generated SQL&lt;br&gt;
After confirming the logic of the above 3 steps, the SQLazy compiler automatically generates native SQL (MySQL syntax used here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH t3 AS (
        SELECT id, logic, val, SUM(CASE 
                WHEN logic = 't' THEN 1
                ELSE 0
            END) OVER (ORDER BY CASE 
                WHEN id IS NULL THEN 1
                ELSE 0
            END, id ASC ROWS UNBOUNDED PRECEDING) AS logic_run
        FROM t2
    )
SELECT id, logic, val, logic_run
    , SUM(CASE 
        WHEN logic = 't' THEN 1
        ELSE val
    END) OVER (PARTITION BY logic_run ORDER BY CASE 
        WHEN id IS NULL THEN 1
        ELSE 0
    END, id ASC ROWS UNBOUNDED PRECEDING) AS oput
FROM t3
ORDER BY CASE 
    WHEN id IS NULL THEN 1
    ELSE 0
END, id ASC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLazy lets you describe logic in business language instead of writing nested SQL queries. This example demonstrates two major features of SQLazy: first, step-by-step calculation that breaks conditional reset accumulation into three independent steps—sort, create grouping marker, accumulate by partition—each verifiable independently; second, the partition + cum syntax for partitioned running totals, which is more concise and intuitive than SQL’s window function syntax, directly expressing the business semantics of “reset on ‘t’, otherwise accumulate”.&lt;/p&gt;

&lt;p&gt;Official Links&lt;br&gt;
SQLazy Online Experience: &lt;a href="https://sqlazy.com/" rel="noopener noreferrer"&gt;sqlazy.com&lt;/a&gt; (free, no registration required)&lt;br&gt;
SQLazy Repository: &lt;a href="https://github.com/SPLWare/SQLazy" rel="noopener noreferrer"&gt;github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>dataengineering</category>
      <category>llm</category>
      <category>sqlazy</category>
    </item>
    <item>
      <title>Compilable and Executable Pseudocode (spec) Solves AI Coding Hallucinations</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Fri, 24 Jul 2026 03:56:21 +0000</pubDate>
      <link>https://dev.to/esproc_spl/compilable-and-executable-pseudocode-spec-solves-ai-coding-hallucinations-160f</link>
      <guid>https://dev.to/esproc_spl/compilable-and-executable-pseudocode-spec-solves-ai-coding-hallucinations-160f</guid>
      <description>&lt;p&gt;The AI-generated SQL – no one dares to say they fully “understand” it&lt;br&gt;
First, look at the following SQL query, which aims to find credit card customers whose transaction amount has increased for three consecutive months:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH ranked AS (
  SELECT customer_id, month, amount,
    LAG(amount,1) OVER(PARTITION BY customer_id ORDER BY month) as prev_1,
    LAG(amount,2) OVER(PARTITION BY customer_id ORDER BY month) as prev_2
  FROM transactions
)
SELECT customer_id, COUNT(*) FROM ranked
WHERE amount &amp;gt; prev_1 AND prev_1 &amp;gt; prev_2
AND prev_1 IS NOT NULL AND prev_2 IS NOT NULL
GROUP BY customer_id HAVING COUNT(*) &amp;gt;= 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax is correct, and it runs. But is the logic sound? No one can say for sure right away.&lt;/p&gt;

&lt;p&gt;This is the daily routine of data analysts and developers today: they give a task to an AI, get dozens of lines of SQL in seconds, copy and paste the code, and it runs. But can they really trust the results? Modern AI can generate runnable SQL, but it is often not fully reliable.&lt;/p&gt;

&lt;p&gt;AI-generated SQL is a black box. The code may look correct, run without errors, and return a clean result, but what it actually does may have nothing to do with what you intended. Worse, you only realize it’s wrong after it has already executed. During the review phase, you have to mentally execute the SQL yourself, effectively redoing the work that the AI was supposed to handle.&lt;/p&gt;

&lt;p&gt;Even worse, even if the accuracy of AI-generated SQL reaches 90%, how can you know that this time they aren’t falling into the remaining 10%? Data-driven decisions should not become a game of chance. You get a SQL query that executes successfully and returns a clean result, but what it actually does is not what the user intended. This type of error cannot be caught by syntax checking. And the verification cost during the code review phase is extremely high: to confirm whether the logic is correct, you have to break down CTEs layer by layer, run them step by step, and compare the intermediate results.&lt;/p&gt;

&lt;p&gt;What is the real problem?&lt;br&gt;
In AI-assisted programming, there is a basic workflow: the user writes a clear specification and AI generates runnable code.&lt;/p&gt;

&lt;p&gt;This is not a question of whether AI is “capable” or not, but a hard constraint under current technical conditions. If the user only sketches out a couple of lines of specification, no AI can generate runnable engineering-grade code out of thin air – unless the task is a common, well-defined one such as bubble sort. The actual AI-assisted programming workflow is: the user writes a spec → the AI generates code → the user reviews it → spec the refined → the AI regenerates the code. The spec is both the starting point and a constraint throughout the entire process.&lt;/p&gt;

&lt;p&gt;But even with a perfectly clear specification, AI can still make mistakes – it’s a matter of probability. A poorly defined specification increases the chance that the AI will guess wrong, while a well-defined specification reduces that probability but can never eliminate it. This is because, at its core, AI is making probabilistic predictions. From input to output, it searches the space of possible answers for the most likely one, rather than logically deriving the only correct result.&lt;/p&gt;

&lt;p&gt;Then what can we do? Is there any way to turn a “likely answer” to a “definitive one”?&lt;/p&gt;

&lt;p&gt;The answer is: make the spec compliant enough to compile. Once the spec can be parsed and executed by a compiler, AI no longer needs to “guess” the code. The compiler is deterministic – the same input always produces the same output. No more “works this time, breaks the next”. This isn’t about “reducing the hallucination rate”; it’s about bypassing the stage where hallucinations can occur.&lt;/p&gt;

&lt;p&gt;So, the overall logic chain is clear: AI-assisted programming requires a spec → a well-defined spec can reduce hallucination rate → but it cannot eliminate hallucinations → make the spec compliant enough to compile → use a compiler instead of AI to generate code →hallucinations disappear.&lt;/p&gt;

&lt;p&gt;Compilable pseudocode: an advanced form of the specification&lt;br&gt;
Traditionally, the requirements document that programmers create is intended for humans. It contains natural language, charts and diagrams, and pseudocode. In the era of AI, the spec can evolve into a new form with two characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Human-readable: No need to be familiar with the target language (SQL, Java, Python), you can grasp the logic at a glance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Machine-compilable: The specification has a fixed syntactic structure and can be deterministically transformed into final code, without relying on AI guessing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is “compilable pseudocode”, a more compliant spec. The difference between it and traditional pseudocode is this: traditional pseudocode won’t be compiled – it is intended only for humans to read; compilable pseudocode is both human-readable and machine-readable, so it can be compiled into target code.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fovssrmmdfdgxaqdofo41.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fovssrmmdfdgxaqdofo41.png" alt="an advanced form of the specification" width="799" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The value of this kind of spec is that it squeezes AI’s “creative space” down to a minimum. AI doesn't need to “understand” the business logic, “design” an implementation plan, and “guess” the correct code – it only needs to translate the uncompliant spec into a compliant one. Translation leaves a far narrower margin for error than “generating code directly from natural language”.&lt;/p&gt;

&lt;p&gt;More importantly, you can verify the correctness of the spec before it is compiled and executed. Each step of the logic can be verified independently, so errors are caught before execution rather than being debugged afterward.&lt;/p&gt;

&lt;p&gt;SQLazy’s workflow: The compilable pseudocode&lt;br&gt;
SQLazy brings this concept to the field of complex SQL development. Its workflow is essentially compilable pseudocode: developers describe data querying logic step by step in natural language, previewing the intermediate result at each step. Once the logic is confirmed correct, the compiler deterministically generates the SQL.&lt;/p&gt;

&lt;p&gt;Still on the same requirement as above: finding the credit card customers whose transaction amount has increased for three consecutive months. Let’s implement this using SQLazy’s workflow:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwlfjvr23vcd4mf9g8hqn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwlfjvr23vcd4mf9g8hqn.png" alt="SQLazy’s code" width="800" height="213"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6evz7frxkbsdocud4aq1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6evz7frxkbsdocud4aq1.png" alt="workflow" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You don’t need to know SQL – the four steps alone make the logic clear. At every step you can preview the intermediate result:&lt;/p&gt;

&lt;p&gt;sort: Sort rows by customer and month.&lt;/p&gt;

&lt;p&gt;segment: Mark records where the transaction amounts fall, and generate a number for each consecutively rising segment.&lt;/p&gt;

&lt;p&gt;summarize: Count the number of months covered by each segment.&lt;/p&gt;

&lt;p&gt;filter: Select customers whose transaction amounts increase for at least consecutive 3 months (&amp;gt;=3).&lt;/p&gt;

&lt;p&gt;Here is another real-world example. A table stores time intervals for multiple accounts. Each account corresponds to multiple records, and the intervals may overlap. The goal is to merge all overlapping intervals within each account. With SQLazy, this can be implemented in just four steps:&lt;/p&gt;

&lt;p&gt;sort account_id, start_date: Sort rows by account and start date.&lt;/p&gt;

&lt;p&gt;compute end_date[:-1] max as prev_max: Calculate the maximum end date among all rows prior to the current row.&lt;/p&gt;

&lt;p&gt;segment condition start_date &amp;gt; prev_max as gid: Segment rows based on the condition, and assign a group number to each segment.&lt;/p&gt;

&lt;p&gt;summarize start_date min as start_date, end_date max as end_date: Aggregate by account and group number.&lt;/p&gt;

&lt;p&gt;Once the above logic is confirmed correct, the SQLazy compiler automatically generates native SQL – 100% accurate, with zero hallucination risk. If you switch the target database from MySQL to Oracle, you only need to change one option and the compiler automatically adapts to the target SQL dialect.&lt;/p&gt;

&lt;p&gt;SQLazy’s workflow is the spec, and the compiler is the last gate guaranteeing the code quality.&lt;/p&gt;

&lt;p&gt;In this process, the role of AI is also redefined. Conventionally, AI is responsible for generating the final SQL directly from the natural language – an extremely difficult task with a high hallucination rate. With SQLazy, AI is charged with only one thing: translating users’ colloquial, step-by-step descriptions into formal workflow syntax. The former is “decision-making” while the latter is “translation”. Even if the AI’s translation is slightly off, you can spot it at a glance at the workflow level, and the cost of correction is nearly zero.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsygvcmqw8z67gwjbr4i1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsygvcmqw8z67gwjbr4i1.png" alt="spec" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
Use LLM to convert the natural language into SQLazy syntax&lt;/p&gt;

&lt;p&gt;AI hallucinations in programming are a problem not because AI occasionally makes mistakes, but because of something more fundamental: under the conventional model, AI’s mistakes can only be discovered after execution. By the time you find the result is wrong, the code has already been generated – even executed – making the cost of correction extremely high.&lt;/p&gt;

&lt;p&gt;SQLazy’s compilable pseudocode shifts the moment of discovering errors from after execution to before execution. Humans confirm the logic at the spec level, and the compiler generates the final code. AI’s room for error is squeezed down to the “translation” step alone, where the result can be verified instantly by humans – solving the hallucination problem in AI programming.&lt;/p&gt;

&lt;p&gt;This does not mean programmers are going back to the era of “writing documentation”. Rather, it redivides the labor between humans and AI: humans handle design (describing “what to do” using structured spec); AI handles translation (converting colloquial spec into compliant spec); and the compiler handles execution (guaranteeing a deterministic output).&lt;/p&gt;

&lt;p&gt;The next phase of AI programming is probably not about making AI generate longer code, but about enabling humans to write clearer spec.&lt;/p&gt;

</description>
      <category>code</category>
      <category>sql</category>
      <category>devops</category>
      <category>software</category>
    </item>
    <item>
      <title>SQLazy: Querying the Start Timestamp of the Next Group from the Event Table</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Wed, 22 Jul 2026 07:24:00 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazy-querying-the-start-timestamp-of-the-next-group-from-the-event-table-4ja4</link>
      <guid>https://dev.to/esproc_spl/sqlazy-querying-the-start-timestamp-of-the-next-group-from-the-event-table-4ja4</guid>
      <description>&lt;p&gt;Problem description&lt;br&gt;
A table stores status values of an object at different timestamps, with one record per timestamp. Segment rows based on changes in the status value and output the effective start timestamp and the effective end timestamp for each status segment – that is, merge consecutive records with the same status into a single time interval.&lt;/p&gt;

&lt;p&gt;Source data:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvzybe3lhpudzmr4cz2g5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvzybe3lhpudzmr4cz2g5.png" alt="Source data" width="800" height="415"&gt;&lt;/a&gt;&lt;br&gt;
Expected output (intervals where each value remains constant):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6q956aiw47ozk7kce3g6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6q956aiw47ozk7kce3g6.png" alt="Expected output " width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take object 1 as an example:&lt;/p&gt;

&lt;p&gt;The first three records contain the same value A, so they can be merged into a single interval: 2024-01-01 – 2024-01-04 (the timestamp when the next record begins).&lt;/p&gt;

&lt;p&gt;Value B remains for record 4 and record 5 and they can be merged into a single interval: 2024-01-04 – 2024-01-06 (the timestamp when the next record begins).&lt;/p&gt;

&lt;p&gt;Record 6 and record 7 have value A again, the same as before. However, since value B intervenes between them, a new group starts: 2024-01-06 – infinity (the end date of the last interval is 9999-12-31).&lt;/p&gt;

&lt;p&gt;Step-by-step implementation with SQLazy&lt;br&gt;
Key approach: sort rows by timestamp, and check whether the value in the current row has changed relative to the previous row. If it has changed, start a new group; otherwise, put it in the current group. Finally, take the minimum timestamp in each group as the start timestamp and the minimum timestamp in the next group as its end timestamp.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcx6y5eqfj8kxhxalnetb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcx6y5eqfj8kxhxalnetb.png" alt="Step-by-step implementation" width="799" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[Run this example online]&lt;/p&gt;

&lt;p&gt;Let me walk through each step below:&lt;/p&gt;

&lt;p&gt;Step 1: Sort rows in chronological order in ascending order&lt;/p&gt;

&lt;p&gt;sort timestamp&lt;/p&gt;

&lt;p&gt;Sort rows by timestamp in ascending order, ensuring intervals in each group are processed in chronological order.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26ciniiwswek1402vxup.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26ciniiwswek1402vxup.png" alt="order in ascending order" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Start a new partition when value changes&lt;/p&gt;

&lt;p&gt;segment value; change; as gid&lt;/p&gt;

&lt;p&gt;This is the core step. The “segment” statement segment rows by the “value” field: each time the value changes, a new group is started and assigned a group number (gid). Within each group, the value remains unchanged.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmda6ht6bna3bkefxr0a5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmda6ht6bna3bkefxr0a5.png" alt="Start a new partition " width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Aggregate by group number and value, and take the start timestamp&lt;/p&gt;

&lt;p&gt;summarize timestamp first as effective_from, id first as id; group gid, value&lt;/p&gt;

&lt;p&gt;Group rows by gid and value:&lt;/p&gt;

&lt;p&gt;Take the timestamp of the first record in each group and use it as the effective start timestamp for that status.&lt;/p&gt;

&lt;p&gt;id: Take the first value in each group.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe5dkl88t2qa5mtpi944t.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe5dkl88t2qa5mtpi944t.png" alt="Group rows by gid and value" width="799" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4: Calculate the effective end timestamp&lt;/p&gt;

&lt;p&gt;&lt;em&gt;compute nvl(effective_from[1], datetime("9999-12-31 00:00:00")) as effective_to&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;effective_from[1] takes the effective_from value of the next row after the current group as the end timestamp for the current group. If the current group is the last group (i.e., effective_from[1] is empty), use nvl to set it to a maximum date (9999-12-31), which represents “to present”.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgzd0pvowmlji7iy2pbuy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgzd0pvowmlji7iy2pbuy.png" alt="Calculate the effective end timestamp" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, remove the helper column gid and output the final result.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9v9zg6gfohu4cuizw4am.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9v9zg6gfohu4cuizw4am.png" alt="remove the helper column gid " width="799" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compile the steps into SQL&lt;/p&gt;

&lt;p&gt;Once the above steps are complete and verified, SQLazy’s compiler can automatically generate the equivalent native SQL (The Oracle syntax for this example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH Value AS (
    SELECT
        id,
        value,
        timestamp
    FROM
        events
),
Value2 AS (
    SELECT
        gid,
        id AS id,
        value AS value,
        timestamp AS effective_from
    FROM
        (
            SELECT
                id,
                value,
                timestamp,
                SUM(
                    CASE
                        WHEN value &amp;lt;&amp;gt; col__5 THEN 1
                        ELSE 0
                    END
                ) OVER (
                    ORDER BY
                        CASE
                            WHEN timestamp IS NULL THEN 1
                            ELSE 0
                        END,
                        timestamp ASC
                ) + 1 AS gid
            FROM
                (
                    SELECT
                        Value.*,
                        LAG(value) OVER (
                            ORDER BY
                                CASE
                                    WHEN timestamp IS NULL THEN 1
                                    ELSE 0
                                END,
                                timestamp ASC
                        ) AS col__5
                    FROM
                        Value
                ) sub__6
        ) Value1
    GROUP BY
        gid
)
SELECT
    gid,
    id,
    value,
    effective_from,
    LEAD(
        effective_from,
        1,
        TO_DATE('9999-12-31 00:00:00', 'YYYY-MM-DD HH24:MI:SS')
    ) OVER (
        ORDER BY
            gid
    ) AS effective_to
FROM
    Value2
ORDER BY
gid;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You only need to verify the logic of each of the above four steps – no need to manually debug the SQL – and the compiler generates the production-ready code.&lt;/p&gt;

&lt;p&gt;SQLazy lets you describe logic using business language, rather than writing nested queries in SQL syntax. This example of “dividing time intervals based on value change” has one core statement: segment value; change, which directly corresponds to the business requirement of “starting a new group whenever the value changes”. To write SQL manually, you need to understand the window boundaries of LAG/LEAD, manually handle NULLs, and adjust for syntax differences across different databases. SQLazy, however, compresses this complex logic into 4 intuitive steps – sort, segment, aggregate, and take the the next row’s timestamp as the end timestamp. The intermediate result of each step can be verified independently, and the compiler generates the final runnable SQL. You just need to verify the business meaning of each step, and leave the rest to the compiler.&lt;/p&gt;

&lt;p&gt;Try SQLazy online: sqlazy.com (Free to use, signup not required)&lt;br&gt;
SQLazy project repository: github.com/SPLWare/SQLazy&lt;/p&gt;

</description>
      <category>devops</category>
      <category>sql</category>
      <category>programming</category>
      <category>sqlazy</category>
    </item>
    <item>
      <title>SQLazy: Merging Overlapping Time Intervals</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Thu, 16 Jul 2026 02:23:06 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazy-merging-overlapping-time-intervals-4fd0</link>
      <guid>https://dev.to/esproc_spl/sqlazy-merging-overlapping-time-intervals-4fd0</guid>
      <description>&lt;h2&gt;
  
  
  Problem description
&lt;/h2&gt;

&lt;p&gt;A table stores multiple time intervals for each account, and the intervals may overlap.&lt;/p&gt;

&lt;p&gt;Source data:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8m2kzxbh6kvefw4yiesu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8m2kzxbh6kvefw4yiesu.png" alt="Source data" width="799" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expected output (merge overlapping intervals for each account):&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ag38451p1lfionpe05n.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ag38451p1lfionpe05n.png" alt="output" width="800" height="276"&gt;&lt;/a&gt;&lt;br&gt;
Take account A as an example:&lt;/p&gt;

&lt;p&gt;The first three intervals (6/20-6/29, 6/25-7/25, 7/20-8/26) overlap and can be merged into 6/20 - 8/26;&lt;/p&gt;

&lt;p&gt;The nonoverlapping interval 12/25 - 1/25;&lt;/p&gt;

&lt;p&gt;The overlapping intervals (4/27-7/27, 6/25-7/14, 7/10-8/14) can be merged into 4/27-8/14;&lt;/p&gt;

&lt;p&gt;The nonoverlapping interval 9/10-11/12.&lt;/p&gt;

&lt;p&gt;Apply the same merging operations to account B: merge any intervals that overlap.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step-by-step implementation with SQLazy
&lt;/h2&gt;

&lt;p&gt;Key approach: Check whether the current interval’s start date is later than the maximum end date among all previous intervals. If it is, the current interval does not overlap with any of the previous intervals and a new group should be started; otherwise, merge it into the current group.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslp41rtwbw38bksp8w2u.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslp41rtwbw38bksp8w2u.png" alt="Step-by-step implementation" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sqlazy.com/?4Bs" rel="noopener noreferrer"&gt;[Run this example online]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 1: Sort rows by account and the start date&lt;/p&gt;

&lt;p&gt;sort account_id, start_date&lt;/p&gt;

&lt;p&gt;Sort rows by account_id and start_date in ascending order, ensuring intervals within each account are processed in chronological order.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fge0yyqk6f7l9x297gtxu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fge0yyqk6f7l9x297gtxu.png" alt="Sort rows by account and the start date" width="800" height="438"&gt;&lt;/a&gt;&lt;br&gt;
Step 2: Calculate the maximum end date among all rows before the current row&lt;/p&gt;

&lt;p&gt;compute end_date[:-1] max as prev_max; partition account_id&lt;/p&gt;

&lt;p&gt;Within each account partition, calculate the maximum end_date value among all rows from the first row up to, but excluding, the current row, and record it as prev_max.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb1za7kgvv857kl1t5t44.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb1za7kgvv857kl1t5t44.png" alt="compute end_date" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Segment rows based on the condition and assign group numbers&lt;/p&gt;

&lt;p&gt;segment condition start_date &amp;gt; prev_max as gid; partition account_id&lt;/p&gt;

&lt;p&gt;Check each row in sequence: if start_date &amp;gt; prev_max, the current interval does not overlap with any of the previous intervals and a new group is started (gid+1); otherwise, the row is assigned to the current group.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3vg9hrcgt00m29piegu8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3vg9hrcgt00m29piegu8.png" alt="segment condition start_date " width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4: Aggregate by account and group number&lt;/p&gt;

&lt;p&gt;summarize start_date min as start_date, end_date max as end_date; group account_id, gid&lt;/p&gt;

&lt;p&gt;For each group, extract the earliest start date and the latest end date to form a merged interval.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7qu6hau5az99xna4zt5s.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7qu6hau5az99xna4zt5s.png" alt="summarize start_date" width="799" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, remove the helper column gid.&lt;/p&gt;

&lt;p&gt;Compile the steps into SQL&lt;/p&gt;

&lt;p&gt;Once the above steps are complete and verified, SQLazy’s compiler can automatically generate the equivalent native SQL (using MySQL as an example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH t2 AS (
    SELECT
        account_id,
        start_date,
        end_date,
        MAX(end_date) OVER (
            PARTITION BY account_id
            ORDER BY
                CASE WHEN account_id IS NULL THEN 1 ELSE 0 END,
                account_id ASC,
                CASE WHEN start_date IS NULL THEN 1 ELSE 0 END,
                start_date ASC
            ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
        ) AS prev_max
    FROM
        acc
),
t3 AS (
    SELECT
        account_id,
        start_date,
        end_date,
        prev_max,
        1 + SUM(
            CASE
                WHEN start_date &amp;gt; prev_max THEN 1
                ELSE 0
            END
        ) OVER (
            PARTITION BY account_id
            ORDER BY
                CASE WHEN account_id IS NULL THEN 1 ELSE 0 END,
                account_id ASC,
                CASE WHEN start_date IS NULL THEN 1 ELSE 0 END,
                start_date ASC
        ) AS gid
    FROM
        t2
)
SELECT
    account_id,
    MIN(start_date) AS start_date,
    MAX(end_date) AS end_date
FROM
    t3
GROUP BY
    account_id,
    gid
ORDER BY
    account_id,
    start_date;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You only need to verify the logic of each of the four steps – no need to understand or debug the SQL – and the compiler will generate the production-ready code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SQLazy is more efficient
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjkxznksxs1dtj89lx7xh.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjkxznksxs1dtj89lx7xh.png" alt="Why SQLazy is more efficient" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQLazy lets you describe logic using business language, rather than writing nested queries in SQL syntax. This example of “merge overlapping intervals” is expressed clearly in just 4 steps – sort, calculate the maximum end date among the preceding rows, segment rows by condition, and aggregate. The compiler generates the final SQL for you, and you only need to verify the business meaning of each step.&lt;/p&gt;

&lt;p&gt;Try SQLazy online: &lt;a href="https://sqlazy.com/" rel="noopener noreferrer"&gt;sqlazy.com&lt;/a&gt; (Free to use, signup not required)&lt;br&gt;
SQLazy project repository: &lt;a href="https://github.com/SPLWare/SQLazy" rel="noopener noreferrer"&gt;github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>discuss</category>
      <category>sqlazy</category>
    </item>
    <item>
      <title>SQLazy: Account-based Grouping with Sequence Number Reset on Gaps Exceeding 1 Hour</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Tue, 14 Jul 2026 02:50:39 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazy-account-based-grouping-with-sequence-number-reset-on-gaps-exceeding-1-hour-4jip</link>
      <guid>https://dev.to/esproc_spl/sqlazy-account-based-grouping-with-sequence-number-reset-on-gaps-exceeding-1-hour-4jip</guid>
      <description>&lt;p&gt;Problem description&lt;br&gt;
Group by account and reset the sequence number when the time interval between events exceeds 1 hour&lt;/p&gt;

&lt;p&gt;A table consists of two fields: account_number and dt. Records within each account are ordered by time, and sequence numbers (Seq) need to be generated according to the following rules:&lt;/p&gt;

&lt;p&gt;Group records by account and order them by datetime in ascending order.&lt;/p&gt;

&lt;p&gt;If the interval between the current event time and the previous one exceeds 1 hour, reset the sequence number to 1.&lt;/p&gt;

&lt;p&gt;Otherwise (the interval ≤ 1 hour), add 1 to the sequence number.&lt;/p&gt;

&lt;p&gt;Source data:&lt;/p&gt;

&lt;p&gt;account_number dt&lt;/p&gt;

&lt;p&gt;19 2024-04-03 07:02:02&lt;/p&gt;

&lt;p&gt;19 2024-04-03 07:02:41&lt;/p&gt;

&lt;p&gt;19 2024-04-03 14:58:49&lt;/p&gt;

&lt;p&gt;19 2024-04-03 19:58:49&lt;/p&gt;

&lt;p&gt;19 2024-04-05 14:58:49&lt;/p&gt;

&lt;p&gt;19 2024-04-05 14:59:31&lt;/p&gt;

&lt;p&gt;19 2024-04-17 23:56:13&lt;/p&gt;

&lt;p&gt;20 2024-04-17 23:59:13&lt;/p&gt;

&lt;p&gt;19 2024-04-18 00:15:13&lt;/p&gt;

&lt;p&gt;19 2024-04-18 14:56:13&lt;/p&gt;

&lt;p&gt;20 2024-04-18 07:41:55&lt;/p&gt;

&lt;p&gt;20 2024-04-18 19:41:55&lt;/p&gt;

&lt;p&gt;20 2024-04-18 19:56:55&lt;/p&gt;

&lt;p&gt;19 2024-04-19 07:41:55&lt;/p&gt;

&lt;p&gt;19 2024-04-19 07:42:20&lt;/p&gt;

&lt;p&gt;19 2024-04-19 08:41:20&lt;/p&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;19 2024-04-03 07:02:02 1&lt;/p&gt;

&lt;p&gt;19 2024-04-03 07:02:41 2&lt;/p&gt;

&lt;p&gt;19 2024-04-03 14:58:49 1&lt;/p&gt;

&lt;p&gt;19 2024-04-03 19:58:49 1&lt;/p&gt;

&lt;p&gt;19 2024-04-05 14:58:49 1&lt;/p&gt;

&lt;p&gt;19 2024-04-05 14:59:31 2&lt;/p&gt;

&lt;p&gt;19 2024-04-17 23:56:13 1&lt;/p&gt;

&lt;p&gt;19 2024-04-18 00:15:13 2&lt;/p&gt;

&lt;p&gt;19 2024-04-18 14:56:13 1&lt;/p&gt;

&lt;p&gt;19 2024-04-19 07:41:55 1&lt;/p&gt;

&lt;p&gt;19 2024-04-19 07:42:20 2&lt;/p&gt;

&lt;p&gt;19 2024-04-19 08:41:20 3&lt;/p&gt;

&lt;p&gt;20 2024-04-17 23:59:13 1&lt;/p&gt;

&lt;p&gt;20 2024-04-18 07:41:55 1&lt;/p&gt;

&lt;p&gt;20 2024-04-18 19:41:55 1&lt;/p&gt;

&lt;p&gt;20 2024-04-18 19:56:55 2&lt;/p&gt;

&lt;p&gt;Step-by-step implementation with SQLazy&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoit9qn176r6s30d5rp4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoit9qn176r6s30d5rp4.png" alt="SQLazy" width="800" height="174"&gt;&lt;/a&gt;&lt;br&gt;
[Run this example online]&lt;/p&gt;

&lt;p&gt;Let me walk through each step below:&lt;/p&gt;

&lt;p&gt;Step 1: Sort rows by account and datetime in ascending order&lt;/p&gt;

&lt;p&gt;sort account_number asc, dt asc&lt;/p&gt;

&lt;p&gt;Sort the original data by account_number and dt in ascending order. This step ensures that records within each account are processed in chronological order.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlid3clewkzslnbzltwb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlid3clewkzslnbzltwb.png" alt="ascending order." width="800" height="390"&gt;&lt;/a&gt;&lt;br&gt;
Step 2: Partition records, check whether the time interval exceeds 1 hour, and generate the group number&lt;/p&gt;

&lt;p&gt;segment condition ((dt[-1] elapse 3600 second)&amp;lt;= dt) partition account_number as grp&lt;/p&gt;

&lt;p&gt;Within each partition of account, check whether the interval between the current row’s dt and the previous row’s dt exceeds 1 hour (3,600 seconds). The condition (dt[-1] elapse 3600 second)&amp;lt;= dt means that after adding 3,600 seconds to the previous row’s timestamp, it is less than or equal to the current row’s timestamp. If so, the interval is greater than or equal to 1 hour, and a new group begins; otherwise, the current row remains in the same group as the previous row. In the end, a group number (grp) is assigned to each record.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fewrcnagg2i38m3tklgbd.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fewrcnagg2i38m3tklgbd.png" alt="generate the group number" width="799" height="394"&gt;&lt;/a&gt;&lt;br&gt;
Step 3: Generate sequence numbers within each subgroup, partitioned by account_number, grp&lt;/p&gt;

&lt;p&gt;compute # as seq partition account_number, grp&lt;/p&gt;

&lt;p&gt;“#” represents a sequence number automatically generated in order within each subgroup, starting from 1. Records are partitioned by account number and group number, and within each subgroup, sequence numbers (seq) are assigned in the existing order of the records (which have already been sorted).&lt;/p&gt;

&lt;p&gt;Running this step produces the following output:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6mp0x5mraq1paf2wgp6e.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6mp0x5mraq1paf2wgp6e.png" alt=" account_number, grp" width="800" height="394"&gt;&lt;/a&gt;&lt;br&gt;
Finally, remove the helper column grp, or simply export the required columns, to obtain the final output.&lt;/p&gt;

&lt;p&gt;Compile the steps into SQL&lt;/p&gt;

&lt;p&gt;Once the above steps are complete, SQLazy’s compiler can generate the equivalent native SQL, without the need to write it manually.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3fjxbuy9inowjrgqrrhm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3fjxbuy9inowjrgqrrhm.png" alt="Compile the steps into SQL" width="800" height="421"&gt;&lt;/a&gt;&lt;br&gt;
Below is the generated SQL statement written in the target database dialect (MySQL in this 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="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;t2&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;account_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dt&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;numEvents&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;t3&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;account_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col__5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="k"&gt;SECOND&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;END&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;account_number&lt;/span&gt;
      &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
        &lt;span class="k"&gt;CASE&lt;/span&gt;
          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;account_number&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;account_number&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;CASE&lt;/span&gt;
          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
          &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;grp&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;t2&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;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&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;account_number&lt;/span&gt;
          &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
            &lt;span class="k"&gt;CASE&lt;/span&gt;
              &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;account_number&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
              &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;account_number&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;CASE&lt;/span&gt;
              &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
              &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;dt&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;col__5&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt;
        &lt;span class="n"&gt;t2&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sub__6&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;account_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;grp&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;account_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;grp&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;account_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;account_number&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;account_number&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;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;dt&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;seq&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;t3&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;account_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;CASE&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;account_number&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;account_number&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;CASE&lt;/span&gt;
    &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You only need to verify the logic of each of the three steps – no need to understand or debug the SQL – and the compiler will generate the production-ready code.&lt;/p&gt;

&lt;p&gt;Let’s compare SQL and SQLazy in a table:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjp1xhdgybwa7elch82j2.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjp1xhdgybwa7elch82j2.png" alt="compare SQL and SQLazy " width="800" height="485"&gt;&lt;/a&gt;&lt;br&gt;
This example demonstrates how concisely SQLazy handles “time-interval-based session segmentation” problem – using a segment condition to directly express the rule “start a new group when the gap exceeds 1 hour”, combined with row numbers within each sub-partition. The method precisely maps to the business requirement.&lt;/p&gt;

&lt;p&gt;Try SQLazy online: sqlazy.com (Free to use, signup not required)&lt;br&gt;
SQLazy project repository: github.com/SPLWare/SQLazy&lt;/p&gt;

</description>
      <category>sql</category>
      <category>sqlazy</category>
      <category>programming</category>
      <category>dataanalytics</category>
    </item>
    <item>
      <title>Compilable and Executable Pseudocode (spec) Solves AI Coding Hallucinations</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Thu, 09 Jul 2026 03:08:21 +0000</pubDate>
      <link>https://dev.to/esproc_spl/compilable-and-executable-pseudocode-spec-solves-ai-coding-hallucinations-5ghk</link>
      <guid>https://dev.to/esproc_spl/compilable-and-executable-pseudocode-spec-solves-ai-coding-hallucinations-5ghk</guid>
      <description>&lt;p&gt;&lt;strong&gt;The AI-generated SQL – no one dares to say they fully “understand” it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, look at the following SQL query, which aims to find credit card customers whose transaction amount has increased for three consecutive months:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH ranked AS (
  SELECT customer_id, month, amount,
    LAG(amount,1) OVER(PARTITION BY customer_id ORDER BY month) as prev_1,
    LAG(amount,2) OVER(PARTITION BY customer_id ORDER BY month) as prev_2
  FROM transactions
)
SELECT customer_id, COUNT(*) FROM ranked
WHERE amount &amp;gt; prev_1 AND prev_1 &amp;gt; prev_2
AND prev_1 IS NOT NULL AND prev_2 IS NOT NULL
GROUP BY customer_id HAVING COUNT(*) &amp;gt;= 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax is correct, and it runs. But is the logic sound? No one can say for sure right away.&lt;/p&gt;

&lt;p&gt;This is the daily routine of data analysts and developers today: they give a task to an AI, get dozens of lines of SQL in seconds, copy and paste the code, and it runs. But can they really trust the results? Modern AI can generate runnable SQL, but it is often not fully reliable.&lt;/p&gt;

&lt;p&gt;AI-generated SQL is a black box. The code may look correct, run without errors, and return a clean result, but what it actually does may have nothing to do with what you intended. Worse, you only realize it’s wrong after it has already executed. During the review phase, you have to mentally execute the SQL yourself, effectively redoing the work that the AI was supposed to handle.&lt;/p&gt;

&lt;p&gt;Even worse, even if the accuracy of AI-generated SQL reaches 90%, how can you know that this time they aren’t falling into the remaining 10%? Data-driven decisions should not become a game of chance. You get a SQL query that executes successfully and returns a clean result, but what it actually does is not what the user intended. This type of error cannot be caught by syntax checking. And the verification cost during the code review phase is extremely high: to confirm whether the logic is correct, you have to break down CTEs layer by layer, run them step by step, and compare the intermediate results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the real problem?&lt;/strong&gt;&lt;br&gt;
In AI-assisted programming, there is a basic workflow: the user writes a clear specification and AI generates runnable code.&lt;/p&gt;

&lt;p&gt;This is not a question of whether AI is “capable” or not, but a hard constraint under current technical conditions. If the user only sketches out a couple of lines of specification, no AI can generate runnable engineering-grade code out of thin air – unless the task is a common, well-defined one such as bubble sort. The actual AI-assisted programming workflow is: the user writes a spec → the AI generates code → the user reviews it → spec the refined → the AI regenerates the code. The spec is both the starting point and a constraint throughout the entire process.&lt;/p&gt;

&lt;p&gt;But even with a perfectly clear specification, AI can still make mistakes – it’s a matter of probability. A poorly defined specification increases the chance that the AI will guess wrong, while a well-defined specification reduces that probability but can never eliminate it. This is because, at its core, AI is making probabilistic predictions. From input to output, it searches the space of possible answers for the most likely one, rather than logically deriving the only correct result.&lt;/p&gt;

&lt;p&gt;Then what can we do? Is there any way to turn a “likely answer” to a “definitive one”?&lt;/p&gt;

&lt;p&gt;The answer is: make the spec compliant enough to compile. Once the spec can be parsed and executed by a compiler, AI no longer needs to “guess” the code. The compiler is deterministic – the same input always produces the same output. No more “works this time, breaks the next”. This isn’t about “reducing the hallucination rate”; it’s about bypassing the stage where hallucinations can occur.&lt;/p&gt;

&lt;p&gt;So, the overall logic chain is clear: AI-assisted programming requires a spec → a well-defined spec can reduce hallucination rate → but it cannot eliminate hallucinations → make the spec compliant enough to compile → use a compiler instead of AI to generate code →hallucinations disappear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compilable pseudocode: an advanced form of the specification&lt;/strong&gt;&lt;br&gt;
Traditionally, the requirements document that programmers create is intended for humans. It contains natural language, charts and diagrams, and pseudocode. In the era of AI, the spec can evolve into a new form with two characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Human-readable: No need to be familiar with the target language (SQL, Java, Python), you can grasp the logic at a glance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Machine-compilable: The specification has a fixed syntactic structure and can be deterministically transformed into final code, without relying on AI guessing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is “compilable pseudocode”, a more compliant spec. The difference between it and traditional pseudocode is this: traditional pseudocode won’t be compiled – it is intended only for humans to read; compilable pseudocode is both human-readable and machine-readable, so it can be compiled into target code.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9e489e69bw27efpsipl0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9e489e69bw27efpsipl0.png" alt=" " width="799" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The value of this kind of spec is that it squeezes AI’s “creative space” down to a minimum. AI doesn't need to “understand” the business logic, “design” an implementation plan, and “guess” the correct code – it only needs to translate the uncompliant spec into a compliant one. Translation leaves a far narrower margin for error than “generating code directly from natural language”.&lt;/p&gt;

&lt;p&gt;More importantly, you can verify the correctness of the spec before it is compiled and executed. Each step of the logic can be verified independently, so errors are caught before execution rather than being debugged afterward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQLazy’s workflow: The compilable pseudocode&lt;/strong&gt;&lt;br&gt;
SQLazy brings this concept to the field of complex SQL development. Its workflow is essentially compilable pseudocode: developers describe data querying logic step by step in natural language, previewing the intermediate result at each step. Once the logic is confirmed correct, the compiler deterministically generates the SQL.&lt;/p&gt;

&lt;p&gt;Still on the same requirement as above: finding the credit card customers whose transaction amount has increased for three consecutive months. Let’s implement this using SQLazy’s workflow:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fynq2xg00ut01aznwdnh9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fynq2xg00ut01aznwdnh9.png" alt=" " width="798" height="214"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhzswgnx004rkfx5oneo.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhzswgnx004rkfx5oneo.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
You don’t need to know SQL – the four steps alone make the logic clear. At every step you can preview the intermediate result:&lt;/p&gt;

&lt;p&gt;sort: Sort rows by customer and month.&lt;/p&gt;

&lt;p&gt;segment: Mark records where the transaction amounts fall, and generate a number for each consecutively rising segment.&lt;/p&gt;

&lt;p&gt;summarize: Count the number of months covered by each segment.&lt;/p&gt;

&lt;p&gt;filter: Select customers whose transaction amounts increase for at least consecutive 3 months (&amp;gt;=3).&lt;/p&gt;

&lt;p&gt;Here is another real-world example. A table stores time intervals for multiple accounts. Each account corresponds to multiple records, and the intervals may overlap. The goal is to merge all overlapping intervals within each account. With SQLazy, this can be implemented in just four steps:&lt;/p&gt;

&lt;p&gt;sort account_id, start_date: Sort rows by account and start date.&lt;/p&gt;

&lt;p&gt;compute end_date[:-1] max as prev_max: Calculate the maximum end date among all rows prior to the current row.&lt;/p&gt;

&lt;p&gt;segment condition start_date &amp;gt; prev_max as gid: Segment rows based on the condition, and assign a group number to each segment.&lt;/p&gt;

&lt;p&gt;summarize start_date min as start_date, end_date max as end_date: Aggregate by account and group number.&lt;/p&gt;

&lt;p&gt;Once the above logic is confirmed correct, the SQLazy compiler automatically generates native SQL – 100% accurate, with zero hallucination risk. If you switch the target database from MySQL to Oracle, you only need to change one option and the compiler automatically adapts to the target SQL dialect.&lt;/p&gt;

&lt;p&gt;SQLazy’s workflow is the spec, and the compiler is the last gate guaranteeing the code quality.&lt;/p&gt;

&lt;p&gt;In this process, the role of AI is also redefined. Conventionally, AI is responsible for generating the final SQL directly from the natural language – an extremely difficult task with a high hallucination rate. With SQLazy, AI is charged with only one thing: translating users’ colloquial, step-by-step descriptions into formal workflow syntax. The former is “decision-making” while the latter is “translation”. Even if the AI’s translation is slightly off, you can spot it at a glance at the workflow level, and the cost of correction is nearly zero.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F86dvr0uerg2wpc46lsya.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F86dvr0uerg2wpc46lsya.png" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
Use LLM to convert the natural language into SQLazy syntax&lt;/p&gt;

&lt;p&gt;AI hallucinations in programming are a problem not because AI occasionally makes mistakes, but because of something more fundamental: under the conventional model, AI’s mistakes can only be discovered after execution. By the time you find the result is wrong, the code has already been generated – even executed – making the cost of correction extremely high.&lt;/p&gt;

&lt;p&gt;SQLazy’s compilable pseudocode shifts the moment of discovering errors from after execution to before execution. Humans confirm the logic at the spec level, and the compiler generates the final code. AI’s room for error is squeezed down to the “translation” step alone, where the result can be verified instantly by humans – solving the hallucination problem in AI programming.&lt;/p&gt;

&lt;p&gt;This does not mean programmers are going back to the era of “writing documentation”. Rather, it redivides the labor between humans and AI: humans handle design (describing “what to do” using structured spec); AI handles translation (converting colloquial spec into compliant spec); and the compiler handles execution (guaranteeing a deterministic output).&lt;/p&gt;

&lt;p&gt;The next phase of AI programming is probably not about making AI generate longer code, but about enabling humans to write clearer spec.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>programming</category>
      <category>productivity</category>
      <category>sqlazy</category>
    </item>
    <item>
      <title>SQLazy: Group-based Cumulative Sums</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Fri, 03 Jul 2026 03:35:38 +0000</pubDate>
      <link>https://dev.to/esproc_spl/sqlazy-group-based-cumulative-sums-2el9</link>
      <guid>https://dev.to/esproc_spl/sqlazy-group-based-cumulative-sums-2el9</guid>
      <description>&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem description
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
Only retain invoiced rows; reset the cumulative amount at each invoiced row&lt;/p&gt;

&lt;p&gt;A business transaction table consists of four fields: ID, Date, Invoiced, and Amount. Each record represents one month, and Invoiced=1 indicates that an invoice was issued for that month.&lt;/p&gt;

&lt;p&gt;The query goal: Return every invoiced month for each ID, where the invoice amount equals the sum of all amounts from the previous invoiced month (exclusive), or the beginning of the table, to the current invoiced month (inclusive).&lt;/p&gt;

&lt;p&gt;Source data:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhigy5cbpuw61ycrvy7lj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhigy5cbpuw61ycrvy7lj.png" alt=" " width="800" height="416"&gt;&lt;/a&gt;&lt;br&gt;
Expected output: Only retain invoiced rows, where each Amount is the cumulative sum since the previous invoiced row (exclusive).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftoukyfamcnd3368bsvpo.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftoukyfamcnd3368bsvpo.png" alt=" " width="799" height="194"&gt;&lt;/a&gt;&lt;br&gt;
Take ID: AAA as an example:&lt;/p&gt;

&lt;p&gt;The first invoice - 2023-03: earlier January (10) + earlier February (15) + current (15) = 40&lt;/p&gt;

&lt;p&gt;The second invoice - 2023-06: Since the previous invoice: April (10) + May (10) + current (10) = 30&lt;/p&gt;

&lt;p&gt;Same for ID: BBB.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;
&lt;h2&gt;
  
  
  Step-by-step implementation with SQLazy
&lt;/h2&gt;

&lt;p&gt;**&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyvpa7pxcxzkwfatzwmvu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyvpa7pxcxzkwfatzwmvu.png" alt=" " width="800" height="305"&gt;&lt;/a&gt;&lt;br&gt;
[&lt;a href="https://www.sqlazy.com/?4yy" rel="noopener noreferrer"&gt;Run this example online&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;Let me walk through each step below:&lt;/p&gt;

&lt;p&gt;Step 1: Sort rows by ID and Date (descending)&lt;/p&gt;

&lt;p&gt;sort id,dt desc&lt;/p&gt;

&lt;p&gt;This step prepares for the subsequent grouping. Once sorted in descending order, each invoiced row and the earlier non-invoiced rows after it are grouped together for cumulative summation.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyno8fvvt4nx3mabdiq2a.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyno8fvvt4nx3mabdiq2a.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;br&gt;
Step 2: Perform a cumulative sum on the Invoiced field to generate group numbers&lt;/p&gt;

&lt;p&gt;compute invoiced cum as grp partition id&lt;/p&gt;

&lt;p&gt;Within each ID partition, perform a cumulative sum on the Invoiced field in the current (descending) order, including the current row. The result is as follows:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fchfsden7qeh5i79oka2t.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fchfsden7qeh5i79oka2t.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;br&gt;
Step 3: Group and aggregate by ID and grp&lt;/p&gt;

&lt;p&gt;summarize dt max as dt invoiced max as invoiced amount sum as amount group id grp&lt;/p&gt;

&lt;p&gt;dt max: Since rows are sorted in descending order, the largest date within each group corresponds to the invoiced month (i.e., the date of the invoiced row).&lt;/p&gt;

&lt;p&gt;invoiced max: Each group contains at least one row where Invoiced = 1, so the maximum Invoiced value within each group is 1.&lt;/p&gt;

&lt;p&gt;amount sum: Sum all amounts within each group, i.e., the total amount since the invoiced row (inclusive).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzq00o8mmdurxqb53ubet.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzq00o8mmdurxqb53ubet.png" alt=" " width="799" height="228"&gt;&lt;/a&gt;&lt;br&gt;
Step 4: Select the required columns&lt;/p&gt;

&lt;p&gt;derive id dt invoiced amount&lt;/p&gt;

&lt;p&gt;This step only cleans up the output by removing the helper column grp.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F27lg6ftqk9vx8vg20asx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F27lg6ftqk9vx8vg20asx.png" alt=" " width="798" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compile the steps into SQL&lt;/p&gt;

&lt;p&gt;Once the above steps are complete, SQLazy’s compiler can generate the equivalent native SQL, without the need to write it manually.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyky3fv2zypqffpqv9bpt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyky3fv2zypqffpqv9bpt.png" alt=" " width="800" height="453"&gt;&lt;/a&gt;&lt;br&gt;
To generate MySQL SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH t3 AS (
  SELECT
    id,
    grp,
    MAX(dt) AS dt,
    MAX(invoiced) AS invoiced,
    SUM(amount) AS amount
  FROM
    (
      SELECT
        id,
        dt,
        invoiced,
        amount,
        SUM(invoiced) OVER (
          PARTITION BY id
          ORDER BY
            CASE
              WHEN id IS NULL THEN 1
              ELSE 0
            END,
            id ASC,
            CASE
              WHEN dt IS NULL THEN 1
              ELSE 0
            END,
            dt DESC ROWS UNBOUNDED PRECEDING
        ) AS grp
      FROM
        invoice
    ) t2
  GROUP BY
    id,
    grp
)
SELECT
  id,
  dt,
  invoiced,
  amount
FROM
  t3
ORDER BY
  id,
  grp

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You only need to verify the logic of each of the four steps – no need to understand or debug the SQL – and the compiler will generate the production-ready code.&lt;/p&gt;

&lt;p&gt;Let’s compare SQL and SQLazy in a table:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feogb2gbanukiipd6apkn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feogb2gbanukiipd6apkn.png" alt=" " width="799" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This example demonstrates how naturally SQLazy handles the “event-based cumulative reset” problem – breaking down complex grouping logic into clear steps with a simple sorting trick and a cumulative sum.&lt;/p&gt;

&lt;p&gt;Try SQLazy online: &lt;a href="https://www.sqlazy.com/" rel="noopener noreferrer"&gt;sqlazy.com&lt;/a&gt; (Free to use, signup not required)&lt;br&gt;
SQLazy project repository: &lt;a href="https://github.com/SPLWare/SQLazy" rel="noopener noreferrer"&gt;github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>ai</category>
      <category>sqlazy</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop trusting AI-generated SQL blindly: Build queries step-by-step with SQLazy</title>
      <dc:creator>Judy</dc:creator>
      <pubDate>Fri, 19 Jun 2026 13:29:17 +0000</pubDate>
      <link>https://dev.to/esproc_spl/stop-trusting-ai-generated-sql-blindly-build-queries-step-by-step-with-sqlazy-2k</link>
      <guid>https://dev.to/esproc_spl/stop-trusting-ai-generated-sql-blindly-build-queries-step-by-step-with-sqlazy-2k</guid>
      <description>&lt;p&gt;AI can write runnable SQL statements, but it is often unreliable. SQLazyturns SQL development into a step-by-step, verifiable, and auditable workflow, with a compiler to ensure that the final output is correct.&lt;/p&gt;

&lt;p&gt;Problem: The AI-generated SQL is a black box&lt;br&gt;
We’ve all been in such a situation: throw a complex analytical query in ChatGPT or Claude and get a monster – dozens of lines of SQL code crammed into one lump. And you think: “This might run… but can I trust it?”&lt;/p&gt;

&lt;p&gt;In reality, the AI-generated SQL often fails in these ways:&lt;/p&gt;

&lt;p&gt;Incorrect join logic— Use the incorrect target table, or miss a necessary join condition.&lt;/p&gt;

&lt;p&gt;Aggregation mistake — GROUP BY does not align with the analysis goal, or miss non-aggregated columns.&lt;/p&gt;

&lt;p&gt;Missed filter condition— Miss a subtle business constraint (such as “only count active users”).&lt;/p&gt;

&lt;p&gt;Semantic deviation— What you mean by “revenue” may not be the same as what the model considers the “total amount”.&lt;/p&gt;

&lt;p&gt;Ignored boundary condition—Usually the NULL values, empty sets, and extreme values are elegantly ignored.&lt;/p&gt;

&lt;p&gt;Today, AI can generate runnable SQL. But you never know whether you can trust it. Once the query involves deeply nested window functions and subqueries, it becomes difficult to review, debug, maintain, and migrate.&lt;/p&gt;

&lt;p&gt;Worse still, when the results look off, how do you even begin to fix it?&lt;/p&gt;

&lt;p&gt;You can only manually run it CTE by CTE, inserting SELECT * FROM … statements everywhere just to trace the logic;&lt;/p&gt;

&lt;p&gt;Or tweak the prompt and ask AI to regenerate – only to end up with code that is even harder to read.&lt;/p&gt;

&lt;p&gt;In the end, it takes longer than just writing it yourself.&lt;/p&gt;

&lt;p&gt;This is the true cost of “black-box SQL generation”.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;
&lt;h2&gt;
  
  
  How SQLazy works?
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
SQLazy does not generate a huge blob of SQL in one go. Instead, it turns the SQL development into a step-by-step, traceable workflow:&lt;/p&gt;

&lt;p&gt;Use semi-natural language syntax to describe what each step does.&lt;/p&gt;

&lt;p&gt;Verify the logic at each step – Intermediate results are inspectable.&lt;/p&gt;

&lt;p&gt;Let the compiler generate the final SQL.&lt;/p&gt;

&lt;p&gt;The final SQL is generated by the compiler, not the LLM. This means:&lt;/p&gt;

&lt;p&gt;Zero SQL errors from AI hallucination, with 100% correct results.&lt;/p&gt;

&lt;p&gt;The logic is fully auditable.&lt;/p&gt;

&lt;p&gt;The output is production-ready.&lt;/p&gt;

&lt;p&gt;Example: Count the longest consecutive rising days for a stock&lt;/p&gt;

&lt;p&gt;This is a typical analytical query, and difficult to write in pure SQL – some companies use it as the interview question, with fewer than 20% of the candidates getting it right.&lt;/p&gt;

&lt;p&gt;Let’s see how SQLazy tackles this step by step.&lt;/p&gt;

&lt;p&gt;First, describe the logic step by step&lt;/p&gt;

&lt;p&gt;Rather than wrestling with the nested subqueries, SQLazy lets you express the logic as simple, sequential actions:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn8wy00hx8t24r6rm2xzf.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn8wy00hx8t24r6rm2xzf.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;br&gt;
That’s it. One step, one simple action. Here’s the line-by-line explanation:&lt;/p&gt;

&lt;p&gt;Load data from a file, a database, or an in-memory table built into SQLazy. In the IDE or WEB, you can instantly view the result at each step.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjztnkkor2q4t1q8pwf4s.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjztnkkor2q4t1q8pwf4s.png" alt=" " width="800" height="379"&gt;&lt;/a&gt;&lt;br&gt;
Filter records for stock 110838:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8799fpcpj0g2k25ppi1e.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8799fpcpj0g2k25ppi1e.png" alt=" " width="799" height="378"&gt;&lt;/a&gt;&lt;br&gt;
Sort records in ascending order by date:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdlr84dx3gr5gbd0aoyqa.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdlr84dx3gr5gbd0aoyqa.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;br&gt;
Mark breaks in the rising trend to separate consecutively rising streaks:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjq9vi9i0tatf1n13y8i.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjq9vi9i0tatf1n13y8i.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;br&gt;
Count the days in each consecutively rising streak:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc0p5ei9m80ppfjmxj76c.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc0p5ei9m80ppfjmxj76c.png" alt=" " width="800" height="378"&gt;&lt;/a&gt;&lt;br&gt;
Get the longest streak:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5u8yl9pxjd3szo8coc7.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5u8yl9pxjd3szo8coc7.png" alt=" " width="798" height="133"&gt;&lt;/a&gt;&lt;br&gt;
The logic is crystal clear. Anyone can understand what the query does without being a SQL expert.&lt;/p&gt;

&lt;p&gt;What’s more, you can execute each step and inspect the intermediate result. For example, after the records are segmented in step 3, a new column called NoRisingDays is generated, showing the group number for each consecutive streak. If something looks wrong, you can fix them immediately – no need to wait the entire query finish and guess where things went wrong.&lt;/p&gt;

&lt;p&gt;Then generate SQL with the compiler&lt;/p&gt;

&lt;p&gt;SQLazy automatically compiles the above steps into the native SQL dialect for your target database. It currently supports MySQL, PostgreSQL, and Oracle, with Snowflake and BigQuery support on the roadmap.&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;WITH&lt;/span&gt; &lt;span class="n"&gt;s2&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;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CL&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;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CL&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;t_3&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;CODE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;110838&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContinuousDays&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;max_ContinuousDays&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;NoRisingDays&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="n"&gt;DT&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;ContinuousDays&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;CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;CL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;col__4&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&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="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;DT&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DT&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;NoRisingDays&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;s2&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;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CL&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="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;DT&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DT&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;col__4&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sub__5&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;s3&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;NoRisingDays&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;s4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting SQL is difficult to understand, review, debug, and modify. But the SQLazy workflow is clear, easy to review and audit. As long as you follow the workflow correctly, the final SQL is guaranteed to be reliable.&lt;/p&gt;

&lt;p&gt;Here are the fundamental differences between SQLazy and ordinary AI SQL generators:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcdvoyda22kr3y35lua44.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcdvoyda22kr3y35lua44.png" alt=" " width="800" height="484"&gt;&lt;/a&gt;&lt;br&gt;
Here are my honest impressions from using SQLazy to run complex queries:&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;p&gt;The result at each step is inspectable. Before, trying to write complex SQL meant imagining the intermediate results – they only exist in your head. Now you can inspect the actual data table after each step is executed, and catch and fix errors immediately. At last, no more black boxes, and what a relief!&lt;/p&gt;

&lt;p&gt;Logic is broken down into multiple steps, which naturally serve as document. Once a workflow is finished, if requirements change three months later, there is no need to re-analyze dozens of lines – just find the relevant step and modify it. And if someone else takes over, reading through steps is so much faster than deciphering the SQL.&lt;/p&gt;

&lt;p&gt;Debugging becomes significantly faster. Once, I wrote the wrong grouping condition at the 4th step, executed the code, spotted an extra row in the intermediate table that shouldn’t have been there, and pinpoint the error immediately. Before, I would have to run the entire SQL, add debug fields everywhere, then run it again… back and forth, over and over.&lt;/p&gt;

&lt;p&gt;Zero cross-database hassle. Write your logic once and generate SQL for both MySQL and Oracle – no manual dialect translation needed.&lt;/p&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;p&gt;Learning cost exists. You need to adapt to the “stepwise way of thinking” – resisting the urge to reach for window functions right away. The first couple of uses may feel slower, but the thinking becomes clearer once you settle into it.&lt;/p&gt;

&lt;p&gt;Not necessary for simple scenarios. It is faster to write a 3-line SELECT in SQL. SQLazy is better suited for the complex scenarios where your brain starts to struggle to keep up.&lt;/p&gt;

&lt;p&gt;SQLazy is not an almighty tool. Note the unsupported features and scenarios.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Try SQLazy
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
Web Version (signup not required) &lt;a href="https://sqlazy.com" rel="noopener noreferrer"&gt;Free to use&lt;/a&gt; , ideal for quick trials.&lt;/p&gt;

&lt;p&gt;Desktop IDE: Best suited for daily work and large dataset processing, with unlimited local debugging.&lt;/p&gt;

&lt;p&gt;Repository address：&lt;a href="https://github.com/SPLWare/SQLazy" rel="noopener noreferrer"&gt;https://github.com/SPLWare/SQLazy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The examples directory in the project contains step-by-step solutions to multiple real-world SQL problems, including the “Count longest consecutive days of stock price gains” problem demonstrated above, as well as scenarios such as session analysis and financial indicator calculations.&lt;/p&gt;

&lt;p&gt;Shortcomings of SQLazy&lt;/p&gt;

&lt;p&gt;Recursive queries are not supported (already on the roadmap).&lt;/p&gt;

&lt;p&gt;Very old databases (such as MySQL 5.5) are not supported.&lt;/p&gt;

&lt;p&gt;SQLazy itself is not open-source, but all example workflows and documentation are on GitHub under the MIT license.&lt;/p&gt;

&lt;p&gt;Expected feedback&lt;/p&gt;

&lt;p&gt;If you have ever encountered an analysis scenario where writing logic in pure SQL is overly convoluted, let us know and we’ll try to rewrite it with SQLazy – so you can see whether the workflow is genuinely easier to read than plain SQL.&lt;/p&gt;

&lt;p&gt;When you use AI to write SQL, which is your biggest pain point – accuracy, maintainability, or trustworthiness?&lt;/p&gt;

&lt;p&gt;When it comes to the “step-by-step” approach to SQL development, what do you think is the biggest weakness?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
