Trae+SQLazy 实践 SQL 国产化移植:Oracle => 达梦

一、前言

许多国内企业面临将国外数据库迁移到国产数据库的任务,这不仅涉及数据搬迁,更核心的挑战在于大量复杂 SQL 语句的移植。目前业界主要有两种方案:

1、目标数据库的兼容模式。许多国产数据库提供了对 Oracle 语法的兼容模拟,可直接运行 Oracle 的 SQL。优点是几乎无需改造、上线快,这对于 SQL 简单的 TP 业务非常适用。缺点是强依赖于目标数据库的实现,兼容覆盖度有限,通常只支持 Oracle,且对某些高级语法支持不完整(如 MODEL 子句、多列 PIVOT 等),难以适用于 SQL 复杂的 AP 业务;且临时解析会造成性能不稳定,也难以发挥目标数据库的原生特性。

2、用 AI 将 SQL 改写成目标数据库语法。优点是可以生成针对目标数据库的原生 SQL,充分发挥其能力。缺点是大语言模型存在幻觉问题,对语料素材缺乏的国产数据库不够友好,改写后的 SQL 出错率较高,需高昂的人工审核成本;而 AP 业务中大量涉及多层 CTE、嵌套窗口函数的复杂 SQL,又会进一步加剧这个问题。

采用 AI+SQLazy 的组合方案可解决上述不足:由 AI 理解原始 SQL,并翻译为结构化的 SQLazy 脚本,经过调试审计确认后,再由 SQLazy 编译为目标数据库的 SQL。SQLazy 脚本是分步执行、可审计的中间表示,每一步逻辑都清晰可读,极其便于人工审核和验证;SQLazy 的生成引擎是编译器机制,不依赖大模型,确定性的输入一定产生确定性的输出,从根本上避免了 AI 幻觉。

本文以 Trae 作为 AI 工具,实践将 Oracle SQL 移植到达梦数据库的过程。

二、工具的角色与能力

Trae 是 AI 编程工具,在本项目中作为理解与翻译的 "大脑"。它能够自动加载项目中的全局规约文件和 SQLazy 函数 / 功能参考文档,当用户输入 "/sqlazy 规划" 命令时,Trae 按四步流程(能力梳理→需求拆解→功能匹配→代码实现)结构化地输出 SQLazy 脚本,而不是直接生成 SQL。这种强制输出流程确保了方案的可审查性——每一步的选择依据都清晰可见。

SQLazy 是结构化数据计算工具,配有专属 IDE 用于编写和执行 SQLazy 脚本,在本项目中作为执行与验证的 "双手"。它能把复杂的业务逻辑编写为一条条人能读懂的 "操作指令",每条指令完成一步数据处理。SQLazy 提供三重保障:单步语义清晰、审计门槛低;分步执行、可快速定位问题;一套脚本、可编译为多种数据库的 SQL(MySQL、PostgreSQL、Oracle、达梦等),无需为不同数据库分别重写。

三、操作流程

3.1 环境准备

项目采用以下标准目录结构:

project_root/
├── 规划.md         # 全局规约:格式规范、加载路径
├── sqlazy规划.md   # 命令入口:/sqlazy规划触发
├── nsql/           # 交付目录:SQLazy脚本存放于此
├── 函数/           # 函数参考文档(自动加载)
└── 功能/           # 功能参考文档(自动加载)

在 Trae 中新建项目后,从 SQLazy 安装目录下的 LLM 目录复制 sqlazy 规划.md、规划.md 文件以及函数和功能两个目录到项目根目录即可。

3.2 触发方式

在 Trae 聊天框中使用 "/sqlazy 规划" 命令触发任务,后接完整的业务需求描述和 Oracle SQL 语句。Trae 会自动加载项目中的规约文件和函数 / 功能文档,按四步流程输出 SQLazy 脚本。

3.3 验证与修正

这是整条链路中最关键的环节:

1、构造少量有代表性的测试数据,手工算出期望结果。
2、在 SQLazy IDE 中分步运行脚本,对比中间结果与期望值。
3、发现问题时,将 SQLazy IDE 报出的错误信息反馈给 Trae,让其修正脚本。如此反复直到能正确运行。

验证通过后,再用 SQLazy 编译出达梦数据库的 SQL 即可,这一步非常简单,下面的实操中就不再提及操作过程了。

四、案例实操

以下四个案例涵盖了 CTE 多层嵌套与窗口函数、MODEL 子句、PIVOT 行转列、多排名函数与同比占比计算等典型 Oracle 特有语法场景。

案例一:区域产品销售占比及排名

这条 SQL 语句的功能是:筛选 2025 年销售数据,按产品和区域分组汇总销售额,然后计算每个产品在区域内的销售占比、产品全局总销售额、区域内排名,最终取各区域排名前三的产品,并计算与上一名产品的销售额差值。

用到的 Oracle 特殊函数和语法:SUM()OVER (PARTITION BY) 窗口聚合、RANK()OVER (PARTITION BY ... ORDER BY ...) 排名函数、LAG()OVER (PARTITION BY ... ORDER BY ...) 取前一行、|| 字符串拼接运算符。

【操作】在 Trae 聊天框中输入:

/sqlazy规划 把下面这句Oracle SQL语句转换成SQLazy脚本
SELECT 
  pa.product,
  pa.region,
  pa.total_sales,
  pa.pct_of_region || '%' AS region_share,
  pa.product_global_total,
  pa.rank_in_region,
  pa.total_sales - LAG(pa.total_sales, 1, 0) OVER (PARTITION BY pa.region ORDER BY pa.rank_in_region) AS sales_gap_from_previous
FROM (
  SELECT 
    rs.product,
    rs.region,
    rs.total_sales,
    ROUND(rs.total_sales / SUM(rs.total_sales) OVER (PARTITION BY rs.region) * 100, 2) AS pct_of_region,
    SUM(rs.total_sales) OVER (PARTITION BY rs.product) AS product_global_total,
    RANK() OVER (PARTITION BY rs.region ORDER BY rs.total_sales DESC) AS rank_in_region
  FROM (
    SELECT 
      product,
      region,
      SUM(amount) AS total_sales,
      COUNT(*) AS sales_count
    FROM sales_data
    WHERE sale_date >= DATE '2025-01-01' AND sale_date < DATE '2026-01-01'
    GROUP BY product, region
  ) rs
) pa
WHERE pa.rank_in_region <= 3
ORDER BY pa.region, pa.rank_in_region

【生成的 SQLazy 脚本】

t1=sales_data:筛选 (sale_date >= 2025-01-01 且 sale_date < 2026-01-01)
t2=汇总 amount 合计 命名 total_sales, 计数 命名 sales_count; 分组 product, region
t3=计算列 total_sales, 合计, 命名 region_total; 分区 region
t4=计算列 round(total_sales / region_total * 100, 2), 命名 pct_of_region
t5=计算列 total_sales, 合计, 命名 product_global_total; 分区 product
t6=排名 total_sales 降序; 命名 rank_in_region; 分区 region
t7=筛选 (rank_in_region <= 3)
t8=排序 region, rank_in_region
t9=计算列 concat(pct_of_region, "%"), 命名 region_share; (total_sales - nvl(total_sales[-1], 0)), 命名 sales_gap_from_previous; 分区 region
t10=导出表 product, region, total_sales, region_share, product_global_total, rank_in_region, sales_gap_from_previous

【纠错过程】

1 次纠错:运行第 2 行时报错 "值 [命名] 没有可以匹配的参数"。原因是 "计数" 前缺少被聚合的计算式,SQLazy 的语法要求聚合运算必须指定被聚合的字段。将 "计数 命名 sales_count" 改为 "amount 计数 命名 sales_count" 后通过。

【编译生成的达梦 SQL】

WITH t2 AS (
  SELECT
    product,
    region,
    SUM(amount) AS total_sales,
    COUNT(amount) AS sales_count
  FROM
    (
      SELECT
        product,
        region,
        amount,
        sale_date
      FROM
        sales_data
      WHERE
        (
          sale_date >= CAST('2025-01-01' AS DATE)
          AND sale_date < CAST('2026-01-01' AS DATE)
        )
    ) t_1
  GROUP BY
    product,
    region
),
t3 AS (
  SELECT
    product,
    region,
    total_sales,
    sales_count,
    SUM(total_sales) OVER (PARTITION BY region) AS region_total
  FROM
    t2
),
t5 AS (
  SELECT
    product,
    region,
    total_sales,
    sales_count,
    region_total,
    round(total_sales / region_total * 100, 2) AS pct_of_region,
    SUM(total_sales) OVER (PARTITION BY product) AS product_global_total
  FROM
    t3
),
sub__5 AS (
  SELECT
    sub__4.*,
    RANK() OVER (
      PARTITION BY region
      ORDER BY
        total_sales DESC
    ) AS rank_in_region
  FROM
    t5 sub__4
),
t9 AS (
  SELECT
    product,
    region,
    total_sales,
    sales_count,
    region_total,
    pct_of_region,
    product_global_total,
    rank_in_region,
    TO_CHAR(pct_of_region) + '%' AS region_share,
    (
      total_sales - COALESCE(
        NULLIF(
          LAG(total_sales, 1) OVER (
            PARTITION BY region
            ORDER BY
              CASE
                WHEN region IS NULL THEN 1
                ELSE 0
              END,
              region ASC,
              CASE
                WHEN rank_in_region IS NULL THEN 1
                ELSE 0
              END,
              rank_in_region ASC
          ),
          ''
        ),
        NULLIF(0, '')
      )
    ) AS sales_gap_from_previous
  FROM
    sub__5
  WHERE
    (rank_in_region <= 3)
)
SELECT
  product,
  region,
  total_sales,
  region_share,
  product_global_total,
  rank_in_region,
  sales_gap_from_previous
FROM
  t9
ORDER BY
  CASE
    WHEN region IS NULL THEN 1
    ELSE 0
  END,
  region ASC,
  CASE
    WHEN rank_in_region IS NULL THEN 1
    ELSE 0
  END,
  rank_in_region ASC

案例二:同比环比增长率(MODEL 子句)

这条 SQL 语句的功能是:使用 MODEL 子句按产品分区,基于年份维度,计算每个产品各季度的同比销售额(前一年同季度)和环比销售额(上一季度),并计算同比增长率和环比增长率。

用到的 Oracle 特殊函数和语法:MODEL 子句(PARTITION BY 分区、DIMENSION BY 维度、MEASURES 度量、RULES 规则)是 Oracle 独有的行列引用模型,通过 sales[year-1] 这样的位置引用语法访问偏移数据;此外还有 NVL() 空值替换、CASE WHEN 条件判断。MODEL 子句在达梦等其他数据库中没有对应物,是最难移植的 Oracle 语法之一。

【操作】在 Trae 聊天框中输入:

/sqlazy规划 把下面这句Oracle SQL语句转换成SQLazy脚本
SELECT 
  product, 
  year, 
  sales, 
  prev_year_sales, 
  yoy_growth_pct,
  prev_quarter_sales,
  qoq_growth_pct
FROM sales_tbl
MODEL
  PARTITION BY (product)
  DIMENSION BY (year)
  MEASURES (
    sales,
    0 AS prev_year_sales,
    0 AS yoy_growth_pct, 
    0 AS prev_quarter_sales,
    0 AS qoq_growth_pct
  )
  RULES (
    prev_year_sales[year] = NVL(sales[year - 1], NULL),
    yoy_growth_pct[year] = 
      CASE 
        WHEN prev_year_sales[year] IS NOT NULL AND prev_year_sales[year] != 0 
        THEN ROUND((sales[year] - prev_year_sales[year]) / prev_year_sales[year] * 100, 2)
        ELSE NULL 
      END,
    prev_quarter_sales[year] = NVL(sales[year - 0.25], NULL),
    qoq_growth_pct[year] = 
      CASE 
        WHEN prev_quarter_sales[year] IS NOT NULL AND prev_quarter_sales[year] != 0 
        THEN ROUND((sales[year] - prev_quarter_sales[year]) / prev_quarter_sales[year] * 100, 2)
        ELSE NULL 
      END
  )
ORDER BY product, year

【生成的 SQLazy 脚本】

t1=sales_tbl:排序 product, year
t2=计算列 sales[-4], 命名 prev_year_sales; sales[-1], 命名 prev_quarter_sales; 分区 product
t3=计算列 条件 ((prev_year_sales 非空 且 prev_year_sales != 0) 则 round((sales - prev_year_sales) / prev_year_sales * 100, 2) 否则 null), 命名 yoy_growth_pct; 条件 ((prev_quarter_sales 非空 且 prev_quarter_sales != 0) 则 round((sales - prev_quarter_sales) / prev_quarter_sales * 100, 2) 否则 null), 命名 qoq_growth_pct
t4=导出表 product, year, sales, prev_year_sales, yoy_growth_pct, prev_quarter_sales, qoq_growth_pct

【纠错过程】

1 次纠错:运行第 1 行时报错 "函数 [year] 参数错误"。原因是 year 是 SQLazy 的内置函数(取日期的年份),直接用作列名时会被解析为函数调用。将列名 year 加引号改为 'year' 后通过。

2 次纠错:运行第 3 行时报错 "连词 [且] 匹配条件项错误"。原因是 "非空" 与 "且" 的组合在条件解析器中存在语法冲突。解决方法是用 nvl 函数将 null 转为 0,然后用单个 "!= 0" 判断同时覆盖 null 检查和除零保护。将条件改为 "nvl(prev_year_sales, 0) != 0" 后通过。这一改写不仅解决了语法问题,逻辑上也更加简洁——nvl(x, 0) != 0 在 x 为 null 时返回 false(等价于 IS NOT NULL),在 x 为 0 时也返回 false(等价于除零保护)。

【最终正确脚本】

t1=sales_tbl:排序 product, 'year'
t2=计算列 sales[-4], 命名 prev_year_sales; sales[-1], 命名 prev_quarter_sales; 分区 product
t3=计算列 条件 (nvl(prev_year_sales, 0) != 0 则 round((sales - prev_year_sales) / prev_year_sales * 100, 2) 否则 null), 命名 yoy_growth_pct; 条件 (nvl(prev_quarter_sales, 0) != 0 则 round((sales - prev_quarter_sales) / prev_quarter_sales * 100, 2) 否则 null), 命名 qoq_growth_pct
t4=导出表 product, 'year', sales, prev_year_sales, yoy_growth_pct, prev_quarter_sales, qoq_growth_pct

【编译生成的达梦 SQL】

WITH t2 AS (
  SELECT
    product,
    year,
    sales,
    LAG(sales, 4) OVER (
      PARTITION BY product
      ORDER BY
        CASE
          WHEN product IS NULL THEN 1
          ELSE 0
        END,
        product ASC,
        CASE
          WHEN "year" IS NULL THEN 1
          ELSE 0
        END,
        "year" ASC
    ) AS prev_year_sales,
    LAG(sales, 1) OVER (
      PARTITION BY product
      ORDER BY
        CASE
          WHEN product IS NULL THEN 1
          ELSE 0
        END,
        product ASC,
        CASE
          WHEN "year" IS NULL THEN 1
          ELSE 0
        END,
        "year" ASC
    ) AS prev_quarter_sales
  FROM
    sales_tbl
),
t3 AS (
  SELECT
    product,
    year,
    sales,
    prev_year_sales,
    prev_quarter_sales,
    CASE
      WHEN COALESCE(NULLIF(prev_year_sales, ''), NULLIF(0, '')) <> 0 THEN round((sales - prev_year_sales) / prev_year_sales * 100, 2)
      ELSE NULL
    END AS yoy_growth_pct,
    CASE
      WHEN COALESCE(NULLIF(prev_quarter_sales, ''), NULLIF(0, '')) <> 0 THEN round(
        (sales - prev_quarter_sales) / prev_quarter_sales * 100,
        2
      )
      ELSE NULL
    END AS qoq_growth_pct
  FROM
    t2
)
SELECT
  product,
  "year",
  sales,
  prev_year_sales,
  yoy_growth_pct,
  prev_quarter_sales,
  qoq_growth_pct
FROM
  t3
ORDER BY
  CASE
    WHEN product IS NULL THEN 1
    ELSE 0
  END,
  product ASC,
  CASE
    WHEN "year" IS NULL THEN 1
    ELSE 0
  END,
  "year" ASC

 

案例三:渠道季度销售透视(PIVOT)

这条 SQL 语句的功能是:将销售数据按产品名称、渠道名称、季度分组汇总销售额,然后通过 PIVOT 将渠道×季度的行数据旋转为多列(如 direct_q1、direct_q2、...、telesales_q4 共 16 列),每个单元格是对应产品和渠道季度的销售额合计。

用到的 Oracle 特殊函数和语法:PIVOT 子句是 Oracle 独有的行转列语法,支持 FOR (多列) IN (多值列表) 形式的多列旋转;DECODE()条件映射函数将 channel_id 映射为渠道名称;SUBSTR() 取子串函数从 calendar_quarter_desc 中提取季度。PIVOT 在达梦等数据库中的支持程度不同,且多列 PIVOT 的语法差异更大。

【操作】在 Trae 聊天框中输入:

/sqlazy规划 把下面这句Oracle SQL语句转换成SQLazy脚本
SELECT *
FROM (
  SELECT 
    p.prod_name AS product,
    DECODE(s.channel_id, 3, 'Direct', 4, 'Internet', 5, 'Catalog', 9, 'TeleSales', 'Other') AS channel_name,
    SUBSTR(t.calendar_quarter_desc, 6, 2) AS quarter,
    SUM(s.amount_sold) AS total_sales
  FROM sales s
  JOIN products p ON s.prod_id = p.prod_id
  JOIN times t ON s.time_id = t.time_id
  GROUP BY p.prod_name, s.channel_id, SUBSTR(t.calendar_quarter_desc, 6, 2)
)
PIVOT (
  SUM(total_sales)
  FOR (channel_name, quarter)
  IN (
    ('Direct', 'Q1') AS direct_q1,
    ('Direct', 'Q2') AS direct_q2,
    ('Direct', 'Q3') AS direct_q3,
    ('Direct', 'Q4') AS direct_q4,
    ('Internet', 'Q1') AS internet_q1,
    ('Internet', 'Q2') AS internet_q2,
    ('Internet', 'Q3') AS internet_q3,
    ('Internet', 'Q4') AS internet_q4,
    ('Catalog', 'Q1') AS catalog_q1,
    ('Catalog', 'Q2') AS catalog_q2,
    ('Catalog', 'Q3') AS catalog_q3,
    ('Catalog', 'Q4') AS catalog_q4,
    ('TeleSales', 'Q1') AS telesales_q1,
    ('TeleSales', 'Q2') AS telesales_q2,
    ('TeleSales', 'Q3') AS telesales_q3,
    ('TeleSales', 'Q4') AS telesales_q4
  )
)
ORDER BY product

【生成的 SQLazy 脚本】

t1=sales:拼接 prod_id; 关联表 products; prod_id; 拼接列 prod_name; 内关联
t2=拼接 time_id; 关联表 times; time_id; 拼接列 calendar_quarter_desc; 内关联
t3=计算列 条件 ((channel_id == 3) 则 "Direct", (channel_id == 4) 则 "Internet", (channel_id == 5) 则 "Catalog", (channel_id == 9) 则 "TeleSales" 否则 "Other"), 命名 channel_name
t4=计算列 (calendar_quarter_desc 取子串("-")), 命名 quarter
t5=计算列 concat(lower(channel_name), "_", lower(quarter)), 命名 pivot_col
t6=汇总 amount_sold 合计 命名 total_sales; 分组 prod_name 命名 product, pivot_col
t7=旋转 分组 product; 标题列 pivot_col; 取值列 total_sales; 合计
t8=排序 product

【纠错过程】

1 次纠错:运行第 1 行时报错 "内关联:字段不存在"。原因是 "内关联" 是拼接功能的默认连接方式,可以省略不写;当关联列在两表中同名时,关联列名也可省略。将脚本去掉 "内关联" 和重复的关联列名后通过。

2 次纠错:运行第 4 行时报错 "函数 [quarter] 参数错误"。与案例二中的 year 列名问题相同,quarter 是 SQLazy 的内置函数(取日期的季度),作为列名需加引号。将命名 quarter 改为命名 'quarter',以及 t5 行中引用 quarter 处改为 'quarter' 后通过。

【最终正确脚本】

t1=sales:拼接 prod_id; 关联表 products; 拼接列 prod_name
t2=拼接 time_id; 关联表 times; 拼接列 calendar_quarter_desc
t3=计算列 条件 ((channel_id == 3) 则 "Direct", (channel_id == 4) 则 "Internet", (channel_id == 5) 则 "Catalog", (channel_id == 9) 则 "TeleSales" 否则 "Other"), 命名 channel_name
t4=计算列 (calendar_quarter_desc 取子串("-")), 命名 'quarter'
t5=计算列 concat(lower(channel_name),"_",lower('quarter')), 命名 pivot_col
t6=汇总 amount_sold 合计 命名 total_sales; 分组 prod_name 命名 product, pivot_col
t7=旋转 分组 product; 标题列 pivot_col; 取值列 total_sales; 合计
t8=排序 product

【编译生成的达梦 SQL】

WITH t2 AS (
  SELECT
    sales.prod_id,
    sales.channel_id,
    sales.time_id,
    sales.amount_sold,
    products.prod_name,
    times.calendar_quarter_desc
  FROM
    sales
    LEFT JOIN products ON sales.prod_id = products.prod_id
    LEFT JOIN times ON sales.time_id = times.time_id
),
t4 AS (
  SELECT
    prod_id,
    channel_id,
    time_id,
    amount_sold,
    prod_name,
    calendar_quarter_desc,
    CASE
      WHEN (channel_id = 3) THEN 'Direct'
      WHEN (channel_id = 4) THEN 'Internet'
      WHEN (channel_id = 5) THEN 'Catalog'
      WHEN (channel_id = 9) THEN 'TeleSales'
      ELSE 'Other'
    END AS channel_name,
    SUBSTR(
      calendar_quarter_desc,
      INSTR(calendar_quarter_desc, ('-')) + LENGTH(('-'))
    ) AS "quarter"
  FROM
    t2
),
t6 AS (
  SELECT
    prod_name AS product,
    pivot_col,
    SUM(amount_sold) AS total_sales
  FROM
    (
      SELECT
        prod_id,
        channel_id,
        time_id,
        amount_sold,
        prod_name,
        calendar_quarter_desc,
        channel_name,
        "quarter",
        lower(channel_name) + '_' + lower("quarter") AS pivot_col
      FROM
        t4
    ) t_4
  GROUP BY
    prod_name,
    pivot_col
),
t7 AS (
  SELECT
    *
  FROM
    (
      SELECT
        product,
        pivot_col,
        total_sales
      FROM
        t6
    ) PIVOT (
      SUM(total_sales) FOR pivot_col IN (
        'catalog_q1' AS catalog_q1,
        'catalog_q2' AS catalog_q2,
        'catalog_q3' AS catalog_q3,
        'catalog_q4' AS catalog_q4,
        'direct_q1' AS direct_q1,
        'direct_q2' AS direct_q2,
        'direct_q3' AS direct_q3,
        'direct_q4' AS direct_q4,
        'internet_q1' AS internet_q1,
        'internet_q2' AS internet_q2,
        'internet_q3' AS internet_q3,
        'internet_q4' AS internet_q4,
        'other_q1' AS other_q1,
        'other_q2' AS other_q2,
        'telesales_q1' AS telesales_q1,
        'telesales_q2' AS telesales_q2,
        'telesales_q3' AS telesales_q3,
        'telesales_q4' AS telesales_q4
      )
    )
)
SELECT
  product,
  'catalog_q1',
  'catalog_q2',
  'catalog_q3',
  'catalog_q4',
  'direct_q1',
  'direct_q2',
  'direct_q3',
  'direct_q4',
  'internet_q1',
  'internet_q2',
  'internet_q3',
  'internet_q4',
  'other_q1',
  'other_q2',
  'telesales_q1',
  'telesales_q2',
  'telesales_q3',
  'telesales_q4'
FROM
  t7
ORDER BY
  CASE
    WHEN product IS NULL THEN 1
    ELSE 0
  END,
  product ASC

案例四:科室手术统计排名

这条 SQL 语句的功能是:统计各科室医生的手术数据,计算难度等级描述、科室内排名(RANK 和 DENSE_RANK 两种)、手术量占科室比例、与上一名的差值、上一名医生姓名、科室平均手术量、以及业绩标签(是否达到科室平均)。

用到的 Oracle 特殊函数和语法:RANK()和 DENSE_RANK() 两种排名函数(区别在于并列时是否占用名次);RATIO_TO_REPORT()占比函数(Oracle 独有,计算当前值占分区合计的比例);AVG() OVER (PARTITION BY) 窗口平均;LAG()取前一行;DECODE() 条件映射;GREATEST() 取最大值函数与 DECODE 组合实现 "大于等于平均则优秀" 的判断逻辑。这些函数中,RATIO_TO_REPORT 和 GREATEST+DECODE 的组合是 Oracle 特有写法,在其他数据库中需要用不同方式实现。

【操作】在 Trae 聊天框中输入:

/sqlazy规划 把下面这句Oracle SQL语句转换成SQLazy脚本
SELECT
  dept_name,
  doctor_name,
  surgery_count,
  DECODE(difficulty_level,
         1, '一级(简单)',
         2, '二级(普通)',
         3, '三级(中等)',
         4, '四级(复杂)',
         5, '五级(疑难)',
         '未分级') AS difficulty_desc,
  RANK() OVER (PARTITION BY dept_name ORDER BY surgery_count DESC) AS rank_in_dept,
  DENSE_RANK() OVER (PARTITION BY dept_name ORDER BY surgery_count DESC) AS dense_rank_in_dept,
  ROUND(RATIO_TO_REPORT(surgery_count) OVER (PARTITION BY dept_name) * 100, 2) AS pct_of_dept,
  NVL(surgery_count - LAG(surgery_count, 1) OVER (PARTITION BY dept_name ORDER BY surgery_count DESC), 0) AS gap_from_prev,
  LAG(doctor_name, 1) OVER (PARTITION BY dept_name ORDER BY surgery_count DESC) AS prev_doctor_name,
  ROUND(AVG(surgery_count) OVER (PARTITION BY dept_name), 2) AS dept_avg,
  DECODE(
    GREATEST(surgery_count, AVG(surgery_count) OVER (PARTITION BY dept_name)),
    surgery_count, '优秀',
    '待提升'
  ) AS performance_tag
FROM surgery_stats
ORDER BY dept_name, surgery_count DESC

【生成的 SQLazy 脚本】

t1=surgery_stats:排序 dept_name, surgery_count 降序
t2=计算列 条件 ((difficulty_level == 1) 则 "一级(简单)", (difficulty_level == 2) 则 "二级(普通)", (difficulty_level == 3) 则 "三级(中等)", (difficulty_level == 4) 则 "四级(复杂)", (difficulty_level == 5) 则 "五级(疑难)" 否则 "未分级"), 命名 difficulty_desc
t3=排名 surgery_count 降序; 命名 rank_in_dept; 分区 dept_name
t4=排名 surgery_count 降序; 命名 dense_rank_in_dept; 中式; 分区 dept_name
t5=计算列 surgery_count, 占比, 命名 tmp_pct; surgery_count, 平均, 命名 tmp_avg; 分区 dept_name
t6=计算列 round(tmp_pct * 100, 2), 命名 pct_of_dept; round(tmp_avg, 2), 命名 dept_avg
t7=计算列 nvl(surgery_count - surgery_count[-1], 0), 命名 gap_from_prev; doctor_name[-1], 命名 prev_doctor_name; 分区 dept_name
t8=计算列 条件 (surgery_count >= dept_avg 则 "优秀" 否则 "待提升"), 命名 performance_tag
t9=导出表 dept_name, doctor_name, surgery_count, difficulty_desc, rank_in_dept, dense_rank_in_dept, pct_of_dept, gap_from_prev, prev_doctor_name, dept_avg, performance_tag

【纠错过程】

一次就生成完全正确的脚本,无需纠错。

【编译生成的达梦 SQL】

WITH t2 AS (
  SELECT
    dept_name,
    doctor_name,
    surgery_count,
    difficulty_level,
    CASE
      WHEN (difficulty_level = 1) THEN '一级(简单)'
      WHEN (difficulty_level = 2) THEN '二级(普通)'
      WHEN (difficulty_level = 3) THEN '三级(中等)'
      WHEN (difficulty_level = 4) THEN '四级(复杂)'
      WHEN (difficulty_level = 5) THEN '五级(疑难)'
      ELSE '未分级'
    END AS difficulty_desc
  FROM
    surgery_stats
),
sub__3 AS (
  SELECT
    sub__2.*,
    RANK() OVER (
      PARTITION BY dept_name
      ORDER BY
        surgery_count DESC
    ) AS rank_in_dept
  FROM
    t2 sub__2
),
t3 AS (
  SELECT
    dept_name,
    doctor_name,
    surgery_count,
    difficulty_level,
    difficulty_desc,
    rank_in_dept
  FROM
    sub__3
),
sub__5 AS (
  SELECT
    sub__4.*,
    DENSE_RANK() OVER (
      PARTITION BY dept_name
      ORDER BY
        surgery_count DESC
    ) AS dense_rank_in_dept
  FROM
    t3 sub__4
),
t5 AS (
  SELECT
    dept_name,
    doctor_name,
    surgery_count,
    difficulty_level,
    difficulty_desc,
    rank_in_dept,
    dense_rank_in_dept,
    (
      surgery_count * 1.0 / SUM(surgery_count) OVER (PARTITION BY dept_name)
    ) AS tmp_pct,
    AVG(surgery_count) OVER (PARTITION BY dept_name) AS tmp_avg
  FROM
    sub__5
),
t7 AS (
  SELECT
    dept_name,
    doctor_name,
    surgery_count,
    difficulty_level,
    difficulty_desc,
    rank_in_dept,
    dense_rank_in_dept,
    tmp_pct,
    tmp_avg,
    round(tmp_pct * 100, 2) AS pct_of_dept,
    round(tmp_avg, 2) AS dept_avg,
    COALESCE(
      NULLIF(
        surgery_count - LAG(surgery_count, 1) OVER (
          PARTITION BY dept_name
          ORDER BY
            NULL
        ),
        ''
      ),
      NULLIF(0, '')
    ) AS gap_from_prev,
    LAG(doctor_name, 1) OVER (
      PARTITION BY dept_name
      ORDER BY
        NULL
    ) AS prev_doctor_name
  FROM
    t5
),
t8 AS (
  SELECT
    dept_name,
    doctor_name,
    surgery_count,
    difficulty_level,
    difficulty_desc,
    rank_in_dept,
    dense_rank_in_dept,
    tmp_pct,
    tmp_avg,
    pct_of_dept,
    dept_avg,
    gap_from_prev,
    prev_doctor_name,
    CASE
      WHEN surgery_count >= dept_avg THEN '优秀'
      ELSE '待提升'
    END AS performance_tag
  FROM
    t7
)
SELECT
  dept_name,
  doctor_name,
  surgery_count,
  difficulty_desc,
  rank_in_dept,
  dense_rank_in_dept,
  pct_of_dept,
  gap_from_prev,
  prev_doctor_name,
  dept_avg,
  performance_tag
FROM
  t8

五、总结

1、用 AI+SQLazy 解决国产数据库移植的流程

通过以上四个案例,可以总结出国外数据库到国产数据库 SQL 移植的流程:

第一步:发起指令。发起 "/sqlazy 规划 把下面这句 Oracle SQL 语句转换成 SQLazy 脚本" 的指令,再附上 SQL 代码。

第二步:生成初稿。Trae 按四步流程(能力梳理→需求拆解→功能匹配→代码实现)输出 SQLazy 脚本初稿。

第三步:测试验证。在 SQLazy IDE 中导入测试数据,分步运行脚本,对比中间结果与期望值。

第四步:反馈纠错。若有语法错误或逻辑错误,把 SQLazy IDE 报出的错误信息原文反馈给 Trae,让其修正。如此反复直到能正确运行。

第五步:编译交付。验证通过后,在 SQLazy IDE 中选择目标数据库,一键编译生成对应的 SQL,交付生产环境使用。

之后保存这份 SQLazy 脚本以供下次修改和交接传承使用,不必保留难懂的 SQL 脚本。

四个案例覆盖了 Oracle 到国产数据库移植中的典型场景:CTE 多层嵌套与窗口函数(案例一)、MODEL 子句(案例二)、PIVOT 行转列(案例三)、多排名函数与占比计算(案例四)。其中案例一经历 1 次纠错后通过,案例二和案例三经历 2 次纠错后通过,案例四无错通过。

纠错过程中暴露出两类典型问题:一是 SQLazy 保留字冲突——year 和 quarter 等列名与 SQLazy 内置函数同名,需加引号区分;二是语法细节差异。这些问题都有一个共同特点:SQLazy 的报错信息非常精确,反馈给 Trae 后通常一次就能修正。

2、总结与展望

归根结底,国产化移植的核心挑战不在于数据搬迁,而在于 SQL 方言转换。AI+SQLazy 的组合提供了一条可靠路径:AI 理解语义并生成可读的中间脚本,确定性引擎编译出目标数据库的 SQL。AI 转换脚本、人工审核、引擎验证执行,三方协作,让 SQL 平滑落地到国产数据库。

这种方案覆盖面更广,虽然本文以 Oracle-> 达梦为例,但实际应用中并不只限于此,AI 可以理解几乎所有常见国外数据库,而采用编译方式生成国产数据库 SQL 时也不依赖于语料素材的多寡。再考虑到 SQLazy 脚本易调试易审计的特性,整体移植的成本也能更低,并获得更高的准确率。

更重要的是,AI+SQLazy 并不是单纯地低成本实现了 SQL 移植工作,这种方案还同时实现了“分析逻辑的持久化”,用简明易读的 SQLazy 脚本取代原本晦涩冗长的 SQL 代码,极大地提升了分析的可审计性和可传承性,代码才能成为资产,后续进一步修改或换库都变得非常轻松。

不过,我们也要清楚地看到,仅仅做 SQL 移植仅能解决功能兼容问题,而不能解决移植后带来性能问题,这受限于目标数据库本身的能力。为此,对于移植后性能锐降的 SQL,我们还可以采用 SQLazy 的基础技术——集算器 SPL 来兜底,SPL 拥有比 SQL 更多的高性能算法,经常可以跑出比 SQL 高出数量级的性能,但因其体系和 SQL 不同,并不能自动化简单移植,需要有一定程度的人工介入。好在,有性能问题的 SQL 通常是少数,整体工作量也不是很大,我们将在后续再做相应的实践。

相关推荐

应用开发中占比不低的报表为啥很难享受 AI 编程的红利

LLM 需要依靠海量公开文本完成训练,但复杂报表开发是融合数据提取、业务逻辑计算、复杂可视化排版、交互打印的复合型工作,逻辑交织,不能用线性文本简单描述,大模型缺乏训练数据,自然无法支撑专业报表开发。在 AI 编程全面重塑软件开发的当下,企业应用中大量复杂的固定报表开发却难以享受这波技术红利,究其根本,主要受制于两大核心阻碍。这种“LLM+ 规则引擎”的混合架构,既增加了 AI 的自由与灵活,又守住了企业报表场景对 100% 准确率的要求。不过,也有能啃硬骨头的,润乾报表就迎难而上,趟出了一条新路子。

Raqsoft 255

能不能把智能问数(Text2SQL)功能集成进自己的应用?

加载几个 JAR 包,配好配置文件,应用里直接调 JDBC 接口就能跑。不需要单独部署 AI 服务,不需要复杂接口对接,像集成普通报表组件一样,无缝对接到业务系统里。现在智能问数倒是有大把方案,但一个比一个重,动不动就要单独部署一套 AI 服务,挂 GPU 集群,配 AI 工程师,比应用系统本身还复杂,单独用起来都不容易,集成更是痴人说梦。它不是一个独立部署的庞然大物,而是一个轻量的“智能问数引擎”,可以以组件形式直接嵌入到业务系统内部。把智能问数能力做到自己的应用里,不用建 AI 团队,不用买 GPU。

Raqsoft 187

智能问数(Text2SQL)的两大方案及实施成本

现在流行挂个 RAG 知识库,把表结构、字段名、业务术语全向量化存起来,每次查询时现去检索相关片段喂给大模型。只做一件事,把口语化的自然语言转成有规则的自然语言,也就是“规范文本”。然后规则引擎根据预先配置好的业务词典和映射规则,把规范文本编译成 100% 准确的 SQL。核心工作量在配置业务词典,把用户爱说的“华北区”“在途订单”这些词,跟数据库表和字段对上。关键是,规则是写死的、确定的。SQL 怎么生成、逻辑怎么组合,全都写在规则里。出了问题,查规则、改词典、补映射,改完就知道对不对,没有不确定性。

Raqsoft 148

AI 化的自助报表和 ChatBI 有什么区别?

这种白盒机制对 ChatBI 的准确性也有极大帮助,当其他 ChatBI 还在为 LLM 的“幻觉”和错误挣扎时,润乾通过让人类随时介入和纠正,将不确定性限制在可控范围内,既保留了对话的便捷,又守住了数据准确性的底线,让产品真正具备了在企业级复杂场景中可靠落地的底气,这才是让 AI 报表从“玩具”走向“生产工具”的关键所在。然而,当前市面上的 ChatBI 大多是全面基于 LLM 实现的,确实能生成分析图表,但都是一次性输出,AI 化的自助报表已经打包进润乾报表的工具版中,,比如先生成一张简单表。

Raqsoft 118

非高频但真趁手,报表工具这个AI功能太方便了

这种功能平时可能不常用,但一旦遇到复杂报表的批量整改,这种说句话就能全选中修改好的爽快感真的让人离不开。它把原本需要半小时的“体力活”压缩到了几秒钟,这才是报表工具该有的智能化体验。如果报表简单,手动改改也就罢了;但若是面对下面这种几百个格子的复杂大报表,想靠肉眼一个个去“找茬”,行高大于 10 的行标成橙色也是一句话搞定,不用挨个去找去看行高。Copilot 即会自动检索并锁定字体为黑体的所有单元格。报表交付后,再根据用户反馈来修改报表,是很正常的事情。比如上面的任务,只需要两个命令就能完成。

Raqsoft 196

智能问数Text2SQL的JOIN噩梦怎样被DQL终结?

Text2SQL 火了三年,有一道行业级的坎始终迈不过去:多表关联(JOIN)。演示环境里百发百中的工具,一进入企业真实数据环境就频频翻车。在 Spider 这类标准多表数据集上,主流大模型方案的执行准确率仅在 60%-82% 之间;而面对网状交织的企业级复杂模型时,复杂跨表场景的准确率甚至会跌破 30%。现实是,绝大多数企业的业务数据都分散在几十上百张表里,80% 以上的日常查询都涉及多表关联。JOIN 问题解决不了,智能问数就只是演示玩具。

Raqsoft 23

做一个项目就能回本的 AI 报表工具,更多都是赚到

这意味着,只要支付相当于一名工程师一个月的成本,就能在一年内无限量地使用这款专注报表领域二十多年的专业工具,无论你的项目有多成功、服务器如何扩容,都不需要再追加一分钱授权费。某能源行业软件公司,随着项目越来越多,服务器节点也从 5 个猛增到一百多个,但因为采用了润乾报表的工具模式,他们无需再为扩容追加一分钱,一年三万买断企业级全功能版本,还有 AI 提升开发效率,做一个项目轻松回本,多出来的每一个项目,省下的授权费都是企业实打实的纯利润!报表工具卖了这么多年,成本一直不低,项目越多、服务器节点越多,

Raqsoft 156

自助报表 /BI 多维分析从 GUI 到 LUI 的进化

这样就可以极低成本为中小企业提供 AI+BI 的整体解决方案赋能,帮助企业跨越技术鸿沟,轻松迈入省钱又省人力的智能时代。,能够作为轻量级模块无缝嵌入企业现有的 OA、CRM、ERP 等业务系统中,不需要单独部署和管理独立沉重的 BI 系统。,彻底免去了学习专业操作的烦恼,只要能描述清楚分析需求,几乎所有人都能轻松上手,极大的拓宽了数据分析的场景。太过口语化的命令可能工具会识别效果不好,这时候可以请 LLM 帮忙,规范成工具可以理解的语言命令。用户只需直接输入分析需求,系统即可自动理解并进行各种分析。

Raqsoft 231

加上AI Copilot,这成了史上门槛最低的报表工具

目前尚无法像其它 AI 编程领域那样做到 “一句话生成所有复杂报表”的程度,部分工作仍需传统方式操作,但利用 AI 持续优化各个环节,降低使用门槛并解放开发者的生产力,也是报表技术演进中极具意义的进步。开发人员往往需要熟练记忆报表工具的各类函数语法、属性配置与布局设计,不仅前期学习成本高昂,后期也常被束缚于繁琐的重复性工作中,开发效率难以实现质的飞跃。”的深刻转变,极大地降低了报表工具的学习成本与难度,称其为史上门槛最低的报表工具也不算夸张。长期以来,报表开发一直属于不难,但是门槛也不低的任务。

Raqsoft 119

智能问数Text2SQL项目为啥经常是一地鸡毛

它包含字段映射、表间关系、维度层次、度量定义,还有一套可扩展的词汇体系,字段词、比较词、聚合词、宏词、动词……词典够丰富,描述能力才够强,复杂查询才能被准确翻译成规范文本,覆盖范围更广更复杂的查询场景。大模型有幻觉,就不要让它直接生成 SQL,而只是负责翻译成用户可读的“规范文本”,用户确认“是这个意思”之后,不能再由大模型负责执行,而由确定的规则引擎编译成 100% 准确的 SQL,这解决了算不准的问题。语义层不仅要建,还得足够复杂、足够丰富,否则描述能力太弱,用户换种问法就懵了。仿佛怼上数据库就能跑。

Raqsoft 127

搞一套 AI 加持下的对话式 BI 多维分析要多少成本?

LLM 只负责将自然语言转化为规范指令,再由规则引擎 100% 准确执行指令进行分析,避免 LLM 直接生成 SQL 或者报表的幻觉风险,另外 LLM 只负责规范语言命令,Token 费用会很低,LLM 不接触数据,也不存在数据安全问题。近年来,BI 工具很是火热,但高昂的价格始终是绕不开的门槛,无论是国外还是国内厂商,动辄数十万的授权费、按用户数叠加的订阅费,都让许多中小企业望而却步。,已经有很多场景验证过了,有需要的同学也可以去试试,反正软件是用好了才买单的。

Raqsoft 210

智能问数Text2SQL想落地,要在承认 AI 有幻觉的前提下设计方案

Text2SQL落地难,根源在于忽视“幻觉不可根除”这一事实。过度追求模型准确率,导致方案脱离现实。真正可行的路径是承认幻觉存在,通过“AI做语义转写、规则引擎生成查询、人机协同校验”的分层架构,将幻觉限制在可感知、可修正的前端,实现错误可见、可控、可修复。润乾NLQ实践表明:与幻觉共存,而非消灭它,才是企业级落地的关键。

Raqsoft 103

智能问数Text2SQL终于能做到工业级落地

大模型不直接写 SQL,只把问题翻译成“规范文本”,规则引擎再编译成 100% 准确的 SQL。真正烧钱的是后面的调优、修 bug、喂数据、养团队。Text2SQL 火了三年,就卷一个东西——准确率。85%、90%、95%,数字越来越好看。期望太高——又要聊天自然,又要多轮深挖,又要 AI 自己出结论。普通服务器,普通团队,照跑。润乾 NLQ,让智能问数从概念变成每个企业都装得起的工具箱。金融敢用了,医疗敢用了。所以十个智能问数项目,九个半都是一地鸡毛。润乾 NLQ,不卷准确率,卷确定性。

Raqsoft 150

病人手术后 6 月内咨询标记

"手术后 6 个月内有没有咨询过" 要靠拼接功能把咨询记录挂到手术记录上,挂的条件就是 "咨询日期不早于手术日期,且不晚于手术日期加 6 个月",这是一个跨表的区间条件,正好用得上拼接功能的非等值条件参数。条件是跨表的区间判断,写在条件参数里:带 @ 的 dt@ 是基准表(手术)的日期,不带 @ 的 dt 是关联表(咨询)的日期,整个条件读作 "咨询日期不早于手术日期,且不晚于手术日期往后 6 个月"。两份记录后面要分别用于不同的计算:手术记录当拼接的基准表,咨询记录当被挂上去的关联表。

Raqsoft 180

TRAE + SQLazy MCP 自动化编写 SQL 实践

回到开头的问题:学 SQL 到底难在哪?难在它要求你先把业务想法翻译成机器的叙述方式——join 的顺序、子查询的嵌套、窗口帧的定义,都是翻译过程里的损耗和出错点。SQLazy 把这层翻译工作接了过来:你用业务语言描述计算过程,它负责编译成标准 SQL 在数据库执行。而 TRAE + SQLazy MCP 又把写这一步也接了过去:你说需求,AI 生成 SQLazy 脚本、自动造测试数据、自动验证结果,连存量 SQL 都能一键倒译回来。三层简化叠加之后,做一次数据查询的路径变成了:打开TRAE →。

Raqsoft 201

一个 POC 搞 20 题?我写你麻辣鸽……祭出豆包撸 SPL,单机 20 倍干翻 MPP

MPPSPL硬件3节点,288核,1.8T内存,SSD单台虚拟机,16核,64G内存20 个校验总耗时17,450 秒(4.8 小时)624 秒(10 分钟)倍数快 27.9 倍288C1.8T 对 16C64G,4.8 小时对 10 分钟,SPL又一次完胜!(为什么要说“又”?这事前前后后也搞了两周多点,不过一多半时间都花在和客户沟通如何造出合理的测试数据,写 SPL 再调性能的时间也就三五天,这在以前是没法想的,豆包算是立了大功!

Raqsoft 144

股票最长连续上涨区间的起始日期和结束日期

因为上一步已经把记录限定在最长的那一段(或并列的几段)里,所以这里算出来的就是最长连续上涨区间的起始日期和结束日期。生成的组号写入新列 NoRisingDays,同一段内的记录组号相同,组号相同的一批记录就是一段连续的上涨行情。整个计算被拆成取数、排序、分段、计数、取最长、汇总六步,每一步都是一张可以直接查看的中间结果表:第 3 步之后能看到区间号,第 4 步之后能看到每段几天,最长的那一段是哪一段一目了然,不需要把整条 SQL 跑完再去猜中间算错了没有。比较用的是符号“=”,不是 SQL 里的“==”。

Raqsoft 205

调试 SQL 该像调试代码一样

写代码的人这时候会打开调试器,打断点,单步走,每个变量走到哪一步、变成多少,全看得一清二楚。这和调试 Python、Java 时用的单步是一个东西——区别只在于 SQLazy 的代码结构更简单,没有分支和循环以及子程序,所以用不着断点、step in、step out 那一套,每句代码本身就是一步。要求:按生产线分组,组内按时间排序后,找出编号等于指定字符串(比如 spL1ml82N4o)的记录,以及它们前后各 2 条记录,最后按 id 去重。步骤是给人看、给人调的,SQL 是给机器跑的,各管一段。

Raqsoft 175

CodeBuddy+SQLazy MCP,AI 开发可信 SQL 的正确姿势

对每天和 SQL 打交道的开发者、分析师而言,复杂 SQL 开发始终是效率卡点:多层嵌套逻辑靠人脑硬堆,调试靠逐层拆解子查询,祖传 SQL 接手像 “拆弹”,跨库适配纯又是体力重复。用 AI 写?快倒是真快了,但敢不敢信是另一回事了。说不准几时就出点幻觉、想要审计还得费劲读懂,刚才的麻烦事一件也少不了。写得虽快却不敢直接上生产。,既保留 AI 的效率优势,又守住工程化的可靠底线,让复杂 SQL 编写和审计成本直降 90%。

Raqsoft 176

N 位开发者测完这个无幻觉 SQL 编译器,我们从中学到了什么

SQLazy刚完成后的一段时间,我们把它交给一批开发者,用他们自己的查询来试。反馈整体是正面的,多数人上手就能把工作流跑通,编译出来的 SQL 直接拿去用了。被点名最多的是这几样:生成的 SQL 不瞎编字段、每一步都能点开看、以前不敢动的旧查询也能拿来重做。但痛点也收上来不少,有些还挺扎人。这些痛点不是一回事,我们分了两类看。比如只看重复行、不看唯一行这种取数需求,用去重功能的三个选项可以直接解决;判断一个值属不属于某个集合,用 contain 就行。这一类不用改代码,说清楚就够了。

Raqsoft 111

CodeBuddy+SQLazy MCP 自动化编写和倒译复杂 SQL 实践

** 订单明细 **: 订单 ID(数值), 零件 ID(数值), 供应商 ID(数值), 序号 (数值), 订购数量 (数值), 订购金额 (数值), 折扣率 (数值), 税率 (数值), 退货标记 (字符串), 发货状态 (字符串), 发货日期 (日期时间), 承诺到货日期 (日期时间), 实际到货日期 (日期时间), 发货指示 (字符串), 运输方式 (字符串)尾差由排序第一的国家承担。- ** 零件供应 **: 零件 ID(数值), 供应商 ID(数值), 库存量 (数值), 成本价 (数值)

Raqsoft 149

写SQL,“懒“ 是你这个季度做的最严谨的事

第一次看到 SQLazy 这个名字,不少人心里一合计:SQL 还能 lazy?这工具靠谱吗?我们起这名字时就知道会被问。名字里全是懒,我们天天挂嘴边的却是可审计、零幻觉、合规,听起来自相矛盾。其实懒和严是一件事的两面。

Raqsoft 161

我们造了一个编译器,让你不必再赌 AI 的正确率了

我们造了一个编译器,让你不必再赌 AI 的正确率了。不是因为模型不够准,而是行业一直让它做不该做的事。你有没有遇到过 AI 给的 SQL 能跑、能过检查,上线后才发现边界错了一行,却不报错?

Raqsoft 146

LLM 规划,编译器生成:以工作流为契约的确定性 SQL 生成器

AI已能产出可运行的 SQL,但可运行不等于可审计。在复杂查询上,公开评测显示最先进模型在 BIRD-bench 等基准上的执行准确率常在六成左右徘徊,远未达到可直接评审通过的程度。典型偏差不在语法,而在窗口边界、分区键、聚合口径等较复杂逻辑,尤以会话化、时序 gap、动态透视等场景尤甚。SQL需多层嵌套 CTE 与窗口累加,关联条件隐蔽,改一处需从最内层逐层验证是否影响外层。后果是三重成本叠加:难以 review、难以单步调试、难以跨库移植。

Raqsoft 376

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

为什么我不信任 AI 生成的 SQL 了,不是因为它不通,而是因为它太通——能过语法检查、能执行,却可能在不注意的地方漏洞百出。你有没有遇到过 AI 给的 SQL 通过了语法检查、执行不报错,数据却悄悄错了一行?

Raqsoft 212

SQLazy: 按配对顺序将刷卡进出记录转为单行记录

汇总时用条件聚合:action="IN" 时取 timestamp 的 max 作为 IN 列,action="OUT" 时取 timestamp 的 max 作为 OUT 列。正常情况下同一人同一建筑的记录成对出现, 先 IN 后 OUT;以 user-3/building-1 为例,原始序列为 OUT, IN, IN, IN, OUT, OUT, OUT,按配对规则切为 6 段:首条 OUT 落单,随后两条 IN 各落单,第四条 IN 与首条 OUT 配对成一行,最后两条 OUT 各落单。

Raqsoft 172

SQLazy 是不是你的 dbt 工作流里缺的那一环

dbt控制转换的管道,SQLazy进行分析逻辑的设计。两者不是替代关系,是互补。dbt 负责把分析结果物化、测试、文档化、追踪血缘;SQLazy 负责让你在写那些复杂分析 SQL 之前,先把逻辑想清楚、验证通过。如果你的 dbt model 里有一段超过 30 行的分析 SQL,试试先在 SQLazy 里拆成步骤。

Raqsoft 280

SQLazy: 取 ConfirmationStarted 之前最近的一条 Closed

现在要在每个 ID 内,找到 ConfirmationStarted 之前的所有的 Closed 中,离 ConfirmationStarted 最近的那条记录,取出记录的 ID 和时间字段。ConfirmationStarted 出现在 2022-07-13,此前的三条 Closed 分别发生在 05-28、06-18、06-25,离它最近的一条是 2022-06-25 05:59:01,正是期望结果中的时间。保证每个 ID 内部的记录按时间先后排列,后续分段和取 "最近" 才有依据。

Raqsoft 237

AI+SQLazy:从 SQL 小白到专家的神器

同一句话,每次问 AI,给你的 SQL 还不一样。改一次需求,它又生成一版全新的,之前看的、改的全都白费。AI 给的是成品,中间怎么算的你看不见,只能选择信,或者重来。从小白到专家,差的从来不是背语法,而是一套看得懂、改得动、传得下去的查询逻辑。SQLazy 给你这套逻辑,AI 帮你把话说明白,上手就能写,写着写着就成了专家。一句话能说清的需求,落到 SQL 里就是几十行嵌套,语法还挑剔,小白看一眼就想放弃。什么 CTE 嵌套,窗口函数,都不操心了,无论多复杂,编译器全搞定。同样的逻辑,输出永远一样。

Raqsoft 122

CodeBuddy+SQLazy 盘活历史复杂 SQL 详解

数据库里总有一些历史 SQL,上百行、多层嵌套,写的人早已不在,注释也几乎没有,接手的人要先花不少时间读懂,才谈得上修改。让 AI 直接改也不可靠,它生成的新 SQL 很可能与原 SQL 对不上,重新审一遍的功夫不比重写少。把这类 SQL 翻译成 SQLazy 分步脚本,逻辑就清楚多了:每一步做什么一目了然,能单独运行,中间结果随时可看,哪一步不对就改哪一步,调试和审计都有据可查;脚本本身就是文档,交接、传承直接交脚本。验证通过后还能编译成其他数据库的 SQL,换库只是重新编译一次。

Raqsoft 156
上一篇: SQLazy: 取 ConfirmationStarted 之前最近的一条 Closed
下一篇: SQLazy 是不是你的 dbt 工作流里缺的那一环
润乾软件
润乾软件 企业官方账号 企业官方账号
博客等级 码龄15年 153粉丝 1142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值