← 数据分析 Agent

Text-to-SQL 的表结构上下文该怎么组织?表关系要写到什么程度?

高频 中等 Text-to-SQL 原理与上下文 · 第 2 / 2 问 更新于 2026/09/29
Text-to-SQLSchema元数据Prompt
本题落地项目AI Agent数据分析平台

简化版

表结构上下文要按「表 → 字段」两层组织,并且只放当前数据集允许查询的表。表这一层写表名、表说明和关联关系;字段这一层写字段名、类型、中文说明、语义类型(维度 / 指标 / 时间 / 标识)、业务含义,必要时列出枚举值和单位。表关系必须写成完整的等式,比如 biz_order.user_id = biz_user.id,不能只写「通过 user_id 关联」——关联字段两边不一定同名,写得含糊,模型就会生成一个不存在的 biz_user.user_id。

详细版

组织表结构上下文的五条原则:

  1. 范围最小:只放当前业务场景用得到、且启用的表,无关表越多,模型越容易选错;
  2. 两层结构:表一层、字段一层,用 JSON 或清晰的列表组织,比整段自然语言更不容易漏看;
  3. 关系写全:关联条件写成 表.字段 = 表.字段 的完整等式,一对多关系要说明方向;
  4. 字段带语义:除了类型,还要写业务含义、语义类型,状态类字段列出每个码值的含义,金额写明单位;
  5. 口径单独放:「销售额」「客单价」这类业务指标不混在字段说明里,单独一段给出计算公式和 SQL 表达式。

不同写法的效果对比:

写法模型的典型反应
「订单表和用户表通过 user_id 关联」生成 biz_order.user_id = biz_user.user_id,用户表没有这一列,执行报错
「通过 biz_order.user_id = biz_user.id 关联用户」直接照抄等式,关联正确
字段只写 status int猜 1、2、3 的含义,过滤条件写错
字段写「订单状态:1 待支付,2 已支付,3 已退款」统计有效订单时自动加 status = 2

完整版教学

一、表结构上下文要回答模型的四个问题

模型拿到一个问题,要写出正确的 SQL,心里得有四个问题的答案:

① 有哪些表可以用?         → 表清单(只放允许查询的)
② 每张表是干什么的?       → 表说明
③ 表和表怎么连?           → 关联关系
④ 每一列是什么意思、怎么用? → 字段说明、语义类型、业务含义、码值、单位

上下文就是按这四个问题来组织的。少回答一个,模型就会在那一处自由发挥,而自由发挥的结果往往是「语法正确、业务错误」。

二、为什么要按「表 → 字段」两层组织

把所有信息写成一大段话,比如「订单表有订单号、用户 ID、金额……,用户表有……」,模型很容易把 A 表的字段张冠李戴到 B 表上。两层结构让每个字段明确归属到某张表:

[
  {
    "tableName": "biz_order",
    "tableComment": "订单主表",
    "relationDescription": "通过 biz_order.user_id = biz_user.id 关联用户",
    "fields": [
      {"fieldName": "order_status", "fieldType": "varchar",
       "semanticType": "维度", "businessMeaning": "订单状态:已支付、已退款"},
      {"fieldName": "pay_amount", "fieldType": "decimal(10,2)",
       "semanticType": "指标", "businessMeaning": "实际支付金额,单位元"}
    ]
  }
]

JSON、DDL(CREATE TABLE 语句加注释)、Markdown 表格都可以,关键是结构稳定、每个字段归属清楚。DDL 的好处是模型对它非常熟悉,缺点是很难自然地塞进业务含义;JSON 更适合附带语义信息。

三、表关系为什么必须写成完整等式

这是 Text-to-SQL 里最容易被忽略、却最常出错的一点。看两种写法:

含糊写法:biz_order 和 biz_user 通过 user_id 关联
完整写法:通过 biz_order.user_id = biz_user.id 关联用户

含糊写法下,模型读到「通过 user_id 关联」,很自然地写出:

join biz_user u on o.user_id = u.user_id   -- biz_user 根本没有 user_id 这一列

这条 SQL 能通过只读安全校验(它确实只是 select),却在执行时报 Unknown column 'u.user_id'。问题的根源在于:关联字段两边往往不同名,一边是外键 user_id,另一边是主键 id。完整等式把两边都写死,模型只需照抄,不需要推断。

易错点:表关系描述是会被原样送进 Prompt 的「代码」,不是给人看的备注。写备注的习惯(「通过 xx 关联」)在这里会直接变成 SQL 错误。

四、字段说明要写到什么程度

类型只告诉模型「这是个数字」,业务含义才告诉它「这个数字怎么用」。三类字段最需要补充:

字段类型要补充什么不补的后果
状态 / 枚举每个码值的含义过滤条件写错,把退款单算进销售额
金额 / 数量单位、是否含税、是否已扣优惠口径不一致,分和元混用
时间是下单时间还是支付时间,精确到日还是秒「上个月销售额」按下单时间算,和财务对不上

语义类型(维度 / 指标 / 时间 / 标识)也很有用:告诉模型哪些字段适合 group by(维度、时间),哪些适合 sum、avg(指标),哪些只是用来关联和去重(标识,比如订单号)。

五、范围控制:只放允许查询的表

上下文里的表越多,出错的机会越多。控制范围有两层:

  • 按业务场景分数据集:电商销售、课程订单、招聘岗位各是一个数据集,用户先选数据集,只把这个数据集的表交给模型;
  • 按启用状态过滤:临时停用的表、敏感表不进上下文,模型从源头上看不到。

算一笔账:一个数据集 3 张表、每张 8 个字段,每个字段描述约 40 个 Token,整段上下文约 1000 Token,模型看得清清楚楚;如果把全库 200 张表都塞进去,上下文膨胀到十万 Token 量级,不仅成本高,模型选表的准确率也会明显下降。表更多的场景,要先用检索把相关的表和字段挑出来,再交给模型。

六、口径为什么单独放一段

字段说明回答的是「这一列是什么」,指标口径回答的是「这个业务词怎么算」,两者不是一回事:

字段:pay_amount  实际支付金额
口径:销售额 = sum(pay_amount),只统计已支付订单
      客单价 = sum(pay_amount) / nullif(count(distinct order_no), 0)

「客单价」不是任何一个字段,它是两个指标的比值,还要处理分母为零。把这类口径直接给成 SQL 表达式,模型遇到「客单价」就照着用,不用自己推导,结果才能跨次一致。

七、上下文不是越全越好

给模型的信息要准确、必要,而不是越多越好:

  • 生成 SQL 需要完整的字段语义和口径;
  • 让模型拆分析计划时,只需要知道有哪些表、字段大概是什么类型、有哪些指标,给太细反而分散注意力;
  • 样例数据(每张表放两三行真实数据)能帮模型理解字段格式,但要注意脱敏,也会增加 Token。

同一套元数据,按任务裁剪成不同粒度的上下文,是比较成熟的做法。

八、常见误区与追问

  • 误区:表关系写「通过 user_id 关联」模型就懂。 关联字段两边经常不同名,含糊的描述会让模型生成不存在的列,必须写成完整等式。
  • 误区:给了字段类型就够了。 类型不告诉模型状态码的含义、金额的单位和时间的口径,这些才是算错的主要来源。
  • 误区:上下文里表越全越保险。 无关表越多,模型越容易选错表,Token 成本也越高,应只放当前场景允许查询的表。
  • 误区:指标口径可以写在字段说明里。 很多指标是多个字段的组合或比值,不对应任何单一字段,应该单独给出公式和 SQL 表达式。
  • 追问:表太多、塞不进上下文怎么办? 先按业务场景拆数据集;再大就对表和字段做语义检索,只把和问题相关的几张表交给模型。
  • 追问:DDL 和 JSON 哪种格式更好? DDL 模型更熟悉,JSON 更方便附带业务含义和语义类型;关键在于结构稳定、字段归属清楚。

九、加强记忆

表结构上下文要回答模型四个问题:有哪些表能用、每张表干什么、表和表怎么连、每一列什么意思。组织上按「表 → 字段」两层写成 JSON 或带注释的 DDL,防止字段张冠李戴。表关系一定写成 表.字段 = 表.字段 的完整等式,因为外键和主键常常不同名,「通过 user_id 关联」这种备注式写法会让模型生成不存在的列,而且能过只读校验、执行才报错。字段要补码值、单位和时间口径,语义类型区分维度、指标、时间、标识。范围上只放当前数据集启用的表,业务指标单独成段给出 SQL 表达式;不同任务按需裁剪上下文粒度。

项目实战落地

项目里怎么做的

《AI Agent数据分析平台》的表结构上下文由 buildDatasetSchema 按「数据集 → 表 → 字段」逐层组装:

  • 先查当前数据集下启用的数据表,每张表输出 tableName、tableComment、relationDescription;
  • 再按「数据集 + 数据表」查这张表的字段语义,每个字段输出 fieldName、fieldComment、fieldType、semanticType、businessMeaning、queryEnabled、aggregateEnabled,挂到表的 fields 数组下;
  • 整个数组转成 JSON 字符串,填进 SQL 生成模板的 {datasetSchema}。

预置的电商数据集里,关系说明都写成了完整等式,比如订单主表是「通过 biz_order.order_no = biz_order_item.order_no 关联订单明细;通过 biz_order.user_id = biz_user.id 关联用户」。教程里专门记录了反例:写成「通过 user_id 关联」时,模型会生成不存在的 biz_user.user_id,SQL 能通过只读校验,却在执行阶段报 bad SQL grammar。

为什么这样取舍

  • 关系说明原样进 Prompt。 这一列不是给人看的备注,而是模型生成关联条件的直接依据,所以管理端要求写成等式。
  • 可查询、可聚合作为语义信息交给模型。 它们告诉模型哪些字段适合查、适合聚合;项目的执行校验按表白名单把关,没有做字段级白名单,这一点在项目的面试文档里如实写明,面试时要按源码实际能力回答。

面试官还会追问

  • Agent 生成分析计划时用的数据集上下文,和生成 SQL 时用的一样吗?为什么要裁剪?
  • 字段标了「不可聚合」,模型还是对它做了 sum,项目在执行前拦得住吗?

学完《AI Agent数据分析平台》,上面这些追问你都会迎刃而解。

本题落地项目登峰造极AI Agent数据分析平台基于Agent、Text-to-SQL和安全查询执行,实现数据集管理、指标口径维护、分析意图识别、SQL纠错、图表推荐、数据解读和报告生成,适合BI分析落地、过程追踪和多轮会话。SpringbootSpringAIAgentText-to-SQLLLM源码+SQL喂饭学习教程配套面试文档环境安装文档项目运行文档 学习这个项目