- Introduction
AI-assisted data development is reshaping the way SQL is traditionally written. Today’s mainstream approaches can be broadly divided into two categories: one is the “end-to-end” approach, which directly generates native SQL; the other is the “layered” approach, which generates structured intermediate representations and then compiles them into SQL. The former is easy to get started with but difficult to handle complex business scenarios; the latter adds an abstraction layer, which enables engineering-grade guarantees of auditability, debuggability, and reproducibility.
The approach adopted in this article is a combination of Trae (ByteDance’s AI programming tool) as the “brain”, responsible for understanding requirements, clarifying ambiguities, and generating SQLazy step-by-step scripts (.nspl); and SQLazy’s dedicated IDE as the “execution layer”, responsible for syntax validation, step-by-step debugging, and cross-database compilation. Together, they form a closed loop of “AI planning + human review + deterministic engine execution”.
We selected four real-world cases, ranging from simple to complex, covering typical scenarios such as statistical aggregation, multi-table merging, cross-subgroup data filling, and amount allocation. The complete process – from requirement input to validation completion– is demonstrated, with emphasis on documenting the errors encountered and the reasoning behind corrections.
- Tool Collaboration Model
2.1 Trae’s Roles and Capabilities
Trae plays three key roles in this workflow:
- Automatically Load the Project Knowledge Base
Deploy the global knowledge base specification file (.md) described in Section 3.1 – Environment Setupto the project and register it as a centralized specification:
SQLazy script output format specifications (three tab-separated columns, one function per step)
Hard constraints (reserved word handling, cross-step reference rules, etc.)
Loading paths for function and feature documentation
When the user enters /sqlazy-plan followed by business requirements in the chat box, Trae automatically inherits all the rules above without needing to restate them.
- Structured Four-Step Output
Trae is constrained to generate solutions following the four steps below instead of directly providing a final answer:
Capability Overview: List the functions and features involved in the task.
Requirement Decomposition: Break down business requirements into a sequence of data processing steps.
Feature Matching: Match each step with the appropriate SQLazy feature.
Code Generation: Generate the final step-by-step script (.nspl).
This enforced output process ensures the auditability of the solution – the rationale behind each step is clearly visible.
- Proactive Requirement Clarification
Facing complex requirements, Trae proactively raises key questions, such as: How should the date range be defined? How should null values be handled? Is the grouping key unique? This prevents the AI from making unsupported assumptions about unstated conditions.
2.2 SQLazy's Core Value
SQLazy provides a dedicated IDE for writing and executing .nspl scripts. Its core value lies in three areas:
- Clear Step Semantics, Low Audit Complexity
Taking "longest streak of consecutive up days for a stock" as an example, the .nspl script requires only 5 steps: filter → sort → segment →count → find the maximum. Reading the entire script feels like reading a business operation checklist, rather than parsing complex nested SQL. Each line represents one step; the output of each step becomes the input for the next, with the entire logic laid out explicitly.
- Step-by-Step Execution, Quick Problem Identification
In the IDE, you can execute each step individually and inspect intermediate results in real time. Once a step's output doesn't meet expectations, you can immediately pinpoint the specific logic error without having to dig through dozens of lines of nested SQL.
- One Script, Compiled Everywhere
After validation passes, one click compiles to native SQL for MySQL, PostgreSQL, Oracle and other mainstream databases. No rewriting per database.
- Workflow
3.1 Environment Setup
The project adopts the following standard directory structure:
project_root/
├── plan.md # Global specification: format conventions, loading paths
├── sqlazy-plan.md # Command entry: /sqlazy-plan trigger, inherits all rules from plan.md
├── nspl/ # Delivery directory: .nspl scripts stored here
├── function/ # Function reference documentation (auto-loaded)
└── action/ # Action reference documentation (auto-loaded)
After creating a new project in Trae, copy sqlazy-plan.md, plan.md files, function/ and action/ directories from the SQLazy installation directory's LLM folder to the project root.
3.2 How to Trigger
Use the /sqlazy-plan command in the Trae chat box to trigger the task, followed by a complete business requirement description. It is best to specify the tables, fields, join relationships, grouping dimensions, time range, and output requirements – all in one go.
3.3 Validation and Correction
This is the most critical step in the entire workflow:
Construct a small set of representative test data and manually calculate the expected results.
Run the script step by step in the SQLazy IDE, comparing intermediate results against expected values.
When issues are found, modify the script directly or report them to Trae for regeneration.
After validation passes, compile to native SQL for the target database.
Hands-on Case Studies
The following four cases, from easy to hard, fully document the problem-solving process.
Case 1: Longest Streak of Consecutive Up Days for a Stock
Requirement:
/sqlazy-plan Stock price table stock contains three columns – CODE,DT (date), and CL (closing price). Calculate the maximum number of consecutive days the stock with code 100046 has been rising (i.e., each day’s closing price is higher than the previous day’s).
Analysis and Implementation:
This is the simplest type of statistical requirement. Trae outputs the following script following the four-step process:
Simple statistical requirement, generated correctly by AI in one attempt. Run and validate directly in the SQLazy IDE, then compile to native SQL for the target database.
Case 2: Merging Multiple Tables by ID into Single Rows
Requirement:
/sqlazy-plan There are four data tables, T1, T2, T3, and T4, with similar structures. Each table has two fields: the first field is an ID (named id, id2, id3, and id4 respectively), and the second field is named colA, colB, colC, and colD respectively. The goal is to merge these four tables by their ID values into a single result table with 9 columns: the first column, ID_main, stores the ID value, and the remaining 8 columns contain the fields from the four tables (i.e., all fields from T1, T2, T3, and T4). Each distinct ID appears as exactly one row in the merged table. If an ID is missing from any of the original tables, the corresponding columns from that table are set to NULL.
Analysis and Implementation:
The core challenge of this requirement is that the four tables have different ID field names (id, id2, id3, id4), and a method is needed to join them while ensuring no IDs are lost.
Trae designed a dual-track strategy of "full join + ID coalescing," outputting an 8-step script:
Same as Case 1, correct on first attempt.
Case 3: Cross-Group Sequential Field Value Filling
Requirement:
/sqlazy-plan Given a data table lines, where the first two columns, Group1 and Group2, are grouping columns, the third column, LineID, is a unique row identifier, and the fourth field, TargetField, is a numeric target column. After sorting by Group1, Group2, and LineID, within the same Group1, every Group2 has the same number of records; only the last Group2 (in sort order) has non-NULL values in TargetField, while all other Group2 groups are NULLs. The goal is to copy the TargetField values from the last Group2 group to the other groups within the same Group1, in the same sequential row order (i.e., matching by row positions in sorted order). The final output should be a table containing only the columns Group1, Group2, LineID, and TargetField, sorted in ascending order by the first three columns.
Analysis and Implementation (corrected after one iteration):
The core difficulty of this requirement is that LineID is unique across all rows and cannot be directly used as a cross-group join key. Trae's first version incorrectly used Group1 + LineID as the join key, causing mapping failures. Below is the complete iterative correction process.
First Version (Incorrect)
Trae's first attempt used Group1 + LineID as the join key to fill the TargetField from the last subgroup back to the whole table:
Problem: LineID is unique across all rows (e.g., 101, 105, 201, 205, 301, 305).
Different Group2 groups share no LineID values. Therefore, when using Group1 + LineID as the join key, only the last subgroup’s own rows can match; all other subgroups’ rows fail to match, TargetField remains NULL, and the fill logic completely fails.
Corrected Version: Using Row Number as Mapping Bridge
Instead of using LineID for joining, the “row number” (positional sequence 1, 2, 3, ... obtained by ranking within each Group2 by LineID) serves as the mapping bridge. Since every Group2 within the same Group1 has the same row count, the “Nth row” naturally corresponds across different Group2 groups.
Validation Example:
Assume the data is as follows (all LineID values are unique):
After t2 ranking, a row sequence number row_idx is generated: G2-1(101→1, 105→2), G2-2(201→1, 205→2), G2-3(301→1, 305→2). t3 takes the two records of G2-3. t4 creates a mapping table (A,1→100), (A,2→200). After t5’s backfill, the first row of every subgroup gets 100, and the second row gets 200.
When LineID is unique across the entire table, it cannot be directly used as a cross-group join key. You must first rank within each subgroup to obtain a row sequence number, and use the row number as the mapping bridge. The maximum-rank filter can directly select the last subgroup without requiring the additional aggregation and backfill steps.
Case 4: Invoice Amount Split by Account, with Total Preserved
Requirement:
/sqlazy-plan For the invoice table i (containing fields invoiceid, amount, projectid) and the project table p (containing fields id, projectid, accountcode), join them on projectid. For the joined result, add a new allocation field, splitamount, to implement a splitting logic that distributes the amount by the number of accounts under each project, while ensuring the total sum remains preserved. Within each group, sort by accountcode in ascending order. For the 2nd through the Nth account, calculate splitamount using amount/ total_number_of_accounts and round the result to 2 decimal places. The first account absorbs the rounding remainder; its splitamount equals the invoice’s original amount minus the sum of all other accounts’ splitamount values, so that the allocated amounts exactly match the original invoice amount.
Analysis and Implementation (finalized after two iterations):
This is the most complex of the four cases. Although Trae’s first version correctly identified the partitioned aggregation approach, there were issues with syntax details and step organization. It took two iterations to finalize.
First Iteration: Partitioned Aggregation (partially corrected)
Trae used a mathematically equivalent approach – partitioned aggregation to calculate the remainder – convert “first row’s remainder = total amount – sum of all the other rows’ allocated values” within the partition to amount - sum_temp_split + temp_split, where sum_temp_split is the sum of base allocated values across all rows in the partition.
Two issues were found after running:
Issue 1: Line 4 used * as the count formula, causing the SQLazy parser to report the error “logic error near [*]”. The current version does not support * as a count formula.
Issue 2: User feedback: “Complete the calculation directly in one step based on whether rank=1, without intermediate temporary columns” – they want to merge the base allocation calculation and the final conditional calculation into a single step, reducing the number of intermediate steps.
Second Iteration: Final Version
Two separate corrections were applied:
Changed * to the specific field projectid (counting projectid within a partition is equivalent to counting rows)
Combine the original t5 and t6 steps into one – within a single computed-column statement, create three derived columns separated by semicolons.
Below is the final script:
Validation Example:
projectid=1, amount=100.00, 3 accounts (accountcode=1, 2, 3)
The splitting logic is correct: the first account absorbs the rounding difference of 0.01, and the total is preserved. Three key lessons from this case: ① * is not supported in the current SQLazy environment; specific field names must be used; ② In computed-column statements, aggregate parameters and cross-row parameters are mutually exclusive and cannot be used simultaneously; ③ Reserved words (such as sum) used as names must be enclosed in single quotes.
- Conclusion
The core value of the Trae + SQLazy combination does not lie in “letting AI write SQL automatically”, but rather in constraining AI’s uncertainty within an auditable, debuggable intermediate layer, then letting a deterministic engine handle the final execution.
In this workflow, the three parties have clear division of labor:
Trae handles understanding requirements, clarifying ambiguities, and generating the structured initial nspl draft – this is what AI does best: “structuring fuzzy problems”.
SQLazy IDE handles syntax validation, step-by-step debugging, and cross-database compilation — this is the reliable execution by a deterministic engine.
Humans are responsible for verifying business rules, validating test results, and correcting logic deviations – this is irreplaceable business judgment.
Reviewing the four cases: from the simple statistics that passed on first attempt, to the multi-table merge generated correctly in one pass, to the cross-group filling that required correcting the join key, to the invoice split finalized after two iterations – each step confirms the feasibility of the “AI assistance + human review + small-sample validation” approach. In practice, this workflow can be standardized to make AI a true productivity amplifier rather than a source of risk.








Top comments (15)
What I like here is that AI isn’t being treated as the final authority. It translates a fuzzy business requirement into something structured, then a deterministic layer takes over and humans still validate the business meaning. That feels much closer to how AI-assisted development should work in production.
Case 3 is a great example: the first solution could be syntactically valid and still be logically wrong because LineID was the wrong relationship. No compiler can understand that business assumption for us.
I’d be curious about one thing as SQL complexity grows: where does SQLazy’s abstraction ceiling appear? Recursive CTEs, complex window logic, database-specific behavior, query-plan-sensitive optimizations, etc. At some point an intermediate language can become complex enough that we’ve effectively created another SQL dialect.
Still, I really like the core idea here: use AI to structure the uncertainty, but keep validation and execution deterministic. Nice work.
Hi,Mustafa , t's free. Why don't you give it a try?
Haha, fair point 😄 You got me.
I’ll give it a try. I’m especially curious to see how far the abstraction holds once the queries move beyond straightforward transformations into recursive CTEs, heavy window-function usage, and execution-plan-sensitive workloads.
That’s probably the best way to answer my own question anyway — break it with real workloads. 😄
Thanks, Judy!
Yes, you are really great! Mustafa~
Well… I took your advice and actually tried it 😄
My first impression is that SQLazy makes much more sense once you use it than when you only read about it.
What stood out to me most was the intermediate-step model. Being able to inspect the transformation step by step makes complex data logic surprisingly easy to reason about. It also creates a nice boundary between AI-generated reasoning and deterministic execution.
I deliberately tried to approach it with the question I mentioned earlier: “When does the abstraction start getting in the way?”
So far, I haven’t hit that wall as quickly as I expected. 😄
I still want to push it with more difficult cases — especially complex window logic, database-specific behavior, and performance-sensitive queries. That’s where I think the really interesting test begins.
So yes, Judy… you were right. Trying it was definitely better than just talking about it. 😄
Hi Mustafa,You are amazing! Looking forward to your sharing of usage cases.
Sure — let me give you a more realistic ERP-style case.
Suppose we have the following tables:
The goal is not simply to calculate stock balance, but to build a time-phased material availability and shortage calculation.
Business rules:
For each demand row, the final output should contain:
Example:
Material M100 in warehouse W1:
Initial usable stock: 100
Sales demand:
Incoming supply:
Expected allocation:
SO001 consumes 70 from current stock.
Remaining stock: 30
SO002 needs 80 on 2026-09-03.
At that point:
So:
SO003 needs 60 on 2026-09-05.
By then:
So SO003 can be fully covered, while the remaining future supply balance must still be tracked correctly.
The interesting part is that this is not just aggregation anymore.
The calculation is sequential, date-sensitive, warehouse-sensitive, and stateful. Each row changes the available balance for the next row.
That is exactly the kind of use case I want to test with SQLazy:
Can the logic still be expressed as clear, auditable steps?
Can intermediate allocation states be inspected easily?
And once compiled to native SQL, does the generated query remain understandable and efficient?
If SQLazy handles this cleanly, I think that would be a much stronger demonstration than a simple join or aggregation example.
Haha, what I'm saying is the successful cases that you have personally experienced. I'm looking forward to seeing you try it yourself!
Hi Judy — fair challenge. You were right to ask for a case I had actually run myself. So I went back and implemented the ERP scenario end to end. 😄
This time, it’s not a hypothetical example.
The POC uses five real source tables and covers progressive sales and production demand, stock consumption, purchase orders, destination transfers, date eligibility, priority ordering, partial allocation, shortages, and multi-warehouse isolation.
I implemented and executed the full 35-step workflow in the official SQLazy web app, captured SQLazy’s actual PostgreSQL compiler output, and then built an independent native PostgreSQL implementation as the control.
The results:
I also documented the parts where the abstraction leaked instead of hiding them. During the implementation I encountered current-alias compilation limitations, numeric nvl behavior, and a second join after aggregation that failed compilation. The final solution works around these using partitioned regulators, a running supply frontier, and a read-only supply-event adapter.
One result I found particularly interesting: the final 35-step NSPL workflow compiles into 753 lines of PostgreSQL across 20 CTEs. So the intermediate representation is considerably easier to review, but that abstraction definitely has a cost at the generated-SQL level.
My conclusion is deliberately narrow:
SQLazy passed this correctness POC.
I would not call that a production-performance result yet. The generated SQL is verbose, the running frame has a finite boundary, and representative-volume benchmarking still needs to be done separately.
Everything is reproducible here:
github.com/merbay-erp/sqlazy-erp-a...
So yes — this one is now a successful case that I personally implemented, executed, debugged, and independently verified. 😄
And if you spot a business rule or edge case I missed, I’d genuinely like to add it to the test suite.
Oh my goodness, you are so amazing!
Haha, thank you Judy! 😄
To be fair, you started this — you told me to actually try it, then caught me when I came back with a hypothetical case. 😂
That little challenge turned into a 35-step POC and a GitHub repository.
But I’m glad you pushed me. I learned much more about SQLazy by trying to break it with a real business problem than I ever could have by just reading the documentation.
So… I guess we both got something useful out of this one. 😄
Thanks for the challenge!
I gave you two stars. Did you see them?
Yes, I saw them! 😄⭐️⭐️
Thank you, Judy — that honestly made me smile.
What started as a small challenge from you ended up becoming one of the most enjoyable little engineering experiments I’ve done recently.
And now the repo has two stars from the SQLazy side… I guess that makes the homework officially accepted. 😂
Thanks again for pushing me to actually build it instead of just talking about it.
You can recommend this excellent tool to your friends who need it around you.
Hi Mustafa,
My colleague has seen your project. Thank you very much for your attempt. Here are some technical discussions for your reference:
Really appreciate you going all the way and building the full POC instead of leaving it as a hypothetical — that's exactly the kind of real workload we want to see tested against SQLazy.
On the ERP allocation scenario you picked: it's genuinely one of the harder ones. We actually ran AI on the same problem and got a ~47-step script. One thing we found internally: when the supply/demand structure gets more complex, that interval-based approach has to re-run extra "rounds" to propagate shortages, which gets fragile. Your single-pass running-frontier design avoids that — so point taken.
Quick responses to the questions you raised:
Auditable steps / inspectable intermediate state: yes, that's the core of what SQLazy is for, and your POC demonstrates it well.
"Does the compiled SQL stay understandable / efficient?": I'd reframe that one. The generated SQL is not something you're meant to read — SQLazy is about working in a higher layer, not reading the emitted SQL, so SQL readability is essentially a non-goal. And performance: we're deliberately not positioning SQLazy as a performance tool. The priority is deterministic, auditable, cross-database logic — not execution-plan-level speed. So we wouldn't read too much into the verbose 753-line output or the missing benchmark; those aren't where the product is aimed.
The abstraction-ceiling question (recursive CTEs, heavy window logic, DB-specific behavior) is a fair and open one — still under exploration.
One important heads-up: we've since changed the condition syntax for eliminating ambiguity, so your code will need a small update to run on the current release. Concretely, condition (...) sum col as alias is now sum col as alias, condition (...) — the condition binds to the preceding sum as a trailing filter. You can see a working example here: sqlazy.com/?3OH
Would be great to have you re-run the POC against the new syntax and let us know if anything else breaks.