〖金仓数据库征文〗给金仓数据库写个“翻译官”:自然语言查询的笨办法
目录
一、为啥要搞这么个东西
我平时工作里经常要帮业务部门取数。他们不懂 SQL,每次都要先口述需求,我再写 SQL 去查,然后导成 Excel 发回去。一来一回,快的话十几分钟,慢的话得等半天。
后来我就想,有没有一种办法,让他们自己输入中文问题,系统自动转成 SQL 去查数据库?这样我就不用来回当传话筒了。
这篇文章不讲金仓怎么安装,只讲我怎么用 Python 连上金仓,然后做了一个极其简陋但能用的"自然语言查询助手"。
二、环境准备与安装简述
2.1 从下载到安装
先说下金仓数据库怎么来的啊。你打开金仓官网的那个主页,顶上一排导航栏里面有个“服务与支持”,鼠标移上去,下拉菜单里找到“下载中心”,点进去就行了。
然后下载中心里面东西还挺多的,你找 KES 那个分类,就是 KingbaseES 的缩写。根据你自己的操作系统选对应的安装包。我们这个场景用的是虚拟机,系统是 CentOS 7,所以选的是 Linux 版本,CPU 架构是 x86_64 的,就选那个对应 Linux x86_64 的 .iso 文件下载就行。


打开我们的VM,下载安装CentOS
注意这里我们直接选择最大内存

这里至少20GB

下载好了之后呢,把那个 .iso 文件传到虚拟机里去。具体用什么方式传都可以,我是直接拖拽进去的,放在“下载”文件夹下面。然后按官方文档里的安装步骤执行就行了,大概就是挂载 ISO、运行 setup.sh、然后跟着图形化向导一步步点下一步。

具体的安装流程我就不在这里详细展开了,金仓官方文档写得挺清楚的,跟着走基本不会卡住。如果你是在安装过程中遇到了具体报错啥的,可以看看社区里有没有人踩过同样的坑,或者直接参考官方手册。
2.2 建表和插入测试数据
数据库装完之后,我用自带的 KStudio 工具连上去,建了一个叫 test 的数据库,然后建了一张用户表 users。
表结构是这样的:
- id:整数类型,主键,自动增长
- name:字符串,存用户姓名
- age:整数,存年龄
- city:字符串,存所在城市
- salary:整数,存薪资
建表的 SQL 大概是这个样子的:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50),
age INT,
city VARCHAR(50),
salary INT
);
表建好之后,我往里面插了 20 条测试数据。这 20 条数据覆盖了不同城市、不同年龄和薪资范围,方便后面演示各种查询。
插数据的 SQL 我用的是批量插入的写法,一次性把 20 条全部写进去。具体数据就不在这儿全贴了,大致就是北京、上海、深圳、广州这些城市都有,年龄从 22 岁到 50 岁不等,薪资从 6500 到 16000 不等。
数据准备好之后,数据库这一侧的工作就算做完了。接下来就是写 Python 代码的环节。
2.3注意
有几个地方需要注意一下。
第一个是安装用户。 金仓官方推荐单独创建一个 kingbase 用户来运行数据库,不要直接用 root。这个其实也好理解,就是权限隔离嘛,万一数据库出点啥问题不会影响到系统级别的东西。
第二个是安装目录。 默认会装到 /opt/Kingbase/ES/V9 下面,我建议就用默认路径,省得后面找的时候还要到处翻。数据目录也在安装目录的子目录里,跟着默认走就行。
第三个是端口和密码。 安装过程中会弹出一个初始化的窗口,让你设置端口和管理员密码。金仓的默认端口是 54321,这个记住了,后面 Python 连接的时候要用。密码设一个自己能记住的,不用特别复杂,测试环境嘛。
三、核心思路:规则匹配代替大模型
现在 AI 很火,正经做法应该是接一个大模型,让模型把自然语言转成 SQL。但这个路子有几个麻烦的地方。
首先是得有 API Key,而且还得花钱。大模型调用不是免费的,虽然说单次调用不贵,但你要是一直在调、一直在试,累计起来也是一笔开销。
其次是网络环境有时候不太稳定。调用大模型的 API 得有网络,而且响应速度完全取决于接口那边的状况,有时候快有时候慢,体验不太一致。
还有就是模型输出不可控。你让它生成 SQL,它可能给你整出一些奇怪的 SQL,或者多解释几句废话,或者把 SQL 包在 Markdown 的代码块里,你还得额外处理一下才能拿去执行。
所以我就换了个思路——不用 AI,用规则匹配。
说白了就是:用户输入一句话,我用正则表达式去匹配关键词,匹配到了就生成对应的 SQL,匹配不到就查全部数据。
这样做的优点是简单、稳定、不花钱。代码写完之后,跑起来不会出幺蛾子,每一次执行结果都是确定的,不依赖外部服务。
缺点也比较明显——灵活性差,只能覆盖预设好的几种查询模式。如果用户问了一个不在规则列表里的问题,程序就不知道该怎么处理了,只能默认返回全部数据。
不过我的需求场景就那么几种,够用就行。
四、代码是怎么写的
4.1 连接数据库的部分
Python 连接金仓数据库用的是 psycopg2 这个驱动。为什么用这个呢?因为金仓数据库兼容 PostgreSQL 协议,而 psycopg2 是 SQL 的官方 Python 驱动,用起来很稳定。

连接配置我放在一个字典里:
DB_CFG = {
"host": "192.168.75.131", # 金仓数据库所在服务器的 IP
"port": 54321, # 金仓默认端口
"dbname": "test",
"user": "system",
"password": "123456"
}
这里面的几个参数解释一下。host 就是数据库服务器的 IP 地址,因为我数据库装在虚拟机里,所以填的是虚拟机的 IP。port 是金仓的默认端口 54321,这个在安装初始化的时候设过。dbname 是数据库名,user 是用户名,password 是对应的密码。
然后封装了一个获取连接的方法,后面所有查询都走这个方法。
4.2 核心函数:把中文问题转成 SQL
这个函数是整个程序的核心。它的逻辑其实不复杂——就是从上往下依次用正则去匹配用户输入的内容。
我先贴一下完整的函数代码,然后再拆开说每一块是干什么的:
def generate_sql(question):
"""把自然语言问题转成 SQL(规则匹配)"""
q = question.strip()
# 规则1:查询所有用户
if re.search(r'所有|全部|全部用户|所有用户', q):
return "SELECT * FROM users;"
# 规则2:年龄大于
match = re.search(r'(?:年龄)?\s*(大于|>)\s*(\d+)(?:\s*岁)?', q)
if match:
age = match.group(2)
return f"SELECT * FROM users WHERE age > {age};"
# 规则3:年龄小于
match = re.search(r'(?:年龄)?\s*(小于|<)\s*(\d+)(?:\s*岁)?', q)
if match:
age = match.group(2)
return f"SELECT * FROM users WHERE age < {age};"
# 规则4:城市查询
city_match = re.search(r'(?:在|位于|城市|来自)\s*(北京|上海|深圳|广州|杭州|成都|武汉|南京|西安|重庆|长沙|郑州|东莞|青岛|厦门|苏州|昆明|天津|大连|宁波)', q)
if city_match:
city = city_match.group(1)
return f"SELECT * FROM users WHERE city = '{city}';"
# 规则5:按姓名查询
match = re.search(r'查.*?([张李王刘陈杨赵黄周吴徐孙马朱胡郭林何高罗][\u4e00-\u9fa5]{0,2})', q)
if match:
name = match.group(1)
return f"SELECT * FROM users WHERE name = '{name}';"
# 规则6:最高薪资
if re.search(r'最高.*薪资|薪资.*最高|最高.*工资|工资.*最高|max', q):
return "SELECT * FROM users ORDER BY salary DESC LIMIT 5;"
# 规则7:平均薪资
if re.search(r'平均.*薪资|平均.*工资|avg', q):
return "SELECT city, AVG(salary) as avg_salary FROM users GROUP BY city ORDER BY avg_salary DESC;"
# 规则8:统计用户数
if re.search(r'多少.*用户|用户.*多少|总.*数|count', q):
return "SELECT COUNT(*) as total_users FROM users;"
# 默认:返回所有用户
return "SELECT * FROM users;"
这个函数从上往下执行,一旦某个规则匹配成功,就直接返回对应的 SQL 语句。
规则1 处理的是"查询所有"“全部”"所有用户"这类输入,直接返回 SELECT * FROM users,不加任何条件。
规则2 和 规则3 处理年龄过滤。正则里面匹配了"大于""小于"以及符号 > 和 <,后面跟一个数字,数字后面还可以跟一个"岁"字。匹配到之后就把数字取出来,拼到 WHERE 条件里去。
规则4 处理城市查询。这里要求输入里必须包含"在"“位于”“城市”“来自"这几个词之一,后面跟上城市名。城市名列表我是用 | 分隔的,这个写法在正则里表示"或者”,可以匹配到完整的多字词。
规则5 是按姓名查。这个正则稍微复杂一点,先匹配一个"查"字,然后取一个姓氏加上最多两个字的姓名。
规则6 到 规则8 分别处理最高薪资、平均薪资和统计总数。这三种都属于聚合类的查询。
4.3 执行查询和打印结果
查询执行就是标准的 psycopg2 操作。
先拿到连接,然后创建游标,用游标执行 SQL,取回所有的行数据,最后把游标和连接都关掉。
这里有个细节需要注意:游标和连接用完之后一定要关,不然会造成连接泄漏。特别是测试阶段,你频繁跑脚本,连接不关的话,一会儿数据库那边的连接数就满了。
结果打印我做了个简单的表格格式化。先拿到列名列表,然后动态计算每一列的最大宽度,这样不管内容是中文还是数字,表格都能对齐。打印出来的效果还算清楚,上面是列名,中间一条分隔线,下面是数据行。
4.4 程序的入口和交互逻辑
主函数是一个无限循环,每次循环让用户输入一个问题,然后调用 generate_sql 生成 SQL,再调用 run_query 去执行,最后用 print_result 把结果打印出来。
用户输入 exit 或者 quit 的时候,循环就结束,程序退出。
整个程序就是这个结构。不算复杂,但确实能用。
五、运行效果
下面是我实际跑出来的几个例子,都是真实截图。
5.1 查所有用户
输入"查询所有用户",程序匹配到规则1,生成 SELECT * FROM users;,返回全部 20 条记录。
从结果可以看到,20 条数据按照 id 顺序排下来,每条记录包含姓名、年龄、城市、薪资这几个字段。第一屏能看到前几条,后面还有更多。

5.2 按年龄过滤
输入"年龄大于30",程序匹配到规则2,生成 SELECT * FROM users WHERE age > 30;,返回年龄大于30的用户。
从结果可以看到,35岁的刘洋、40岁的杨磊、45岁的吴飞、50岁的何炅等都被查出来了。这个查询过滤掉了年龄小于等于30的用户,只返回符合条件的部分。

5.3 按城市查询
输入"在深圳",程序匹配到规则4,生成 SELECT * FROM users WHERE city = '深圳';,返回深圳的用户。
结果显示只有一条记录,王强,28岁,深圳,薪资11000。这说明数据里深圳就这一条,查询结果是准确的。

5.4 最高薪资
输入"最高薪资",程序匹配到规则6,生成 SELECT * FROM users ORDER BY salary DESC LIMIT 5;,返回薪资排前五的用户。
结果显示何炅(天津,16000)、吴飞(重庆,13000)、胡歌(厦门,12500)、刘洋(广州,12000)、朱婷(青岛,11500)排在前五位。

5.5 统计总数
输入"有多少用户",程序匹配到规则8,生成 SELECT COUNT(*) as total_users FROM users;,返回总数 20。
这个查询返回的是一个单行单列的结果,就是 20。

5.6 平均薪资
输入"平均薪资",程序匹配到规则7,生成 SELECT city, AVG(salary) as avg_salary FROM users GROUP BY city ORDER BY avg_salary DESC;,展示每个城市的平均薪资。
从结果可以看到不同城市的平均薪资对比。这个查询对于了解各地薪资水平分布还是有点参考价值的。

六、踩过的坑
说几个印象比较深的问题。
第一个是正则匹配顺序的问题。 年龄规则如果放在城市规则后面,"年龄大于30"里面的"大"字会被当成城市名去匹配。结果就生成了一句 WHERE city = '大',自然就查不到东西了。解决的办法就是把年龄规则提到最前面,先处理年龄相关的匹配。
第二个是城市名的匹配方式。 正则里的中括号 [北京上海] 是字符类,匹配的是"北京上海"这四个字符中的任意一个,而不是整个"北京"或者整个"上海"。必须用 (北京|上海) 这种分组交替的写法,才能匹配到完整的多字城市名。
第三个是编码问题。 中文字段名和数据在 Linux 下有时候会乱码。解决方法是在连接数据库的时候指定客户端编码为 UTF-8,这样中文就能正常显示了。
第四个是关于金仓的端口。 金仓数据库的默认端口是 54321,跟 PostgreSQL 的 5432 不一样。刚开始配置的时候我习惯性写了 5432,结果连不上,后来才反应过来是端口写错了。
这些问题都不是什么高深的技术难题,但遇到了确实挺耽误时间的。记录下来供大家参考。
七、后续还能怎么改进
目前这个版本只能处理预设好的几种查询模式。后续如果想让它更智能,可以考虑接入大模型 API,用真正的 NL2SQL 能力来替代规则匹配。
如果走大模型这条路的话,就不需要手动写这么多正则规则了。用户输入什么问题,直接丢给大模型,让模型去理解并生成 SQL。这种方式覆盖的场景会更广,灵活性也更高。
另外也可以加一个 Web 界面,做成一个简单的对话式查询工具,而不是在命令行里运行。这样业务部门的人直接用浏览器访问就行,不需要安装 Python 环境,用起来更方便。
不过话说回来,目前这个版本虽然简陋,但对于固定场景的查询需求来说,已经能省下不少沟通成本了。有时候解决问题不一定非要上最先进的技术,找到够用且稳定的方案就行。
八、总结
这篇文章记录了我用 Python 连接金仓数据库实现的一个简单自然语言查询工具。
整体思路就是三个步骤:
- 用户输入中文问题
- 程序用正则匹配关键词,生成对应的 SQL 语句
- 执行 SQL,把查询结果返回并打印给用户
整个流程我已经在本地实验环境里跑通了。因为用的是 Python,所以这个方案对操作系统没什么限制,Windows 和 Linux 下都能跑。连接金仓数据库用的是 psycopg2 驱动,这个驱动本身是给 PostgreSQL 用的,但因为金仓兼容 PostgreSQL 协议,所以直接用就行。
如果你也有类似的取数场景,不妨试试这个思路。不一定非得上大模型,规则匹配有时候反而是最省事、最稳定的方案。代码量不大,理解起来也不费劲,根据自己的需求改改规则就能用。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)