LLMSQL:给 WikiSQL 焕新,适配大模型时代的 Text-to-SQL 任务
咱程序员聊 Text-to-SQL,绕不开 WikiSQL 这个老数据集 —— 毕竟 8 万多组(问题 + SQL)对,还有 2 万多张维基百科来的表格,早年练手 NL2SQL 模型全靠它。但老归老,毛病是真不少,最近用大模型跑的时候尤其明显:一会儿字段大小写不统一导致查不到结果,一会儿数据类型对不上(比如数字存成带逗号的字符串),甚至有些问题明明 SQL 写对了,跑出来却是空表。更麻烦的是,它原来用数字占位符表示字段索引和聚合操作,比如用 “3” 代表 COUNT,大模型看了都懵,还得先转格式,实在不符合现在 LLM 直接生成 SQL 的习惯。

https://item.jd.com/15185906.html 京东链接,6折
《ChatBI核心技术》是刚上市的新书,本书旨在为读者提供一个全面的ChatBI学习框架。从基础概念到核心技术,再到实际应用场景,书中详细介绍了ChatBI的定义、特点、与传统BI的区别,以及其在企业决策支持、数据分析民主化、即时数据洞察等多场景中的应用。书中还深入探讨了提示工程、AI智能体、检索增强生成、大模型微调等关键技术,并通过实战案例展示了如何构建AI智能体和业务知识库,以及实现数据智能查询与可视化等功能。此外,书中还讨论了对话理解、智能分析、用户交互等重要环节,帮助读者全面掌握ChatBI的技术实现和应用思路。
所以波兰弗罗茨瓦夫理工大学的团队干脆搞了个大工程 —— 把 WikiSQL 彻底翻新,做成了 LLMSQL。这不是简单打补丁,而是从根上解决问题,最后出来的数据集既能保留原有的规模和多样性,又能直接给大模型用,不用再额外预处理。接下来咱就掰扯掰扯,他们到底怎么修的,还有用大模型测出来的效果到底怎么样。
先说说原来 WikiSQL 的坑有多具体。比如有 140 多张表缺列名,像某张记录英国首相的表,表头里有个空值,后来人工补上 “Prime Minister Ordinal Number (s)” 才正常;还有数据类型混乱的情况,比如 real 类型的字段里存着 “-2,123.9” 这种带逗号的字符串,得先把逗号去掉再转成浮点数,text 类型里塞整数的情况也得统一转成字符串;最头疼的是空结果 ——49.25% 的查询跑出来没数据,其中 41.22% 都是因为大小写不匹配,比如问题里写 “butler cc (ks)”,SQL 里却写成 “Butler CC (KS)”,跟表数据对不上。另外,原来的 SQL 格式是真反人类,比如 “sql: {"sel":5,"conds":[[6,0,"9ABX02"]],"agg":0}”,得自己对应 “sel=5 是第 5 列,agg=0 是无聚合”,大模型哪懂这暗号?
针对这些坑,团队的清理和重标注操作很实在。缺列名的 140 张表全人工补了,参考列里的数据内容来定名字,确保 SQL 能正常建表;数据类型冲突主要靠自动化脚本处理,比如把 “1224” 这种整数转成 text 类型的 “1224”,把 “628” 这种 int 存成的 real 改成 “628.0”,少数特殊情况才手动调;重复数据也清了 —— 只要两张表的列名、数据类型、行数据全一样就算重复,直接删掉重复表,把对应的问题归到保留的表上,重复的问题也直接删,最后剩下 25609 张表和 80330 个问题,比原来干净多了。
空结果的解决办法挺有意思,分了两步走。首先是让 SQL 里的字符串和问题里的大小写保持一致,比如问题里是 “butler cc (ks)”,SQL 里就改成一样的;如果还查不到,就生成所有可能的大小写组合去试,比如 “New York” 会试 “new York”“new york” 等各种情况,直到查到数据为止;要是还不行,就去对照表里面的实际数据,把 SQL 里的字符串改成和表中完全一致的大小写 —— 这招解决了 41.22% 的空结果问题,剩下 8.03% 暂时没搞定,留到后续研究了。
最关键的是 SQL 格式的改造,直接把原来的数字占位符换成了标准 SQL 语句。比如原来的 “sql: {"sel":5,"conds":[[6,0,"9ABX02"]],"agg":0}”,现在直接写成 “SELECT "Original air date" FROM "1-10088101-1" WHERE "Production code" = '9ABX02';”,字段名、表名全用引号括好,大模型一看就懂。这里的映射规则很明确,比如 agg 的 0 代表无聚合,1 是 MAX,2 是 MIN,3 是 COUNT,4 是 SUM,5 是 AVG,conds 里的 0 是 “=”,2 是 “<”,全换成了标准运算符,具体对应关系看下面这张表就行:
表 1:数值占位符与聚合 / 比较运算符的映射关系

不过有个问题还没彻底解决 —— 聚合运算符用错。比如有个问题问 “对手是 twins 时,有多少人出席?”,原来的 SQL 用了 COUNT ("Attendance"),但 Attendance 列存的是具体人数(比如 23849.0),明显应该用 SUM 才对。虽然改了运算符后,因为原来的字符串 “twins” 和表中的 “Twins” 大小写不匹配,跑出来还是空,但这说明聚合运算符的正确性需要结合问题语义判断,自动化处理难度大,暂时只能先放着。另外,关于 “是否要把所有查不到数据的查询都改好”,团队还在讨论,毕竟保留一些特殊情况也能让数据集更多样,目前这类查询占比大概 10%,不同聚合运算符的分布如下:
表 2:不同聚合运算符对应的空结果分布

清理完数据集,就得看大模型在 LLMSQL 上的表现了。团队测了不少模型,从 1B 参数的小模型到 685B 的大模型都有,还分了 0-shot、1-shot、5-shot 三种场景 —— 简单说就是不给例子、给 1 个例子、给 5 个例子让模型生成 SQL。评估标准是 “执行准确率”:模型生成的 SQL 跑出来的结果,行数、列数、数值都和标准答案一致才算对,用的是 SQLite 数据库,毕竟 WikiSQL 本来就适配它,而且 Python 自带,方便复现。
这里得提一下 prompt 设计,团队很贴心地加了表的样例行。比如给模型的 prompt 里,除了问题、列名、数据类型,还会加一行 “Sample row: ['Apple', 'iPhone 14', 899.99, '128GB', 'White']”,帮助模型理解表结构。而且严格规定了输出格式,只能有 SQL,不能加解释,表名固定用 “Table”,允许用的函数就 MAX、MIN、COUNT、SUM、AVG,运算符只有 =、>、<、!=,关键词也只准用 SELECT、WHERE、AND—— 主要是为了避免模型生成子查询、别名这些 LLMSQL 里没有的复杂结构,减少不必要的错误。
测试结果挺有意思,先看不同模型在三种场景下的表现,参数规模、准确率、评估时间都列得很清楚:
表 3:不同 LLM 在 LLMSQL 基准测试中的表现(按参数规模排序)

从这张表能看出不少门道:首先,参数规模不是唯一决定因素。比如 Gemma 3 4B(4.3B 参数)的 0-shot 准确率 60.9%,比 Mistral 7B(7.2B 参数)的 24.4% 高多了,说明模型架构和指令微调的影响很大。其次,给例子(few-shot)对小模型帮助特别大,比如 Phi 3.5 mini 从 0-shot 的 24.7% 涨到 5-shot 的 62.07%,提升了 151%,gpt-oss-20B 也从 47.3% 涨到 71.77%—— 小模型需要例子来理解任务边界,不然容易瞎生成复杂结构,比如用 SUBSTRING () 这种 SQLite 不支持的函数,直接导致执行失败。
再看小模型之间的差距,Llama 3.2 1B 和 Qwen 2.5 1.5B 参数差不多,但 Qwen 的 0-shot 20.6% 比 Llama 的 5.7% 高太多,5-shot 更是 53.41% 对 22.44%,这说明就算参数规模接近,训练数据质量和架构设计不一样,效果能差出一截。而大模型,尤其是 DeepSeek R1 0528 和 OpenAI o4-mini,表现就很稳,0-shot 就能到 85% 以上,5-shot 反而没涨多少,甚至 DeepSeek R1 还从 88.4% 降到 86.57%—— 这是因为大模型能快速理解 prompt 里的规则,不用依赖例子,给多了反而可能干扰。
除了直接用 prompt 测试,团队还做了微调实验。把 LLMSQL 按原 WikiSQL 的划分分成训练、验证、测试集,用交叉熵损失微调模型,看不同 epoch 下的准确率变化。微调的参数设置很统一:优化器是 adamw_bnb_8bit,学习率 4e-5,用余弦调度,batch size 根据模型容量在 16-128 之间调整,gpt-oss-20B 还用了 LoRA(rank=8,alpha=16),只微调线性层。
微调结果也挺有启发,看下面这张图(横轴是 epoch,纵轴是测试集的执行准确率):

图 1:不同模型在 5-shot 设置下,LLMSQL 测试集上的微调准确率变化
小模型微调后进步特别大,Gemma3 4B、Phi3.5 mini 这些都能冲到 90% 以上的准确率,说明微调能让小模型快速掌握 LLMSQL 的任务特点,比如正确使用聚合函数和运算符。但大模型进步有限,比如 gpt-oss-20B 微调后准确率一直在 78%-78.5% 左右,Gemma3 27B 也没到 90%—— 可能是因为大模型本身已经有较强的 SQL 能力,微调带来的提升有限,或者需要更久的训练周期(这次只跑了 3 个 epoch,而且没出现过拟合,说不定多跑几轮还能涨)。
可能有人会问,LLMSQL 只处理单表查询,没有 JOIN、子查询这些复杂操作,现在还有必要用吗?其实看实际场景就知道,真实业务里的 SQL 大多没那么复杂。比如 Uber 分析过 810 万条生产环境的 SQL,超过 62% 用了 JOIN,但用到 UNION、INTERSECT、EXCEPT 这些运算符的还不到 1%—— 也就是说,能把单表查询做好,已经能覆盖大部分实际需求。而且 LLMSQL 的优势在于 “干净” 和 “适配 LLM”,不用再处理原来的格式问题,能让研究者更专注于模型本身的能力提升,而不是数据集的预处理。
团队还规划了后续的改进方向,比如给现在没对应问题的表加新问题,增加 JOIN 查询让任务更复杂,加入日期、时间这些新数据类型,甚至支持多语言 —— 把问题和表结构翻译成其他语言,方便测试大模型的跨语言 Text-to-SQL 能力。另外,他们还想把其他不适合 LLM 的老数据集也按 LLMSQL 的标准改一改,整合到一起,形成一个更全面的基准测试集。
总的来说,LLMSQL 算是给老 WikiSQL 续了命,解决了原来的一堆坑,还专门适配了大模型的使用习惯。不管是用小模型微调练手,还是用大模型做基准测试,都挺合适。而且数据集后续会公开,到时候大家自己拿过来就能用,不用再重复造轮子做清理工作 —— 对做 Text-to-SQL 的程序员来说,这绝对是个好消息。
更多推荐

所有评论(0)