前面已经讨论分阶段与多维分析。本章回到第 4 章的客户排名例子,保留 LLM 熟悉的 SQL 写法,仍然查询同一个语义模型。业务用户继续用自然语言提问,改变的是 LLM 提交请求的方式。
这条路线是:自然语言 → 语义 SQL → DSL 或内部查询计划 → 物理 SQL → 数据库执行。
以下语法仍是教学约定,重点看前后两个 SQL 分别面向什么,以及它们怎样转换。
第 1 章的 SQL 查询 invoices 和 customers,需要自己关联、筛选有效状态、把分转为元。
语义 SQL 可以改为查询 SalesInvoiceQueryModel。它代表第 4 章约定的业务查询面,公开客户 ID、客户名称、开票日期和单张有效发票金额;真实表名和关联方式由语义模型提供。
这张虚拟表由语义层的 SQL 入口识别,不要求复制数据或创建同名数据库视图。LLM 可以复用已有的 SQL 语法经验,但仍需读取查询元数据,确认字段含义与可用操作。
下面沿用第 4 章的语义字段:
SELECT
customerId,
customerName,
SUM(salesAmount) AS totalSales
FROM SalesInvoiceQueryModel
WHERE invoiceDate >= '2025-06-01'
AND invoiceDate < '2025-07-01'
GROUP BY customerId, customerName
ORDER BY totalSales DESC, customerId ASC
LIMIT 3;
它与第四章的 DSL 表达相同意图:按客户累计六月金额,降序取前三名。对照关系如下:
| SQL 部分 | 对应的查询表达 |
|---|---|
FROM SalesInvoiceQueryModel |
model 指定语义模型 |
SELECT |
columns,包含金额求和及别名 |
WHERE |
聚合前条件 slice |
GROUP BY |
groupBy |
ORDER BY |
orderBy |
LIMIT |
limit 指定返回条数 |
这里没有物理表 JOIN、有效状态条件或分转元表达式:这些规则仍由语义模型提供。SUM(salesAmount) 只负责把已经以元表达的单票金额按客户汇总。
flowchart LR
A[语义 SQL] --> B[解析、绑定与校验]
B --> C[DSL 或内部查询计划]
C --> D[编译物理 SQL]
D --> E[数据库执行]
解析器将 SELECT、WHERE 等结构整理成语法树(AST);绑定阶段确认表名对应哪个语义模型、字段是否公开,以及 totalSales 来自哪个表达式。
随后检查类型、聚合与权限,确定过滤和计算发生的阶段。上表只是本例的对应关系,不能把任意 SQL 都按关键词机械换成 JSON 键名。
本例可以转换为第 4 章的 JSON 请求,再沿用相同的执行规则;复杂查询可能直接形成内部计划,不一定生成公开的 JSON 文件。
本例的单票金额可以求和,第 3 章的平均每张发票金额则需要按当前分组重新计算,不能把各客户的平均值当作普通金额再次相加。
因此,语义 SQL 入口仍要保留指标依赖、聚合方式与计算上下文。计算指标怎样引用,也需要由查询协议说明。保留 SQL 写法可以减少语法适配的负担,业务定义仍需明确。
本例只涉及选列、行过滤、分组、求和、排序和返回条数。子查询、CTE、窗口函数、跨语义模型关联及自定义函数,还需要由入口明确声明支持范围。
SQL 语法合法,不代表该入口就能执行。遇到不支持的写法,应返回明确诊断;若现有 DSL 能等价表达,可以在含义和权限不变的前提下改写,否则说明暂不支持。
本章改变的是查询入口:LLM 用 SQL 表达意图,语义层仍依据同一套业务定义校验和编译。前一个 SQL 面向虚拟业务表,后一个面向物理数据库。
统一计划专题继续看两个入口怎样共用执行计划,避免各自维护一套业务规则。