我不再信 AI 生成的 SQL 了,我现在信这套做法

为什么我不信任 AI 生成的 SQL 了,不是因为它不通,而是因为它太通——能过语法检查、能执行,却可能在不注意的地方漏洞百出。

你有没有遇到过 AI 给的 SQL 通过了语法检查、执行不报错,数据却悄悄错了一行?

能跑,不等于可信

一个看起来很简单的需求:状态流水表用字段 "NewStatus" 记录每个 ID 的状态,每个 ID 都有 "ConfirmationStarted" 和多条 "Closed",要取 "ConfirmationStarted" 之前最近的那条 "Closed"。

CreatedAt

ID

NewStatus

2022-05-25 23:17:44

147

Active

2022-05-28 05:59:02

147

Closed

2022-06-18 05:59:01

147

Closed

2022-06-25 05:59:01

147

Closed

2022-07-13 00:02:47

147

ConfirmationStarted

2022-08-25 05:59:01

147

Closed

2023-04-29 05:59:02

1645

Closed

2023-05-08 14:53:34

1645

ConfirmationStarted

期望结果:

ID

CreatedAt

147

2022-06-25 05:59:01

1645

2023-04-29 05:59:02

以 ID=147 为例,在 ConfirmationStarted 之前共有三条 Closed,最后一条 06-25 才是“最近的一条”;08-25 虽也是 Closed 但已在 ConfirmationStarted 之后,不算。

期望结果只有两行:"147→2022-06-25"、"1645→2023-04-29"。其他 Closed 要么太早,要么在 ConfirmationStarted 之后,都不算。

把需求丢给 AI,几秒拿到一段漂亮的 SQL,CTE 套窗口函数,review 时挑不出毛病。上线两周后对账才发现 147 取成了 05-28,数据错了一行,但 SQL 能跑。

乍一看 bug 很难发现:分段逻辑写对了,却把最后的聚合从 MAX 写成了 MIN,把“离 ConfirmationStarted 最近的一条”变成了“最早的一条”,结果在边界数据上悄悄偏了一行。

AI 给的有漏洞的 SQL 长这样:

WITH t2 AS (
        SELECT CreatedAt, ID, NewStatus
            , 1 + SUM(CASE WHEN NewStatus='ConfirmationStarted' THEN 1 ELSE 0 END)
                OVER (PARTITION BY ID ORDER BY ID ASC, CreatedAt ASC ROWS UNBOUNDED PRECEDING) AS seg
        FROM mytable
    )
SELECT ID, MIN(CreatedAt) AS CreatedAt --这是几处错误之一,应为MAX,取最近的
FROM (
    SELECT CreatedAt, ID, NewStatus, seg
    FROM t2
    WHERE NewStatus='Closed' AND seg=1
) t_3
GROUP BY ID
ORDER BY ID

这就是 AI 直接产终态 SQL 最危险的地方:你能让 AI 重写一段话,却很难让它自己发现逻辑里那一行看不见的偏差。

我们让AI做了最不该做的事

AI 很擅长把口语需求拆成步骤,却不擅长为最终 SQL 的每一个边界条件都给出 100% 保证。它是概率模型,不是编译器。

公开评测显示,即使让最先进的大模型直接把自然语言翻译成可执行 SQL,在复杂查询上的执行准确率也常在六成左右徘徊,远未达到可直接签核的程度,平均三、四次就可能错一次。

旧范式是 "提示词→AI→不确定的终态 SQL",黑盒交付,只能让它整段重猜。

我们需要的新范式应该是 "提示词→AI→规范步骤→编译器→确定性 SQL",每一步可验证。

“跑得通”不算数,“敢签字”才算。

SQLazy,就是这个敢签字的底气,就是规范步骤的编译器,就是新范式的落地工具。

SQLazy里,这件事只4

同样的 "最近一条 Closed",在 SQLazy 里是 4 步,关键是:每一步都能点开看中间结果。

Name

Anchor

Statement

t1

mytable

sort ID, CreatedAt asc

t2


segment condition (NewStatus = "ConfirmationStarted") partition ID as seg

t3


filter (NewStatus = "Closed" and seg = 1)

t4


summarize CreatedAt max as CreatedAt; group ID


Picture1png

第 1 步 "sort ID, CreatedAt asc" 先把时序排正。

第 2 步 "segment condition (NewStatus ="ConfirmationStarted") partition ID as seg" 按 "ConfirmationStarted" 分段,"partition ID" 保证每个 ID 独立。分段列的名字是 seg,其中 seg=1 就是目标区间,不用手写 "SUM(CASE WHEN ...) OVER"。

Picture2png

第 3 步 "filter (NewStatus ="Closed"and seg = 1)" 只留目标区间里的 Closed。

第 4 步 "summarize CreatedAt max as CreatedAt; group ID" 每 ID 取最大时间,就是最近一条。

分段对不对,点开 t2 看 "seg" 列就知道;哪步错了就改哪步——错在第 2 步就只改第 2 步,不用推倒重来。编译时选 MySQL 还是 Snowflake,永远同一输入同一输出,不会编造字段。

WITH t2 AS (
        SELECT CreatedAt, ID, NewStatus
            , 1 + SUM(CASE
                WHEN (NewStatus = 'ConfirmationStarted') THEN 1
                ELSE 0
            END) OVER (PARTITION BY ID ORDER BY ID ASC, CreatedAt ASC ROWS UNBOUNDED PRECEDING) AS seg
        FROM mytable
    )
SELECT ID, MAX(CreatedAt) AS CreatedAt
FROM (
    SELECT CreatedAt, ID, NewStatus, seg
    FROM t2
    WHERE (NewStatus = 'Closed'
        AND seg = 1)
) t_3
GROUP BY ID
ORDER BY ID

这段 SQL 不是 AI 猜的,是编译器按固定规则把 4 步翻译出来的,肯定对,不用审。

当然,简单 CRUD 真没必要用 SQLazy,SQLazy 就是为那种 30 行以上的复杂逻辑准备的,比如带时序、分段、相对位置的逻辑。

把你最可疑的那段AI SQL贴进来

最典型的幻觉是什么?凭空编造不存在的字段或表、把筛选条件安到错的列上、把关联键或聚合口径搞错——SQL 照样能跑,结果早已跑偏,类似的场景人人都曾遇到。

别再陷入“重写提示词→重跑→碰运气”的循环了。把那段最不放心的业务逻辑丢给 SQLazy,拆成一步步可验的流程,并在评论区晒出你的幻觉案例。

AI给逻辑,编译器保落地——零幻觉