Text-to-SQL 的 Demo 很好做:给模型一张表的 schema,让它写 SQL,十有八九能写出来。但真正在企业里落地,挑战完全不同:几十张表、命名混乱的字段、复杂的业务规则、不同用户能看的数据范围不一样……这篇文章讲企业级 Text-to-SQL 真正要解决的工程问题。
一、 企业数据库****的真实情况
和教程里干净的示例表不同,企业真实数据库通常是这样的:
· 字段名是历史遗留的缩写:order_amt 是金额还是数量?cust_cd 是什么意思?
· 业务逻辑在代码里,不在数据库里:「活跃用户」的定义是 status=1 AND last_login > 30 天前,但表里没有注释
· 数据分散在多张表,需要复杂的 JOIN:算一个订单的完整信息需要 join 5 张表
· 同一个概念在不同表里有不同名字:customer 表、client 表、user 表,可能都指同一类主体
二、表结构理解:让模型真正读懂你的数据库
2.1 丰富的元数据注入
仅仅给模型 CREATE TABLE 语句是不够的。需要把业务语义也传进去:
| # 注入给模型的 schema 信息(比原始 DDL 丰富得多)TABLE_METADATA = {'orders': {'description': '订单主表,每行代表一笔已确认订单','columns': {'order_amt': '订单金额(元),含税','order_status': '状态:1=待支付 2=已支付 3=已取消 4=已完成','cust_id': '关联 customers.id',},'common_joins': ['customers ON orders.cust_id = customers.id'],'business_rules': ['退款订单 order_status=3 且 refund_amt > 0'],}} |
|---|
2.2 向量化表结构,按需检索
当数据库表数量很多(> 20 张),把所有 schema 都塞进 prompt 会超出 context 限制。解决方案:把每张表的 metadata 向量化存储,查询时先用自然语言问题检索「最相关的几张表」,只把这几张表的 schema 传给模型。
三、生成 SQL 的质量控制
3.1 自检环节
让模型生成 SQL 之后,别急着执行,先做自检:
| def validate_sql(sql: str, db_schema: dict) -> ValidationResult:# 1. 语法检查(用数据库的 EXPLAIN / PARSE)syntax_ok = db.explain(sql)# 2. 字段存在性检查columns_valid = check_column_exists(sql, db_schema)# 3. 危险操作检测is_safe = not contains_write_ops(sql) # 只允许 SELECT# 4. 复杂度检查(防止全表扫描)complexity_ok = check_has_where_clause(sql)return ValidationResult(syntax_ok, columns_valid, is_safe, complexity_ok) |
|---|
3.2 Few-shot 示例的作用
对于企业特有的 SQL 模式(比如你们公司统计活跃用户的固定写法),把正确示例放进 prompt 里比任何工程优化都有效。维护一个「问题-SQL 对照库」,每次查询时检索最相似的几个例子加入 prompt。
四、权限控制:这是最容易忽视的关键点
自然语言查询的一个风险:普通员工可能查到他无权看的数据。权限控制需要在 SQL 执行前介入:
· 行级权限: 销售人员只能查自己负责的客户订单,在 SQL 里自动追加 AND sales_rep_id = {current_user}
· 列级权限: 财务字段(如员工薪资)对非财务人员不可见,从 SELECT 子句中自动移除
· 表级权限: 某些表(如 HR 数据)对特定角色完全不可见,拦截访问请求
| def apply_row_level_security(sql: str, user: User) -> str:tables_accessed = extract_tables(sql)for table in tables_accessed:policy = get_rls_policy(table, user.role)if policy:sql = inject_where_clause(sql, policy.filter_expr(user))return sql |
|---|
五、结果呈现
原始 SQL 查询结果通常是二维表格,对非技术用户不友好。在数据返回后,可以加一步:用 LLM 把结果翻译成自然语言摘要,再附上原始表格供核验。
| 查询:「上个月哪个部门的报销金额最高?」SQL 结果:dept_name | total_amount研发部 | 128,400市场部 | 87,200...自然语言摘要:「上月报销金额最高的部门是研发部(12.84 万元),占全公司报销总额的 34%,主要集中在差旅和设备采购两类。」 | | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| 工程延伸
| 如果你的场景需要把自然语言数据查询能力集成进现有系统(而不是单独建一个查询界面),可以通过 API 接入带 Text-to-SQL 能力的工作流平台。ZGI(zgi.cn)的结构化数据增强模块内置了 Text-to-SQL 能力,支持跨表联查和复杂聚合,可通过 Webhook 接入现有 BI 工具。 |
|---|