这款 Text2SQL 技术为什么能对噩梦般的 JOIN 免疫
在 Text2SQL 领域,JOIN 一直是个“终极考场”。不少方案宣称在公开测试集上能达到 90% 以上的惊人准确率,但背后往往存在一个关键“水分”:许多测试是在预先做好的大宽表上进行的,规避了真实的、动态的多表关联挑战。一旦面对企业真实的、网状交织的数据环境,需要系统动态识别关联路径并生成正确的 JOIN 时,准确率便会经历“高台跳水”。在 Spider 这类标准多表数据集上,先进大语言模型(LLM)方案的执行准确率通常在 60%-82% 之间,而且需要投入极高成本。其中错误多集中在 JOIN 遗漏、关联错误等环节。这正是许多演示酷炫的工具难以在企业复杂场景中扎根的核心瓶颈。
行业也在探索从“概率生成”到“确定编译”的范式转变。主流路径如 Google、微软的方案,依赖更强大的 LLM 与检索增强生成(RAG)技术;而另一些研究思路(如 UNJOIN 框架)则尝试将多表模式“扁平化”为虚拟单表供 LLM 理解,再后置重构 JOIN。然而,这些方法的核心仍未摆脱对概率模型的依赖,将最复杂的关联逻辑暴露于 LLM 的“幻觉”风险之下。
润乾 NLQ 选择了一条不同的工程化路径。它没有让 AI 在 JOIN 迷宫中“猜路”,而是构建了一套基于“语义层”和确定性编译的架构,将复杂的关联逻辑转化为可预测、可验证的规则执行,从而在面对复杂 JOIN 时保持了稳定的高准确率。
为什么 JOIN 是 Text2SQL 的“噩梦”?
要理解润乾 NLQ 的突破,需看清当前主流技术路径的局限。问题核心在于,无论是端到端生成还是引入中间表示,其本质都依赖于 LLM 的概率性生成与泛化能力。模型根据所学到的海量模式和提示词,去“推算”出最可能的 SQL。
这种方法在简单查询上表现尚可,但面对业务中真实的复杂 JOIN 时,其短板暴露无遗。一个正确的多表关联查询,背后是严格的业务逻辑和精确的数据结构知识。然而,LLM 著名的“幻觉”在此是致命的——它可能自信地生成一个引用不存在字段或错误关联关系的 SQL。此外,企业的数据模型通常包含大量存在复杂关联的业务表,当试图将所有这些表的字段信息纳入上下文时,冗长的提示会使 LLM 难以兼顾全局、精确捕捉每一处关联细节,从而导致其无法可靠地推导出完整的关联图谱。
更根本的挑战在于,一般方法试图让 AI 直接解决一个复合问题:既要理解自然语言意图,又要精确匹配数据库模式,还要编排正确的 JOIN 逻辑。这就像让一个翻译同时兼任地图导航和交通调度,出错概率自然倍增。
举个例子,一个常见的业务需求是:“帮我列出各省的员工数量、产品数量和订单数量”。这听起来很直观。
但对应的 SQL 却是噩梦级的,能让许多 LLM“翻车”:
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
这段 SQL 的难点在于:
多表关联与维度对齐:需要从三个独立的事实表(employees/products/orders)分别聚合,再按省份维度对齐
缺失值处理:并非所有省份在三张表中都有数据,需要 FULL OUTER JOIN 和 COALESCE 处理
聚合层级匹配:需要在子查询中完成各自聚合,再在外部进行关联,而且子查询还要 JOIN,还不止一个。
让一个没有深刻理解企业数据关系的 LLM,一次性“蒙对”这种逻辑链,概率极低。它可能会错误地使用 INNER JOIN 导致数据丢失,或完全搞错对齐方式。这正是业界在复杂查询场景下准确率暴跌至 30% 左右的核心原因——模型是在“生成”,而非“理解”。
免疫原理:润乾 NLQ 的确定性编译架构
上面那个噩梦级的 SQL,其实是润乾 NLQ 从“各省的员工数量、产品数量和订单数量”这句话生成的(因为程序生成,里面的中间表名明显是没有业务意义的),Text2SQL 中的复杂 JOIN 在这里免疫了。
与大多数依赖 LLM 生成 SQL(或中间层)的方案不同,润乾 NLQ 构建了一套多层确定性编译架构,将 Text2SQL 这个复杂的认知问题,分解为“语义转写”、“逻辑编排”与“关联生成”多个可验证的工程阶段。其完整路径为:自然语言 → 规范文本 → MQL(模型查询语言) → DQL(关联查询语言) → SQL。它将最易出错的多表关联逻辑(JOIN),从依赖概率生成的 LLM 任务中彻底剥离,交由基于确定规则的 DQL 引擎处理。
DQL 消除关联
DQL(Dimensional Query Language)是此架构中解决 JOIN 难题的核心。其设计哲学是进行一场根本性的思维转换:从关心“表如何连接”的数据库思维,转向关心“想要什么数据”的对象思维。这主要通过两大核心机制实现:“外键属性化”与“按维对齐”。
1. 外键属性化:用点号(.)替代复杂 JOIN
DQL 将传统 SQL 中多对一的外键关联,抽象为类似“对象. 属性”的访问方式。例如,在传统 SQL 中,要查询“员工的部门名称”,需要明确地写出 SELECT department.name FROM employee JOIN department ON employee.department_id = department.id。而在 DQL 中,这可以直接表达为 employee.department.name。系统会根据预定义的语义层模型,将这个点号路径编译为正确的 LEFT JOIN 链。查询语句极度简洁,且符合人的直观思维,无需再记忆和编排复杂的表连接关系。
2. 按维对齐:处理“各自为政”的并行聚合
“各省员工数、产品数、订单数”这类需求,是“按维对齐”的经典场景。其特点是:需要从多个没有直接外键关联的事实表中分别聚合数据(如员工表、产品表、订单表),然后按照一个共同的维度(如“省”)将结果对齐展示。
在 SQL 中,这需要编写包含多个子查询和 FULL JOIN 的冗长语句,逻辑复杂且极易出错。而 DQL 的“按维对齐”机制则优雅地解决了此问题:它允许每个表作为独立的数据对象进行聚合,只需声明大家共同对准的维度即可,无需关心这些事实表彼此之间如何连接。
全流程解析
以“各省员工数、产品数、订单数”为例
第一步:自然语言 → 规范文本
用户的问法被规整为清晰的意图表述。这个过程可以由 LLM 辅助完成,但 LLM 的任务被大大简化了:它只需做“转写”,不需要理解数据结构。
第二步:规范文本 → MQL(声明逻辑意图)
NLQ 引擎会依据预先配置好的“业务词典”(里面定义了“省”、“员工”、“产品”、“订单”等概念具体对应哪些表、哪些字段),生成下面的 MQL 语句。MQL 的核心作用是声明分析维度和度量指标,而非描述具体关联。
SELECT
EMPLOYEE.count(1) AS 员工数,
PRODUCT.count(1) AS 产品数,
ORDERS.count(1) AS 订单数
ON Province AS 省
FROM EMPLOYEE
BY 籍贯省
JOIN PRODUCT
BY 供应商省
JOIN ORDERS
BY 发货省
MQL 仅表达了“按省对齐统计”的业务意图。
第三步:MQL → DQL(生成精确对象路径与对齐逻辑)
这是关键一步。DQL 引擎会执行两项核心转换:
外键属性化转换:将 MQL 中简化的 BY 字段,解析为完整的对象属性路径。这需要查询语义层模型。
-
BY 籍贯省 → BY EMPLOYEE.HOMECITY.PROVINCE (意为:员工. 籍贯所在城市. 所属省份)
-
BY 供应商省 → BY PRODUCT.SUPPLIER.CITY.PROVINCE (意为:产品. 供应商. 所在城市. 所属省份)
-
BY 发货省 → BY ORDERS.SHIPCITY.PROVINCE (意为:订单. 发货城市. 所属省份)
按维对齐建模:识别出这是一个多事实表按共同维度(Province)对齐的需求,并采用 FULL JOIN 进行组织。
转换后,会生成类似如下的 DQL 语句:
SELECT
EMPLOYEE.count(1),
PRODUCT.count(1),
ORDERS.count(1)
ON Province
FROM EMPLOYEE BY EMPLOYEE.HOMECITY.PROVINCE
FULL JOIN PRODUCT BY PRODUCT.SUPPLIER.CITY.PROVINCE
FULL JOIN ORDERS BY ORDERS.SHIPCITY.PROVINCE
这条 DQL 语句清晰地体现了对象思维:三个表独立统计,通过 FULL JOIN 并列,统一向 Province 维度对齐。点号语法描述了各自获取“省”信息的深层路径。
第四步:DQL → SQL(编译为数据库指令)
最终,DQL 引擎将包含了精确关联路径的 DQL 中间表示,确定性地编译为如前文所示的、包含多层子查询和 FULL JOIN 的复杂标准 SQL。整个转换链条从 MQL 开始便是确定性的,彻底杜绝了“幻觉”SQL 的产生。
更多实例
基于维度对齐和确定性编译的思想,润乾 NLQ 能够系统性地化解各类让 Text2SQL 方案棘手的 JOIN 难题。不仅能处理前文所述的跨表多维统计,还能轻松应对以下多种复杂关联查询场景:
带有聚合结果过滤的查询
业务查询:“找出订单总金额超过 20 万元的女员工。”
对应 SQL:
SELECT e.Name AS 员工姓名, e.Gender AS 性别, SUM(o.Amount) AS 订单金额
FROM Employees e
INNER JOIN Orders o ON e.EmployeeID = o.SalesPersonID
WHERE e.Gender = '女'
GROUP BY e.EmployeeID, e.Name, e.Gender
HAVING SUM(o.Amount) > 200000
包含时间智能的多表查询
业务查询:“去年北京发往青岛的订单”
对应 SQL:
SELECT
YEAR(T_1_1.SHIPDATE) AS "发货日期_Year",
T_1_1.SHIPCITY AS "发货城市",
T_1_2.CITYCODE AS "客户城市",
T_1_1.ORDERID AS "订单编码",
T_1_1.SIGNDATE AS "签单日期",
T_1_1.SHIPDATE AS "发货日期",
T_1_1.RECEIVEDATE AS "收货日期",
T_1_1.AMOUNT AS "订单金额"
FROM ORDERS T_1_1
LEFT JOIN CUSTOMER T_1_2 ON T_1_1.CUSTOMERID = T_1_2.CUSTID
WHERE (YEAR(T_1_1.SHIPDATE) = YEAR(DATEADD('yy', -1, NOW())))
AND (T_1_1.SHIPCITY = 30101)
AND (T_1_2.CITYCODE = 20201)
需要跨越多层表关联的查询
业务查询:“列出订单编号、日期、对应的产品名称以及供应商所在城市。”
对应 SQL:
SELECT
T_1_1.ORDERID AS "订单编码",
T_1_2.SHIPDATE AS "订单日期",
T_1_3.PRODUCTNAME AS "产品名称",
T_1_4.NAME AS "供应商名称",
T_1_4.CITY AS "供应商城市"
FROM ORDERDETAIL T_1_1
LEFT JOIN ORDERS T_1_2 ON T_1_1.ORDERID = T_1_2.ORDERID
LEFT JOIN PRODUCT T_1_3 ON T_1_1.PRODUCTID = T_1_3.PRODUCTID
LEFT JOIN SUPPLIER T_1_4 ON T_1_3.SUPPLIERID = T_1_4.SUPPLIERID
这些实例表明,无论是涉及聚合后过滤(HAVING)、时间智能与动态关联解析,还是跨越多个实体的长路径关联,润乾 NLQ 都能通过其确定性架构,将复杂的业务提问准确地编译为正确的多表 JOIN SQL。
润乾 NLQ 这套方案,说直白点,就是稳、省、简单、好接入。不靠 AI 去“猜”复杂的多表关联该怎么连,而是用一套可靠的规则引擎来“算”,所以结果非常准,不会出现时灵时不灵的情况。因为核心是规则计算而不是跑大模型,所以特别省钱,用普通的服务器就能跑起来,不用投钱买昂贵的显卡。整套系统也很轻便,能很容易地塞进现有的系统里,对于预算和 IT 能力有限的中小企业来说,是个能立刻用上、负担得起的智能问数工具。
