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

Text2SQL 火了三年,有一道行业级的坎始终迈不过去:多表关联(JOIN)。

演示环境里百发百中的工具,一进入企业真实数据环境就频频翻车。在 Spider 这类标准多表数据集上,主流大模型方案的执行准确率仅在 60%-82% 之间;而面对网状交织的企业级复杂模型时,复杂跨表场景的准确率甚至会跌破 30%。

现实是,绝大多数企业的业务数据都分散在几十上百张表里,80% 以上的日常查询都涉及多表关联。JOIN 问题解决不了,智能问数就只是演示玩具。

问题的根源,从来不是大模型不够聪明,而是技术方向走偏了:让概率性生成的 LLM 去处理要求精确的关联逻辑,本身就是一场赌博。润乾 NLQ 给出的解法,是用一套包含名为 DQL 的确定性编译架构,把 JOIN 逻辑从 LLM 的任务中彻底剥离,从机制上杜绝了关联错误的可能。

一、为什么 JOIN 是 Text2SQL 绕不开的死穴?

在拆解 DQL 之前,先要搞清楚:为什么参数越来越大的大模型,偏偏栽在看似基础的 JOIN 上?

1. 三重任务叠加,出错概率指数级上升

纯 LLM 端到端生成 SQL 的模式,本质是让 AI 一步到位解决三个完全不同的问题:

  • 理解自然语言背后的业务意图

  • 匹配对应的表、字段与关联关系

  • 编排正确的 JOIN 路径、连接类型与查询逻辑

这三步每一步都有独立的出错概率,最终准确率是三者的乘积。业务越复杂、数据表越多,准确率下跌的速度就越快。这就像让一个翻译同时兼任地图导航和交通调度,不出错才是反常。

2. 幻觉的致命性:错了也能跑出 “合理结果”

JOIN 错误最危险的地方,在于它通常不会触发报错。

少写一个关联条件、混淆 INNER JOIN 与 LEFT JOIN、搞错维度对齐方式,都能正常返回一个数值完整、看起来很合理的结果。普通业务用户没有能力校验 SQL 逻辑,很容易就被错误数据误导,进而造成决策偏差。这也是金融、医疗、制造等严肃业务场景不敢直接使用纯 AI 方案的核心原因。

3. 上下文瓶颈:装不下完整的关联图谱

企业级数据模型动辄几十上百张表,表间关系盘根错节。想让 LLM 生成正确的 JOIN,理论上要把所有表结构、关联关系、业务口径都塞进提示词里。

但大模型的上下文窗口始终有限,注入的表信息越多,模型对细节的捕捉能力就越差,遗漏和错配的概率反而越高。靠 RAG 检索相关表?本质还是概率匹配,依然无法保证关联路径 100% 正确。

二、DQL:把 JOIN 从 “AI 猜” 变成 “规则算”

面对同一个难题,DQL 走了一条完全不同的工程化路线:它不试图让 AI 学会怎么连表,而是把所有关联逻辑提前封装进语义层,用确定性的规则引擎编译生成 JOIN,全程和 AI 的概率生成无关。

DQL 全称Dimensional Query Language(维度查询语言),是一套基于维度建模思想设计的中间查询语言,核心定位是:屏蔽底层数据表的关联细节,用业务化的维度、指标视角描述查询需求。
简单来说:业务侧只需要说清楚 “要什么指标、按什么维度看”,DQL 负责自动把它翻译成正确的多表关联 SQL。两大核心设计系统性地解决了 Text2SQL 的 JOIN 痛点。

1. 外键属性化:一个点号,替代整段 JOIN

企业中最常见的关联场景,是多对一的外键引用:订单属于客户、员工属于部门、产品属于供应商。

传统 SQL 的思路是 “连接思维”:必须明确声明两张表按什么字段连接。比如查询 “员工姓名和所属部门名称”,SQL 必须写全关联逻辑:

SELECT e.name, d.dept_name
FROM employee e
JOIN department d ON e.dept_id = d.id

而 DQL 的思路是 “对象思维”:把关联表的字段当成主表的属性,用点号(.)直接访问。同样的需求,DQL 的写法是:

SELECT employee.name, employee.dept.name FROM employee

看起来只是写法简化,背后是思考范式的根本转变:

  • SQL 思维:我要把两张表按什么条件连起来

  • DQL 思维:我要取这个业务对象的哪个属性

所有的 JOIN 逻辑,都由 DQL 引擎根据预定义的语义层元数据自动生成。用 LEFT JOIN 还是 INNER JOIN、关联字段是哪个、是否需要别名,全部由规则决定,不会有任何偏差。

哪怕是跨越三四层表的长路径关联,处理方式也完全一致。比如查询 “订单对应的供应商所在省份”:

SELECT orders.order_id, orders.product.supplier.province FROM orders

系统会自动生成订单表→产品表→供应商表的三层关联 SQL,不需要人工干预,也不会出现路径遗漏或关联错误。

2. 按维对齐:多表聚合的终极解法

如果说外键属性化解决了 “单路径关联” 的基础场景,那 “按维对齐” 就是攻克了多表关联里最硬核的难题:多事实表的跨表维度对齐。

什么是多事实表对齐?比如一个典型的经营分析需求:统计各省的员工数量、产品数量和订单数量。

员工、产品、订单是三张完全独立的业务事实表,彼此之间没有直接的外键关联,只是都能通过维度表关联到 “省份” 这个公共维度。

用标准 SQL 实现这个需求,需要编写三段独立的聚合子查询,再通过 FULL JOIN 按省份对齐,还要用 COALESCE 处理维度缺失:

SELECT
  COALESCE(T_1.F_1, T_2.F_1, T_3.F_1) AS "省",
  T_1.F_2 AS "员工数",
  T_2.F_2 AS "产品数",
  T_3.F_2 AS "订单数"
FROM (
    SELECT T_1_2.PROVINCE AS F_1, COUNT(1) AS F_2
    FROM EMPLOYEE T_1_1
    LEFT JOIN CITY T_1_2 ON T_1_1.HOMECITY = T_1_2.CITYCODE
    GROUP BY T_1_2.PROVINCE
) T_1
FULL JOIN (
    SELECT T_2_3.PROVINCE AS F_1, COUNT(1) AS F_2
    FROM PRODUCT T_2_1
    LEFT JOIN SUPPLIER T_2_2 ON T_2_1.SUPPLIERID = T_2_2.SUPPLIERID
    LEFT JOIN CITY T_2_3 ON T_2_2.CITY = T_2_3.CITYCODE
    GROUP BY T_2_3.PROVINCE
) T_2 ON T_1.F_1 = T_2.F_1
FULL JOIN (
    SELECT T_3_2.PROVINCE AS F_1, COUNT(1) AS F_2
    FROM ORDERS T_3_1
    LEFT JOIN CITY T_3_2 ON T_3_1.SHIPCITY = T_3_2.CITYCODE
    GROUP BY T_3_2.PROVINCE
) T_3 ON COALESCE(T_1.F_1, T_2.F_1) = T_3.F_1

三段子查询 + 两次 FULL JOIN + 维度补全逻辑,哪怕是资深数据开发也容易写错细节。让 LLM 一次性生成完全正确的语句,概率极低。

而在 DQL 里,同样的需求只需要清晰描述 “按什么维度、统计哪些指标”:

SELECT
  EMPLOYEE.count(1) AS 员工数,
  PRODUCT.count(1) AS 产品数,
  ORDERS.count(1) AS 订单数
ON Province AS 省
FROM EMPLOYEE BY EMPLOYEE.HOMECITY.PROVINCE
FULL JOIN PRODUCT BY PRODUCT.SUPPLIER.CITY.PROVINCE
FULL JOIN ORDERS BY ORDERS.SHIPCITY.PROVINCE

核心逻辑只有一个:三张表各自独立聚合,统一向 Province 维度对齐。

DQL 引擎会自动识别这是多事实表对齐场景,自动生成子查询、FULL JOIN、维度合并的完整 SQL 逻辑。关联方式、对齐逻辑、缺失值处理全部由规则保证,结果 100% 准确。这就是确定性编译的价值:再复杂的关联逻辑,只要能被规则描述,就永远不会出错。

三、从自然语言到 SQL:DQL 的全链路定位

DQL 不是孤立存在的技术点,它是润乾 NLQ 整个确定性架构里承上启下的关键一环。完整的查询链路分为四层,DQL 承担了 “关联逻辑编译” 的核心职责:

自然语言 → 规范文本 → MQL → DQL → SQL

  1. 第一步:语义转写(LLM 负责)

用户的口语化提问,由 LLM 转写成标准化的规范文本。这是整条链路唯一用到 AI 的环节,任务被极度简化:只做自然语言到业务话术的文本转写,只要了解业务术语词汇,不需要懂数据库结构知识。

  1. 第二步:逻辑解析(NLQ 引擎负责)

规范文本通过预定义的业务词典解析,生成 MQL(模型查询语言)。MQL 只负责声明查询的维度、指标、过滤条件,描述 “查什么”,完全不涉及 “怎么连表” 的技术细节。

  1. 第三步:关联编译(DQL 引擎负责)

这是核心的转换环节。DQL 引擎根据语义层的元数据,把 MQL 里的维度、字段解析成完整的对象属性路径,处理外键关联和多表维度对齐,生成结构化的 DQL 语句。所有 JOIN 逻辑在这一步被确定性地编译出来。

  1. 第四步:SQL 生成(执行层负责)

最后,DQL 语句被编译成对应数据库的标准 SQL,提交到数据库执行。

整条链路里,从规范文本开始,后续所有步骤全部基于规则运行,没有任何概率性生成。LLM 的影响被严格限制在最前端的语义转写,而且输出的规范文本人类一眼就能看懂、可以人工确认,从根源上把幻觉关进了笼子里。

四、DQL 能覆盖的典型业务场景

依托外键属性化和按维对齐两种范式,DQL 可以系统性解决企业中绝大多数的关联查询场景,远不止基础的两表关联。下面对照标准 SQL 实现,可以直观感受到这类业务查询的技术复杂度。

场景 1:带多表条件的明细查询

业务提问:查询 2023 年北京发往青岛的订单明细

这个需求在业务视角非常直白:按时间、发货城市、收货城市三个条件筛选订单。但发货城市归属订单表、收货城市归属客户表,城市名称又存储在独立的维度表中,需要跨三层表关联才能完成过滤与字段输出。

基于 DQL 引擎,用户无需关心城市字段归属哪张表、关联键是什么、同一张维度表要不要起别名,只需用自然语言描述筛选条件,系统会自动通过外键属性路径解析并生成完整的关联逻辑。

最终生成的 SQL:

SELECT
  ord.ORDERID AS "订单编码",
  ord.SIGNDATE AS "签单日期",
  ord.SHIPDATE AS "发货日期",
  ord.RECEIVEDATE AS "收货日期",
  ord.AMOUNT AS "订单金额",
  ship_city.CITY AS "发货城市名称",
  receive_city.CITY AS "收货城市名称"
FROM ORDERS ord
LEFT JOIN CITY ship_city ON ord.SHIPCITY = ship_city.CITYCODE
LEFT JOIN CUSTOMER cust ON ord.CUSTOMERID = cust.CUSTID
LEFT JOIN CITY receive_city ON cust.CITYCODE = receive_city.CITYCODE
WHERE YEAR(ord.SHIPDATE) = 2023
  AND ship_city.CITY = '北京'
  AND receive_city.CITY = '青岛'

场景 2:关联 + 聚合 + 聚合后过滤

业务提问:找出订单总金额超过 20 万元的女员工

这个需求同时涉及多表关联、维度分组聚合、聚合后条件过滤三层逻辑,是 SQL 编写中非常容易出错的场景,很容易把 HAVING 条件误写进 WHERE 子句,或者遗漏分组字段。

DQL 引擎会自动区分行级过滤条件(性别)与聚合后过滤条件(金额阈值),自动匹配正确的执行层级,不会出现条件位置放错的语法与逻辑错误。

最终生成的 SQL :

SELECT 
  emp.NAME AS "员工姓名", 
  emp.GENDER AS "性别", 
  SUM(ord.AMOUNT) AS "订单总金额"
FROM EMPLOYEE emp
INNER JOIN ORDERS ord ON emp.EMPLOYEEID = ord.SALESPERSONID
WHERE emp.GENDER = '女'
GROUP BY emp.EMPLOYEEID, emp.NAME, emp.GENDER
HAVING SUM(ord.AMOUNT) > 200000

场景 3:多层嵌套的长路径关联

业务提问:列出订单编号、订单日期、产品名称以及供应商所在城市

这是典型的跨实体长链路查询:从订单明细表出发,依次关联订单主表、产品表、供应商表,跨越四层业务实体才能拿到全部字段。关联的表越多,关联顺序、关联类型、字段归属出错的概率就越高。

在 DQL 中,这类多层关联只需要通过「业务对象. 属性」的点号语法逐级描述即可,关联路径由语义层元数据自动推导,层级再多也不会出现关联遗漏或键值错配。

最终生成的 SQL :

SELECT
  od.ORDERID AS "订单编号",
  ord.SIGNDATE AS "订单日期",
  prod.PRODUCTNAME AS "产品名称",
  sup.NAME AS "供应商名称",
  sup.CITY AS "供应商所在城市"
FROM ORDERDETAIL od
LEFT JOIN ORDERS ord ON od.ORDERID = ord.ORDERID
LEFT JOIN PRODUCT prod ON od.PRODUCTID = prod.PRODUCTID
LEFT JOIN SUPPLIER sup ON prod.SUPPLIERID = sup.SUPPLIERID

五、DQL 的本质:用工程化替代概率游戏

聊到这里不难发现,DQL 的价值不是 “写出更简洁的 SQL”,而是从根本上改变了 Text2SQL 的解题思路:

  • 主流方案的逻辑是:让 AI 越来越聪明,争取 “猜对” JOIN

  • DQL 的逻辑是:把 JOIN 变成规则问题,根本不需要 “猜”

这种范式转变,带来了三个工业级落地的核心优势:

1. 100% 的确定性

只要语义层的表关系、字段映射配置正确,生成的关联逻辑就永远正确。不存在 “这次对、下次错” 的随机波动,查询结果可复现、可审计、可追溯,完全满足严肃业务场景的合规要求。

2. 极低的运维成本

不需要标注海量问答样本,不需要反复调优 Prompt,不需要堆 GPU 算力。包含 DQL 的 NLQ 引擎在普通 CPU 服务器上就能流畅运行,实施和运维成本只有纯 AI 方案的零头。

3. 可控的能力边界

能查什么、不能查什么,完全由语义层定义。业务能力的扩展就是词典和元数据的完善过程,清晰可控。不会出现 “有时候特别厉害、有时候犯低级错误” 的黑盒问题,IT 团队对系统有完全的掌控力。

Text2SQL 发展到今天,已经过了靠炫酷 Demo 惊艳市场的阶段。但是,真正能走进企业核心业务的方案,并不是 “最聪明” 的那个,而是 “最靠谱” 的那个。DQL 的出现,相当于给智能问数装上了一套工业级的传动系统:前端用 AI 降低使用门槛,让业务人员用自然语言就能提问;后端用规则保证结果准确,让数据输出经得起业务校验。把模糊的 AI 能力,装进了确定性的工程框架里。

这才是智能问数真正能落地的样子:不赌概率,只讲规则;不追求无所不能,而是保证说出来的,就一定对。