NL2SQL智能问数的基本概念和实现路径教程

智能问数NL2SQL(Natural Language to SQL)是一种基于人工智能技术的数据查询解决方案,它能够将用户的自然语言查询自动转换为结构化查询语言(SQL),实现对数据库的智能查询。该技术结合了大语言模型的理解能力和数据库的结构化数据管理,为非技术用户提供便捷的数据查询方式。

资源下载

前置教程

如想快速掌握NL2SQL实现,你可能需要先完成以下前置教程:

1. NL2SQL技术简介

NL2SQL技术是指将自然语言描述转换为SQL查询语句的技术,它弥合了人类语言与机器语言之间的鸿沟,让非技术人员也能轻松查询数据库。

NL2SQL的核心优势

  • 降低使用门槛:无需学习SQL语法,用自然语言即可查询

  • 提高查询效率:快速生成复杂SQL语句,节省开发时间

  • 智能理解能力:能够理解模糊查询和复杂的业务逻辑

  • 多表关联支持:支持多表联合查询和复杂关系处理

  • 实时交互:支持即时查询和结果反馈

NL2SQL的应用场景

企业数据分析:

  • 业务人员快速查询销售数据

  • 管理层获取关键业务指标

  • 财务人员进行数据核对

教育培训领域:

  • 学生学习数据库查询

  • 教师演示数据查询原理

  • 培训机构进行SQL教学

智能客服系统:

  • 用户查询订单信息

  • 客服查询客户数据

  • 自动化报表生成

2. NL2SQL实现架构

2.1 技术架构概述

NL2SQL系统的核心架构包含以下几个主要组件:

核心组件结构

用户输入 → 大语言模型 → SQL生成 → 数据库查询 → 结果返回    ↓           ↓          ↓          ↓          ↓自然语言   语义理解    结构化查询   数据执行    格式化输出

技术栈说明

  • 前端界面:用户输入查询的自然语言

  • 大语言模型:负责语义理解和SQL生成

  • 查询引擎:执行生成的SQL语句

  • 数据库:存储和查询结构化数据

  • 结果处理器:格式化并返回查询结果

2.2 实现路径规划

实现版本

实现目标

技术特点

1.0 基础

单表查询支持<br>基础SQL语句生成<br>手动提示词配置

使用Ollama或LM Studio运行大模型<br>通过预置提示词指导模型生成SQL<br>手动将生成的SQL粘贴到数据库工具执行

2.0 进阶

集成Dify工作流<br>自动化SQL执行<br>Web界面交互

使用Dify构建工作流应用<br>集成HTTP请求自动执行SQL<br>构建本地Web服务处理数据库查询

3.0 高级

大量表结构管理<br>智能表结构识别<br>动态提示词生成

支持大量数据表的元数据管理<br>智能识别查询目标表<br>动态生成表结构信息与表关联关系

4.0 智能

更准确、更快速、更智能

会在后面的教程中详细介绍,敬请期待…

3. 基础环境准备

3.1 数据库环境配置

请参照WSL(Ubuntu)环境下安装MySQL服务及基本使用教程,并确保MySQL服务已启动,HeidiSQL已安装并能够成功连接数据

3.2 大模型环境配置

如使用本地大模型,请参照Windows环境下安装Ollama及本地部署大语言模型教程),并确保Ollama服务已启动,可以通过Cherry Studio与其对话。需要特殊说明的是,请尽量使用参数量大的LLM模型,否则生成的SQL语句准确性较差,影响查询甚至无法查询。如果是qwen系列模型,请使用qwen3:8b及以上模型

你也可以使用LLM的API服务,请参照大语言模型LLM服务商Api服务调用教程,并在Cherry Studio中配置相应的API服务

3.3 Dify环境配置

请参照Dify 本地部署安装与基于本地知识库实现RAG教程,并确保Dify服务已启动,Ollama或API服务已导入到Dify中作为LLM服务

3.3 数据库准备

3.3.1 创建示例数据库

使用HeidiSQL创建测试数据库:

-- 创建数据库CREATE DATABASE nl2sql CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 使用数据库USE nl2sql;-- 创建用户表CREATE TABLE `users` (    `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',    `name` VARCHAR(50) NOT NULL COMMENT '姓名' COLLATE 'utf8_unicode_ci',    `age` INT(11) NULL DEFAULT NULL COMMENT '年龄',    `hobby` VARCHAR(50) NULL DEFAULT NULL COMMENT '爱好' COLLATE 'utf8_unicode_ci',    `city` VARCHAR(50) NULL DEFAULT NULL COMMENT '所在城市' COLLATE 'utf8_unicode_ci',    `high_school_score` FLOAT(12) NULL DEFAULT NULL COMMENT '高考成绩',    `cutoff_score` FLOAT(12) NULL DEFAULT NULL COMMENT '分数线',    PRIMARY KEY (`id`) USING BTREE) COMMENT='用户表' COLLATE='utf8_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=5;-- 插入示例数据INSERT INTO `users` (`name`, `age`, `hobby`, `city`, `high_school_score`, `cutoff_score`) VALUES('张三', 25, '编程', '北京', 650.0, 500.0),('李四', 30, '篮球', '上海', 580.0, 450.0),('王五', 28, '音乐', '广州', 620.0, 480.0),('赵六', 22, '读书', '深圳', 680.0, 550.0);

3.3.2 导入数据库脚本

如果使用网盘提供的脚本文件:

  1. 在HeidiSQL中点击”文件” → “加载SQL文件”

  2. 选择网盘中的nl2sql.sql文件

  3. 点击”执行”按钮导入数据

  4. 验证数据表和数据的正确性

4. NL2SQL基础实现(1.0版本)

4.1 提示词工程

4.1.1 提示词设计原则

有效的提示词是NL2SQL成功的关键,需要包含以下核心要素:

提示词核心结构

  1. 角色定义:明确模型的角色定位

  2. 任务描述:清楚说明需要完成的任务

  3. 上下文信息:提供数据库表结构信息

  4. 输出格式:明确指定输出格式要求

  5. 约束条件:设置查询的约束和限制

4.1.2 基础提示词模板

## 角色你是一名专业的数据库数据查询人员## 工作内容你需要实现将你接收到的自然语言转换为MySQL查询语句,以便于用户能够查询数据库中的数据## 被查询的数据表结构CREATE TABLE `users` (    `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',    `name` VARCHAR(50) NOT NULL COMMENT '姓名' COLLATE 'utf8_unicode_ci',    `age` INT(11) NULL DEFAULT NULL COMMENT '年龄',    `hobby` VARCHAR(50) NULL DEFAULT NULL COMMENT '爱好' COLLATE 'utf8_unicode_ci',    `city` VARCHAR(50) NULL DEFAULT NULL COMMENT '所在城市' COLLATE 'utf8_unicode_ci',    `high_school_score` FLOAT(12) NULL DEFAULT NULL COMMENT '高考成绩',    `cutoff_score` FLOAT(12) NULL DEFAULT NULL COMMENT '分数线',    PRIMARY KEY (`id`) USING BTREE) COMMENT='用户表' COLLATE='utf8_unicode_ci' ENGINE=InnoDB AUTO_INCREMENT=5;## 可用的查询方法- 当用户询问"求和"或"总和"时,使用SUM()函数- 当用户询问"平均数"或"平均"时,使用AVG()函数- 当用户询问"最大值"时,使用MAX()函数- 当用户询问"最小值"时,使用MIN()函数- 当用户询问"计数"时,使用COUNT()函数## 输出要求1. 如果用户输入无法生成SQL语句,请回复:"抱歉,该命令无法形成数据库查询操作"2. 生成的SQL语句必须完整且可直接执行3. 对于字符串查询,使用LIKE操作符而不是等号4. 只输出SQL语句,不要包含任何其他内容

4.2 基础查询实现

4.2.1 配置Cherry Studio

  1. 打开Cherry Studio

  2. 点击右上角助手,右键编辑助手

  3. 提示词中粘贴上述提示词

  4. 保存配置

4.2.2 执行查询测试

基础查询示例

  1. 用户输入:张三几岁了

  2. 预期输出:SELECT age FROM users WHERE name LIKE '张三';

  3. 用户输入:所有用户的平均年龄

  4. 预期输出:SELECT AVG(age) FROM users;

  5. 用户输入:有多少个用户来自北京

  6. 预期输出:SELECT COUNT(*) FROM users WHERE city LIKE '北京';

4.2.3 查询结果验证

  1. 复制生成的SQL语句

  2. 在HeidiSQL中打开查询窗口

  3. 粘贴SQL语句并执行

  4. 验证查询结果的正确性

5. 进阶实现:Dify集成(2.0版本)

5.1 创建工作流应用

网盘中有工作流设计文件,可自行下载并导入Dify

  1. 登录Dify管理界面

  2. 点击”创建空白应用”

  3. 选择”工作流”类型

  4. 输入应用名称:智能问数NL2SQL

  5. 点击”创建”按钮

5.2 工作流设计

LLM节点配置

  1. 在工作流画布中添加LLM节点

  2. 配置模型参数:

    • 选择已配置的大模型

    • 设置系统提示词(使用4.1.2节的模板)

  3. 配置用户输入:

    • 在提示词末尾添加:开始/{x}content

HTTP请求节点配置

  1. 在LLM节点后添加HTTP请求节点

  2. 具体配置内容后文详细介绍

结束节点配置

  1. 添加结束节点

  2. 配置输出变量:

    • 设置输出内容为HTTP请求的响应体

    • text=http请求{x}body

5.3 Python Web服务开发

创建虚拟环境

# 使用Conda创建Python虚拟环境conda create -n nl2sql python=3.10conda activate nl2sql

安装依赖包

# 安装必要的依赖pip install fastapi uvicorn pymysql python-multipart python-dotenv

创建app.py文件

网盘中有本项目示例代码,可自行下载使用

下载该代码后,需要将.env.example文件重命名为.env,并根据实际情况修改数据库配置

from fastapi import FastAPI, Request, HTTPException, Formfrom fastapi.responses import PlainTextResponse, JSONResponseimport pymysqlimport reimport uvicornimport osfrom typing import Dict, Any, Optionalfrom pydantic import BaseModelfrom dotenv import load_dotenv# 加载环境变量load_dotenv()app = FastAPI(title="NL2SQL Query Service", version="2.0")# 数据库配置DB_CONFIG = {    'host': os.getenv('DB_HOST', '127.0.0.1'),    'port': int(os.getenv('DB_PORT', 3306)),    'user': os.getenv('DB_USER', 'root'),    'password': os.getenv('DB_PASSWORD', 'root'),    'charset': os.getenv('DB_CHARSET', 'utf8mb4')}# 定义请求模型class QueryRequest(BaseModel):    db_name: str    sql_query: strdef clean_sql_query(sql_query: str) -> str:    """清理SQL查询语句"""    if not sql_query:        return ""    # 处理多行SQL,提取最后一个有效SQL语句    lines = sql_query.split('\n\n')    sql_lines = [line.strip() for line in lines if line.strip()]    if not sql_lines:        return ""    # 取最后一个非空行作为SQL语句    clean_sql = sql_lines[-1]    # 清理代码块标记    clean_sql = re.sub(r'```sql\s*', '', clean_sql)    clean_sql = re.sub(r'```\s*', '', clean_sql)    # 移除多余的空白字符    clean_sql = clean_sql.strip()    return clean_sqldef execute_sql_query(db_name: str, sql_query: str) -> Dict[str, Any]:    """执行SQL查询并返回结果"""    connection = None    try:        # 建立数据库连接        connection = pymysql.connect(            database=db_name,            **DB_CONFIG        )        with connection.cursor(pymysql.cursors.DictCursor) as cursor:            # 执行SQL查询            cursor.execute(sql_query)            # 获取查询结果            result = cursor.fetchall()            # 获取列名            if result:                columns = list(result[0].keys())                return {                    'success': True,                    'columns': columns,                    'data': result,                    'row_count': len(result)                }            else:                # 对于无结果集的查询(如INSERT、UPDATE、DELETE)                return {                    'success': True,                    'message': '查询执行成功,无返回结果',                    'affected_rows': cursor.rowcount                }    except pymysql.Error as e:        return {            'success': False,            'error': f'数据库错误: {str(e)}'        }    except Exception as e:        return {            'success': False,            'error': f'系统错误: {str(e)}'        }    finally:        if connection:            connection.close()@app.post("/query")async def handle_query(request: QueryRequest):    """处理SQL查询请求"""    # 清理SQL查询    clean_sql = clean_sql_query(request.sql_query)    if not clean_sql:        raise HTTPException(status_code=400, detail="错误:无效的SQL查询语句")    # 执行SQL查询    result = execute_sql_query(request.db_name, clean_sql)    if result['success']:        return result    else:        raise HTTPException(status_code=500, detail=result['error'])@app.post("/query/form")async def handle_query_form(    db_name: str = Form(...),    sql_query: str = Form(...)):    """处理表单形式的SQL查询请求"""    # 清理SQL查询    clean_sql = clean_sql_query(sql_query)    if not clean_sql:        raise HTTPException(status_code=400, detail="错误:无效的SQL查询语句")    # 执行SQL查询    result = execute_sql_query(db_name, clean_sql)    if result['success']:        return result    else:        raise HTTPException(status_code=500, detail=result['error'])@app.get("/health")async def health_check():    """健康检查接口"""    return {        'status': 'healthy',        'service': 'NL2SQL Query Service',        'version': '2.0'    }@app.post("/format_text", response_class=PlainTextResponse)async def format_text_output(    db_name: str = Form(...),    sql_query: str = Form(...)):    """返回格式化的文本输出(兼容原有格式)"""    # 清理SQL查询    clean_sql = clean_sql_query(sql_query)    if not clean_sql:        raise HTTPException(status_code=400, detail="错误:无效的SQL查询语句")    # 执行SQL查询    result = execute_sql_query(db_name, clean_sql)    if not result['success']:        raise HTTPException(status_code=500, detail=result['error'])    # 格式化输出为文本    if 'data' in result and result['data']:        output_lines = []        # 输出表头        output_lines.append(" | ".join(result['columns']))        output_lines.append("-" * 50)        # 输出数据行        for row in result['data']:            row_data = []            for col in result['columns']:                value = row.get(col)                row_data.append('NULL' if value is None else str(value))            output_lines.append(" | ".join(row_data))        output_lines.append(f"\n共查询到 {result['row_count']} 条记录")        return "\n".join(output_lines) + "\n"    else:        return "查询结果为空\n"@app.post("/format_text/json")async def format_text_output_json(request: QueryRequest):    """返回格式化的文本输出(JSON格式)"""    # 清理SQL查询    clean_sql = clean_sql_query(request.sql_query)    if not clean_sql:        raise HTTPException(status_code=400, detail="错误:无效的SQL查询语句")    # 执行SQL查询    result = execute_sql_query(request.db_name, clean_sql)    if not result['success']:        raise HTTPException(status_code=500, detail=result['error'])    # 格式化输出为文本    if 'data' in result and result['data']:        output_lines = []        # 输出表头        output_lines.append(" | ".join(result['columns']))        output_lines.append("-" * 50)        # 输出数据行        for row in result['data']:            row_data = []            for col in result['columns']:                value = row.get(col)                row_data.append('NULL' if value is None else str(value))            output_lines.append(" | ".join(row_data))        output_lines.append(f"\n共查询到 {result['row_count']} 条记录")        return {"text": "\n".join(output_lines) + "\n"}    else:        return {"text": "查询结果为空\n"}if __name__ == '__main__':    print("启动NL2SQL查询服务...")    print("服务地址: http://localhost:8000")    print("健康检查: http://localhost:8000/health")    print("查询接口: http://localhost:8000/query (JSON格式)")    print("查询接口(表单): http://localhost:8000/query/form (表单格式)")    print("文本接口: http://localhost:8000/format_text (文本格式)")    print("文本接口(JSON): http://localhost:8000/format_text/json (JSON格式)")    # 启动FastAPI应用    uvicorn.run(        "app:app",        host="0.0.0.0",        port=8000,        reload=False    )

启动服务

# 确保虚拟环境已激活python app.py
  1. 确保Python虚拟环境已激活

  2. app.py文件放置到项目目录

  3. 确保MySQL服务正常运行

  4. 启动Python服务:python app.py

  5. 服务将在端口8000上运行

5.4 工作流配置更新

更新Dify工作流中的HTTP请求配置:

  1. 在Dify工作流中,找到HTTP请求节点

  2. 修改请求参数:

    • 方法:POST

    • URLhttp://host.docker.internal:8000/query(JSON格式)或http://host.docker.internal:8000/format_text(文本格式)

    • Content-Typeapplication/json

  3. 配置请求体(JSON格式):

    {  "db_name": "nl2sql",  "sql_query": "{{#LLM.text#}}"}

注意:FastAPI提供了两种格式的接口:

  • /query:接受JSON格式请求

  • /query/form:接受表单格式请求

  • /format_text:返回纯文本格式(接受表单数据)

  • /format_text/json:返回JSON格式的文本(接受JSON数据)

发布和运行

  1. 在Dify中点击”发布”

  2. 选择”更新”应用

  3. 点击”运行”按钮

  4. 在弹出的对话框中输入测试查询

查询测试

  1. 输入:张三几岁了

  2. 预期流程:

    • LLM生成:SELECT age FROM users WHERE name LIKE '张三';

    • HTTP请求执行查询

    • 返回JSON格式结果:

      {  "success": true,  "columns": ["age"],  "data": [{"age": 25}],  "row_count": 1}
  3. 输入:所有用户的平均高考成绩

  4. 预期流程:

    • LLM生成:SELECT AVG(high_school_score) FROM users;

    • HTTP请求执行查询

    • 返回结果:

      {  "success": true,  "columns": ["AVG(high_school_score)"],  "data": [{"AVG(high_school_score)": 632.5}],  "row_count": 1}

6. 高级实现:多表及关联表查询(3.0版本)

6.1 数据表元数据管理

元数据表与其它关联表设计:

网盘中有元数据表设计文件,可自行下载并导入数据库,名称为nl2sql_higher.sql

创建存储表结构信息的元数据表:

--  nl2sql 的数据库结构CREATE DATABASE IF NOT EXISTS `nl2sql` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;USE `nl2sql`;-- 表 nl2sql.data_table_structure 结构CREATE TABLE IF NOT EXISTS `data_table_structure` (  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',  `table_name` varchar(50) NOT NULL COMMENT '数据表名',  `table_structure` text NOT NULL COMMENT '数据表结构',  `description` varchar(200) DEFAULT NULL COMMENT '表描述',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='数据表结构元数据';-- 表  nl2sql.data_table_structure 的数据:~3 rows (大约)DELETE FROM `data_table_structure`;INSERT INTO `data_table_structure` (`id`, `table_name`, `table_structure`, `description`) VALUES    (1, 'users', 'CREATE TABLE `users` (`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT \'ID\', `name` VARCHAR(50) NOT NULL COMMENT \'姓名\', `age` INT(11) NULL DEFAULT NULL COMMENT \'年龄\', `hobby` VARCHAR(50) NULL DEFAULT NULL COMMENT \'爱好\', `city` VARCHAR(50) NULL DEFAULT NULL COMMENT \'所在城市\', `high_school_score` FLOAT NULL DEFAULT NULL COMMENT \'高考成绩\', `cutoff_score` FLOAT NULL DEFAULT NULL COMMENT \'分数线\', PRIMARY KEY (`id`) USING BTREE) COMMENT=\'用户表\' COLLATE=\'utf8_unicode_ci\' ENGINE=InnoDB AUTO_INCREMENT=5;', '用户信息表'),    (2, 'orders', 'CREATE TABLE `orders` (\r\n    `id` INT(11) NULL DEFAULT NULL COMMENT \'ID\',\r\n    `goods_id` INT(11) NULL DEFAULT NULL COMMENT \'货品ID\',\r\n    `user_id` INT(11) NULL DEFAULT NULL COMMENT \'下单用户ID\',\r\n    `date` DATE NULL DEFAULT NULL COMMENT \'下单日期\'\r\n)\r\nCOMMENT=\'订单\'\r\nCOLLATE=\'utf8_unicode_ci\'\r\nENGINE=InnoDB\r\n;\r\ngoods_id对应下述goods表结构的id字段\r\nCREATE TABLE `goods` (\r\n    `id` INT(11) NOT NULL COMMENT \'ID\',\r\n    `name` VARCHAR(50) NOT NULL DEFAULT \'\' COMMENT \'名称\' COLLATE \'utf8_unicode_ci\'\r\n)\r\nCOMMENT=\'商品\'\r\nCOLLATE=\'utf8_unicode_ci\'\r\nENGINE=InnoDB\r\n;\r\nuser_id对应下述users表结构的id字段\r\nCREATE TABLE `users` (\r\n    `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT \'ID\',\r\n    `name` VARCHAR(50) NOT NULL COMMENT \'姓名\' COLLATE \'utf8_unicode_ci\',\r\n    `age` INT(11) NULL DEFAULT NULL COMMENT \'年龄\',\r\n    `hobby` VARCHAR(50) NULL DEFAULT NULL COMMENT \'爱好\' COLLATE \'utf8_unicode_ci\',\r\n    `city` VARCHAR(50) NULL DEFAULT NULL COMMENT \'所在城市\' COLLATE \'utf8_unicode_ci\',\r\n    `high_school_score` FLOAT NULL DEFAULT NULL COMMENT \'高考成绩\',\r\n    `cutoff_score` FLOAT NULL DEFAULT NULL COMMENT \'分数线\',\r\n    PRIMARY KEY (`id`) USING BTREE\r\n)\r\nCOMMENT=\'用户表\'\r\nCOLLATE=\'utf8_unicode_ci\'\r\nENGINE=InnoDB\r\nAUTO_INCREMENT=5\r\n;', '订单信息表'),    (3, 'goods', 'CREATE TABLE `goods` (`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT \'ID\', `name` VARCHAR(100) NOT NULL COMMENT \'商品名称\', `price` DECIMAL(10,2) NULL DEFAULT NULL COMMENT \'商品价格\', `category` VARCHAR(50) NULL DEFAULT NULL COMMENT \'商品类别\', PRIMARY KEY (`id`) USING BTREE) COMMENT=\'商品表\' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;', '商品信息表');-- 表 nl2sql.goods 结构CREATE TABLE IF NOT EXISTS `goods` (  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',  `name` varchar(100) NOT NULL COMMENT '商品名称',  `price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',  `category` varchar(50) DEFAULT NULL COMMENT '商品类别',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';-- 表  nl2sql.goods 的数据:~3 rows (大约)DELETE FROM `goods`;INSERT INTO `goods` (`id`, `name`, `price`, `category`) VALUES    (1, '商品A', 100.00, '类别1'),    (2, '商品B', 200.00, '类别2'),    (3, '商品C', 300.00, '类别1');-- 表 nl2sql.orders 结构CREATE TABLE IF NOT EXISTS `orders` (  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',  `user_id` int DEFAULT NULL COMMENT '用户ID',  `goods_id` int DEFAULT NULL COMMENT '商品ID',  `order_date` date DEFAULT NULL COMMENT '下单日期',  `amount` decimal(10,2) DEFAULT NULL COMMENT '订单金额',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单表';-- 表  nl2sql.orders 的数据:~3 rows (大约)DELETE FROM `orders`;INSERT INTO `orders` (`id`, `user_id`, `goods_id`, `order_date`, `amount`) VALUES    (1, 1, 1, '2023-01-01', 100.00),    (2, 2, 2, '2023-01-02', 200.00),    (3, 3, 3, '2023-01-03', 300.00);-- 表 nl2sql.users 结构CREATE TABLE IF NOT EXISTS `users` (  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',  `name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL COMMENT '姓名',  `age` int DEFAULT NULL COMMENT '年龄',  `hobby` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL COMMENT '爱好',  `city` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL COMMENT '所在城市',  `high_school_score` float DEFAULT NULL COMMENT '高考成绩',  `cutoff_score` float DEFAULT NULL COMMENT '分数线',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='用户表';-- 表  nl2sql.users 的数据:~4 rows (大约)DELETE FROM `users`;INSERT INTO `users` (`id`, `name`, `age`, `hobby`, `city`, `high_school_score`, `cutoff_score`) VALUES    (1, '张三', 25, '编程', '北京', 650, 500),    (2, '李四', 30, '篮球', '上海', 580, 450),    (3, '王五', 28, '音乐', '广州', 620, 480),    (4, '赵六', 22, '读书', '深圳', 680, 550);

动态提示词生成

网盘中有Dify工作流设计文件,可自行下载并导入导入,名称为nl2sql_higher.yml

修改提示词模板,支持动态获取表结构信息:

## 角色你是一名数据库管理人员## 工作内容你需要根据"{{#context#}}"的判定主体是什么,并根据我给出的下面的数据表结构内容生成查询对应table_name(表名)的table_structure字段中内容的mysql查询语句## 你可用于判定主体的数据表的表名为| id | table_name | comment || --- | --- | --- || 1 | users | 用户表 || 2 | orders | 订单表 || 3 | goods| 产品表 |## 被查询的数据表的结构CREATE TABLE `data_table_structure` (    `id` VARCHAR(50) NULL DEFAULT NULL COMMENT 'ID',    `table_name` VARCHAR(50) NULL DEFAULT NULL COMMENT '数据表名',    `table_structure` VARCHAR(50) NULL DEFAULT NULL COMMENT '数据表结构'    )    COMMENT='数据表结构'    COLLATE='utf8_unicode_ci';## 主要目的你生成的sql语句不是直接用于查询数据而是获取完整的数据表名、数据表结构的信息例如:我输入:张三几岁了你输出:SELECT table_structure FROM data_table_structure WHERE table_name = 'users';## 注意1.你要查询表结构的表名为"data_table_structure"。

在原有HTTP请求节点添加LLM模型调用节点,用于调用LLM模型生成SQL查询语句,并设置提示词如下:

## 角色你是一名数据库数据查询人员## 工作内容你需要将"{{#context#}}"转换为SQL查询语句去MySql数据库中查找数据,请根据下方表结构准确判断用户所需要查询的字段信息## 被查询的数据表的表结构{{#1746794342206.body#}}## 你可以使用的其他方法用户输入类似于“求和”或“总和”时,则在sql语句中使用SUM()。用户输入类似于“平均数”或“平均”时,在在sql语句中使用AVG()。## 要求1.如果用户输入的内容无法生成为sql语句,请直接说“抱歉,该命令无法形成数据库查询操作”。2.当可以生成sql语句时,请确保输出的内容为完整正确的sql语句,不要输出此外的其他任何字符,确保你生成的内容用户可以直接执行查询操作。3.对于字符串内容的查询请使用LIKE操作而不是等于操作。4.请不要在回复中包括除sql语句之外的任何内容。

表结构识别阶段

首先识别用户查询涉及的数据表:

## 第一阶段:表结构识别你需要根据用户查询内容,识别出需要查询的数据表,并获取该表的完整结构信息。例如:用户输入:"张三的订单信息"你需要识别出需要查询users表和orders表,并获取这两个表的结构信息

SQL生成阶段

基于识别出的表结构生成最终的SQL查询:

## 第二阶段:SQL生成基于第一阶段获取的表结构信息,生成完整的SQL查询语句要求:1. 正确处理表关联关系2. 使用适当的JOIN语句3. 确保字段名称和表名称的准确性

6.3 测试多表关联查询

为了验证系统的多表关联查询功能,我们可以测试以下查询:

  • 查询所有用户的订单信息(包括用户姓名、订单ID、订单金额等)

  • 查询用户张三的所有订单信息(包括订单ID、订单金额、订单日期等)

  • 查询所有订单中金额大于200的订单信息(包括订单ID、订单金额、订单日期等)

7. 总结

通过本教程,您应该掌握了智能问数NL2SQL系统的完整实现路径,从基础的单表查询到复杂的多表关联查询。由于篇幅限制,我们将在后面的教程中通过智能体的方式实现智能问数的智能阶段的实现。

7.1 核心技能掌握

  • 提示词工程:设计有效的提示词指导大模型生成SQL

  • 数据库设计:合理设计表结构和关联关系

  • 工作流集成:使用Dify构建完整的查询流程

  • Web服务开发:构建HTTP API处理查询请求

  • 多表关联:实现复杂的多表联合查询

7.2 技术发展趋势

随着大语言模型技术的不断发展,NL2SQL技术将呈现以下趋势:

  • 理解能力增强:更准确地理解复杂查询意图

  • 多数据库支持:支持更多类型的数据库系统

  • 实时学习能力:根据用户反馈持续优化查询质量

  • 可视化集成:与数据可视化工具深度集成

7.3 应用扩展

基于本教程的基础,您可以进一步扩展系统功能:

  • 权限管理:实现基于用户角色的数据访问控制

  • 查询历史:记录和管理用户的查询历史

  • 结果缓存:提高常用查询的响应速度

  • 可视化展示:将查询结果以图表形式展示

  • API接口:提供RESTful API供外部系统集成

通过掌握NL2SQL技术,您将能够构建智能化的数据查询系统,大大降低数据分析的技术门槛,让更多用户能够便捷地获取和分析数据。


NL2SQL智能问数的基本概念和实现路径教程
http://localhost:8090//archives/zhi-neng-wen-shu-nl2sqlde-ji-ben-gai-nian-he-shi-xian-lu-jing-jiao-cheng
作者
昊昱天合
发布于
2025年11月24日
更新于
2025年11月24日
许可协议