提升 Text2SQL 准确率

提升 Text2SQL 准确率

摘要

随着大语言模型的爆发,Text2SQL(自然语言转SQL)技术正在重塑我们与数据库的交互方式。本文将系统性地梳理提升 Text2SQL 准确率的核心方法,涵盖提示工程、模型微调、推理增强三大维度。所有示例基于微软 AdventureWorksDW2016 数据仓库

引言:为什么 Text2SQL 准确率如此重要?

在数据驱动的时代,让非技术用户能够通过自然语言查询数据库,是一个极具价值但也极具挑战的任务。传统的 Text2SQL 系统准确率有限,难以投入实际应用。但随着 GPT-4、Llama、Qwen 等大模型的出现,这一领域迎来了革命性的突破。

目前业界领先的系统在 Spider 数据集上已能达到 82.5% 的执行准确率,但如何在实际业务场景中达到甚至超越这个水平?本文将从五个核心维度展开:

  1. Schema 设计与呈现 - 让模型更好地理解数据库结构
  2. 提示工程优化 - 设计高效的提示词策略
  3. 业务上下文配置 - 消除业务语言与技术语言的鸿沟
  4. 模型微调方法 - 针对特定任务优化模型能力
  5. 推理时增强策略 - 在推理阶段提升准确率

本文使用的是微软SQLServer官方的示例库,使用的是数据仓库的示例库AdventureWorksDW,我学习数据仓库的时候一直都参考这个库,大家可以很容易在微软的SQLServer官方网站上下载到这个库。


一、Schema 设计与呈现:打好地基

1.1 J-Schema:更友好的数据库结构呈现

传统 Schema 往往只提供表名和字段名,这对于模型理解业务语义是不够的。J-Schema 方法提出了一种更优秀的呈现方式:

示例:DimCustomer 表(客户维度表)

复制代码
表名: DimCustomer (客户维度表)
字段:
  - CustomerKey (客户代理键, 主键, 自增)
  - GeographyKey (地理代理键, 外键 → DimGeography.GeographyKey)
  - CustomerAlternateKey (客户业务标识, 如会员编号)
  - FirstName (名)
  - LastName (姓)
  - BirthDate (出生日期, 格式: YYYY-MM-DD)
  - MaritalStatus (婚姻状态, 可选值: S-单身/M-已婚)
  - Gender (性别, 可选值: M-男/F-女)
  - EmailAddress (电子邮箱)
  - YearlyIncome (年收入, 单位: 美元)
  - TotalChildren (子女总数)
  - EnglishEducation (教育程度, 可选值: Bachelors/Graduate/High School/Partial College/Partial High School)
  - EnglishOccupation (职业, 如: Professional/Management/Clerical 等)
  - AddressLine1 (地址第一行)
  - Phone (电话号码)
  - DateFirstPurchase (首次购买日期)
  - CommuteDistance (通勤距离, 如: 0-1 Miles/1-2 Miles/5-10 Miles)
  
示例值:
  - CustomerKey: 11000, 11001
  - FirstName: Jon, Yang
  - LastName: Yang, Zhu
  - BirthDate: 1972-01-12
  - MaritalStatus: S
  - Gender: M
  - YearlyIncome: 90000.00
  - EnglishEducation: Bachelors
  - EnglishOccupation: Professional

Key points

  • ✅ 提供字段语义描述
  • ✅ 说明主外键关系
  • ✅ 给出字段格式和可选值
  • ✅ 提供示例数据(帮助模型理解数据特征)

1.2 表关联关系管理

多表查询是 Text2SQL 的难点之一。清晰定义表关联关系,能让模型生成更准确的 JOIN 语句:

AdventureWorksDW2016 核心表关联关系

json 复制代码
{
  "relationships": [
    {
      "from_table": "FactInternetSales",
      "from_column": "CustomerKey",
      "to_table": "DimCustomer",
      "to_column": "CustomerKey",
      "relation_type": "many_to_one",
      "description": "一个客户可以有多个互联网销售订单"
    },
    {
      "from_table": "FactInternetSales",
      "from_column": "ProductKey",
      "to_table": "DimProduct",
      "to_column": "ProductKey",
      "relation_type": "many_to_one",
      "description": "一个产品可以被多次销售"
    },
    {
      "from_table": "FactInternetSales",
      "from_column": "OrderDateKey",
      "to_table": "DimDate",
      "to_column": "DateKey",
      "relation_type": "many_to_one",
      "description": "订单日期关联到日期维度"
    },
    {
      "from_table": "DimCustomer",
      "from_column": "GeographyKey",
      "to_table": "DimGeography",
      "to_column": "GeographyKey",
      "relation_type": "many_to_one",
      "description": "客户关联到地理位置"
    },
    {
      "from_table": "DimProduct",
      "from_column": "ProductSubcategoryKey",
      "to_table": "DimProductSubcategory",
      "to_column": "ProductSubcategoryKey",
      "relation_type": "many_to_one",
      "description": "产品属于某个子类别"
    },
    {
      "from_table": "DimProductSubcategory",
      "from_column": "ProductCategoryKey",
      "to_table": "DimProductCategory",
      "to_column": "ProductCategoryKey",
      "relation_type": "many_to_one",
      "description": "子类别属于某个大类"
    },
    {
      "from_table": "FactResellerSales",
      "from_column": "ResellerKey",
      "to_table": "DimReseller",
      "to_column": "ResellerKey",
      "relation_type": "many_to_one",
      "description": "经销商销售关联到经销商维度"
    },
    {
      "from_table": "FactResellerSales",
      "from_column": "EmployeeKey",
      "to_table": "DimEmployee",
      "to_column": "EmployeeKey",
      "relation_type": "many_to_one",
      "description": "经销商销售关联到员工(销售代表)"
    }
  ]
}

如果建表脚本已经包含了这些信息,可以让大模型帮助生成这些关联信息。


二、提示工程优化:引导模型思考

2.1 思维链(Chain-of-Thought)引导

复杂查询需要多步推理。通过思维链提示,引导模型逐步分析,以下是一个具体的示例:

示例:查询 2014 年每个产品类别的销售额

复制代码
问题: 查询2014年每个产品类别的总销售额,按销售额降序排列

思考过程:
1. 确定时间范围: 2014年 → DimDate.CalendarYear = 2014
2. 需要的字段: 产品类别名、销售额总计
3. 涉及的表: 
   - FactInternetSales (销售事实表)
   - DimProduct (产品维度)
   - DimProductSubcategory (产品子类别)
   - DimProductCategory (产品类别)
   - DimDate (日期维度)
4. 关联条件:
   - FactInternetSales.ProductKey = DimProduct.ProductKey
   - DimProduct.ProductSubcategoryKey = DimProductSubcategory.ProductSubcategoryKey
   - DimProductSubcategory.ProductCategoryKey = DimProductCategory.ProductCategoryKey
   - FactInternetSales.OrderDateKey = DimDate.DateKey
5. 聚合逻辑: 按 EnglishProductCategoryName 分组, SUM(SalesAmount) 计算总额
6. 排序: ORDER BY 总销售额 DESC

SQL:
SELECT 
    dc.EnglishProductCategoryName AS ProductCategory,
    SUM(fis.SalesAmount) AS TotalSalesAmount
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    INNER JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
    INNER JOIN DimDate dd ON fis.OrderDateKey = dd.DateKey
WHERE dd.CalendarYear = 2014
GROUP BY dc.EnglishProductCategoryName
ORDER BY TotalSalesAmount DESC;

2.2 Few-Shot 示例库

针对复杂、高频的查询场景,提供示例 SQL:

json 复制代码
examples = [
    {
        "question": "查询每个产品类别的销售数量",
        "sql": """
SELECT 
    dc.EnglishProductCategoryName,
    COUNT(*) AS SalesCount
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    INNER JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
GROUP BY dc.EnglishProductCategoryName
ORDER BY SalesCount DESC;
        """
    },
    {
        "question": "查找最近30天未下单的客户",
        "sql": """
SELECT 
    dc.FirstName,
    dc.LastName,
    dc.EmailAddress
FROM DimCustomer dc
WHERE dc.CustomerKey NOT IN (
    SELECT DISTINCT CustomerKey 
    FROM FactInternetSales 
    WHERE OrderDate >= DATEADD(DAY, -30, GETDATE())
);
        """
    },
    {
        "question": "查询每个地区的销售额和订单数",
        "sql": """
SELECT 
    dst.SalesTerritoryRegion,
    dst.SalesTerritoryCountry,
    COUNT(DISTINCT fis.SalesOrderNumber) AS OrderCount,
    SUM(fis.SalesAmount) AS TotalSales
FROM FactInternetSales fis
    INNER JOIN DimSalesTerritory dst ON fis.SalesTerritoryKey = dst.SalesTerritoryKey
GROUP BY dst.SalesTerritoryRegion, dst.SalesTerritoryCountry
ORDER BY TotalSales DESC;
        """
    },
    {
        "question": "查询年收入前10的客户及其购买总额",
        "sql": """
SELECT TOP 10
    dc.FirstName,
    dc.LastName,
    dc.YearlyIncome,
    SUM(fis.SalesAmount) AS TotalPurchases
FROM DimCustomer dc
    INNER JOIN FactInternetSales fis ON dc.CustomerKey = fis.CustomerKey
GROUP BY dc.CustomerKey, dc.FirstName, dc.LastName, dc.YearlyIncome
ORDER BY dc.YearlyIncome DESC;
        """
    }
]

示例 SQL 的价值

  • 帮助模型"举一反三",将示例应用到类似场景
  • 规范 SQL 风格和最佳实践
  • 提升复杂查询的生成质量

2.3 自定义提示词规则

定义全局适用的业务规则:

复制代码
全局规则:
1. 所有金额字段统一使用 money 类型,注意精度问题
2. 时间范围查询优先使用 DateKey 关联 DimDate 表
3. 产品相关查询需要考虑产品层次结构: Product → Subcategory → Category
4. 客户相关查询注意 GeographyKey 关联地理位置信息
5. 销售数据有两个来源: FactInternetSales (互联网销售) 和 FactResellerSales (经销商销售)
6. 日期字段有三种: OrderDate (下单日期), DueDate (到期日期), ShipDate (发货日期)
7. 金额计算注意货币类型: CurrencyKey 关联 DimCurrency
8. 促销活动: PromotionKey 关联 DimPromotion

三、业务上下文配置:让模型"懂业务"

3.1 术语配置:业务语言 → 技术语言

业务人员说的"大客户",数据库里可能是 YearlyIncome > 100000。术语配置是关键桥梁:

json 复制代码
{
  "terminology": [
    {
      "business_term": "大客户",
      "technical_mapping": "YearlyIncome > 100000",
      "description": "年收入超过10万美元的客户"
    },
    {
      "business_term": "互联网销售",
      "technical_mapping": "FROM FactInternetSales",
      "description": "通过网站直接销售给客户的订单"
    },
    {
      "business_term": "经销商销售",
      "technical_mapping": "FROM FactResellerSales",
      "description": "通过经销商渠道的销售"
    },
    {
      "business_term": "北美地区",
      "technical_mapping": "SalesTerritoryGroup = 'North America'",
      "description": "北美销售区域(美国、加拿大)"
    },
    {
      "business_term": "欧洲地区",
      "technical_mapping": "SalesTerritoryGroup = 'Europe'",
      "description": "欧洲销售区域"
    },
    {
      "business_term": "太平洋地区",
      "technical_mapping": "SalesTerritoryGroup = 'Pacific'",
      "description": "太平洋销售区域(澳大利亚等)"
    },
    {
      "business_term": "自行车类产品",
      "technical_mapping": "EnglishProductCategoryName = 'Bikes'",
      "description": "自行车类别产品"
    },
    {
      "business_term": "配件类产品",
      "technical_mapping": "EnglishProductCategoryName = 'Accessories'",
      "description": "配件类别产品"
    },
    {
      "business_term": "服装类产品",
      "technical_mapping": "EnglishProductCategoryName = 'Clothing'",
      "description": "服装类别产品"
    },
    {
      "business_term": "促销订单",
      "technical_mapping": "PromotionKey <> 1",
      "description": "参与了促销活动的订单(PromotionKey=1 表示无促销)"
    }
  ]
}

3.2 字段描述增强

清晰的字段描述比字段名更重要:

示例:FactInternetSales 表字段描述

sql 复制代码
-- ❌ 不好的做法(仅有字段名)
CREATE TABLE FactInternetSales(
    ProductKey int,
    OrderDateKey int,
    CustomerKey int,
    SalesAmount money,
    ...
);

-- ✅ 好的做法(包含详细描述)
-- 销售事实表:记录互联网销售订单明细
CREATE TABLE FactInternetSales(
    ProductKey int,              -- 产品代理键,关联 DimProduct
    OrderDateKey int,            -- 订单日期键,关联 DimDate(订单创建日期)
    DueDateKey int,              -- 到期日期键,关联 DimDate(应付日期)
    ShipDateKey int,             -- 发货日期键,关联 DimDate(实际发货日期)
    CustomerKey int,             -- 客户代理键,关联 DimCustomer
    PromotionKey int,            -- 促销代理键,关联 DimPromotion(1=无促销)
    CurrencyKey int,             -- 货币代理键,关联 DimCurrency
    SalesTerritoryKey int,       -- 销售区域代理键,关联 DimSalesTerritory
    SalesOrderNumber nvarchar(20), -- 销售订单号(业务主键)
    SalesOrderLineNumber tinyint, -- 订单行号(与 SalesOrderNumber 组合为主键)
    RevisionNumber tinyint,      -- 订单修订版本号
    OrderQuantity smallint,      -- 订购数量
    UnitPrice money,             -- 单价
    ExtendedAmount money,        -- 扩展金额 = UnitPrice × OrderQuantity
    UnitPriceDiscountPct float,  -- 单价折扣百分比
    DiscountAmount float,        -- 折扣金额
    ProductStandardCost money,   -- 产品标准成本
    TotalProductCost money,      -- 产品总成本
    SalesAmount money,           -- 销售金额(实际收入)
    TaxAmt money,                -- 税额
    Freight money,               -- 运费
    CarrierTrackingNumber nvarchar(25), -- 物流跟踪号
    CustomerPONumber nvarchar(25),      -- 客户采购订单号
    OrderDate datetime,          -- 订单日期(冗余字段,便于查询)
    DueDate datetime,            -- 到期日期(冗余字段)
    ShipDate datetime,           -- 发货日期(冗余字段)
    ...
);

四、模型微调方法:从通用到专用

4.1 为什么需要微调?

通用大模型虽然能力强大,但在特定领域的 Text2SQL 任务上,仍可能存在:

  • 不理解特定业务术语(如"太平洋地区"对应哪个 SalesTerritoryGroup)
  • 生成的 SQL 不符合数据仓库规范(如忘记关联 DimDate)
  • 复杂查询准确率不足(如多表关联、层次结构查询)

微调可以让模型更好地适应特定场景。

4.2 DB-GPT-Hub 微调实战

DB-GPT-Hub 是一个专注于 Text-to-SQL 微调的开源项目,在 Spider 数据集上达到了 78.9% 的执行准确率(超过 GPT-4 的 76.2%)。

微调流程(以 AdventureWorksDW2016 为例)

复制代码
1. 数据准备
   - 收集 (问题, SQL) 对,例如:
     Q: "查询2014年每个产品类别的销售额"
     A: "SELECT dc.EnglishProductCategoryName, SUM(fis.SalesAmount)..."
   
   - 数据清洗和增强
     * 添加同义问题("查询各产品类别的销售总额")
     * 变换时间条件(2014 → 2013, 2015)
     * 变换聚合维度(按类别 → 按地区、按客户)
   
   - 划分训练集/验证集(80%/20%)

2. 模型选择
   - 推荐: CodeLlama-13B 或 Qwen2.5-Coder-32B
   - 量化: 4bit + LoRA 降低显存需求(可在单张 A100 上训练)

3. 训练配置
   - 学习率: 2e-4
   - Batch Size: 16
   - Epochs: 3-5
   - 最大序列长度: 2048(AdventureWorks 表名较长)

4. 评估优化
   - 使用执行准确率评估(在真实数据库上执行)
   - 对比语法准确率 vs 执行准确率
   - 分析错误类型:表关联错误、字段错误、聚合错误等

AdventureWorksDW2016 微调数据示例

json 复制代码
[
  {
    "question": "查询销售额最高的前10个产品",
    "sql": "SELECT TOP 10 dp.EnglishProductName, SUM(fis.SalesAmount) AS TotalSales FROM FactInternetSales fis INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey GROUP BY dp.EnglishProductName ORDER BY TotalSales DESC"
  },
  {
    "question": "查询每个国家的客户数量",
    "sql": "SELECT dg.EnglishCountryRegionName, COUNT(DISTINCT dc.CustomerKey) AS CustomerCount FROM DimCustomer dc INNER JOIN DimGeography dg ON dc.GeographyKey = dg.GeographyKey GROUP BY dg.EnglishCountryRegionName ORDER BY CustomerCount DESC"
  },
  {
    "question": "查询2014年每月的销售趋势",
    "sql": "SELECT dd.EnglishMonthName, dd.MonthNumberOfYear, SUM(fis.SalesAmount) AS MonthlySales FROM FactInternetSales fis INNER JOIN DimDate dd ON fis.OrderDateKey = dd.DateKey WHERE dd.CalendarYear = 2014 GROUP BY dd.EnglishMonthName, dd.MonthNumberOfYear ORDER BY dd.MonthNumberOfYear"
  }
]

微调的方法门槛高,成本高,周期长,中小短期项目不是很推荐。


五、推理时增强策略:多选最优

5.1 自洽性(Self-Consistency)

生成多个候选 SQL,通过投票选择最优解:

示例:查询每个产品类别的平均销售额

复制代码
问题: 查询每个产品类别的平均订单金额

候选SQL:
1. 
SELECT dc.EnglishProductCategoryName, AVG(fis.SalesAmount) AS AvgAmount
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    INNER JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
GROUP BY dc.EnglishProductCategoryName;

2.
SELECT dc.EnglishProductCategoryName, AVG(fis.ExtendedAmount) AS AvgAmount
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    INNER JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
GROUP BY dc.EnglishProductCategoryName;

3.
SELECT dc.EnglishProductCategoryName, SUM(fis.SalesAmount)/COUNT(*) AS AvgAmount
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    INNER JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
GROUP BY dc.EnglishProductCategoryName;

分析:
- SQL 1 使用 SalesAmount(实际销售金额,包含折扣)
- SQL 2 使用 ExtendedAmount(扩展金额,未含折扣)
- SQL 3 手动计算平均值

投票结果:
SQL 1 和 SQL 3 语义等价 → 选择最常见的逻辑
最终选择: SQL 1(语义最清晰)

硬投票 vs. 软投票

  • 硬投票:选择出现次数最多的 SQL
  • 软投票:根据执行结果相似度加权投票(更优)

5.2 MCS-SQL:多提示架构

MCS-SQL 方法在 BIRD 数据集上达到 65.5% 准确率,Spider 上达到 89.6%

核心流程

  1. 多提示生成:使用不同风格的提示词生成多个候选 SQL
  2. Schema 精炼:从完整 Schema 中筛选相关表和字段
  3. 置信度评分:评估每个候选 SQL 的可靠性
  4. 多选机制:综合选择最优解

AdventureWorksDW2016 应用示例

复制代码
问题: "查询2014年北美地区自行车类产品的销售额"

提示1(结构化):
表: FactInternetSales, DimProduct, DimProductCategory, DimSalesTerritory, DimDate
条件: CalendarYear=2014, SalesTerritoryGroup='North America', EnglishProductCategoryName='Bikes'
目标: SUM(SalesAmount)

提示2(自然语言):
从销售事实表中查询2014年的销售数据,筛选北美地区和自行车产品,计算总销售额

提示3(SQL模板):
SELECT SUM(fis.SalesAmount)
FROM FactInternetSales fis
    JOIN DimDate dd ON fis.OrderDateKey = dd.DateKey
    JOIN DimSalesTerritory dst ON fis.SalesTerritoryKey = dst.SalesTerritoryKey
    JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
WHERE dd.CalendarYear = ?
    AND dst.SalesTerritoryGroup = ?
    AND dc.EnglishProductCategoryName = ?

多提示生成的候选SQL经过置信度评分和投票,选择最优解。

六、RAG 增强:注入领域知识

6.1 为什么需要 RAG?

大模型的知识来自训练数据,对于:

  • 企业内部业务规则(如"促销订单的 PromotionKey <> 1")
  • 实时数据变化(如新增产品类别)
  • 非公开的领域知识(如 AdventureWorks 的业务逻辑)

模型无法直接获取。RAG(检索增强生成)通过外挂知识库解决这一问题。

6.2 RAG 在 Text2SQL 中的应用

AdventureWorksDW2016 知识库示例

复制代码
用户问题: "查询参与了促销活动的订单数量"
  ↓
检索相关文档
  ↓ (找到: "促销订单的 PromotionKey <> 1,PromotionKey=1 表示无促销")
注入到 Prompt
  ↓
模型生成 SQL:
SELECT COUNT(DISTINCT SalesOrderNumber) AS PromotionOrderCount
FROM FactInternetSales
WHERE PromotionKey <> 1;

典型流程

  1. 知识库构建:整理业务规则、术语定义、常见查询模式

    复制代码
    知识条目示例:
    - "互联网销售存储在 FactInternetSales 表"
    - "经销商销售存储在 FactResellerSales 表"
    - "产品层次: Product → Subcategory → Category"
    - "日期维度包含日历年度和财年两种时间维度"
    - "销售区域分为三大组: North America, Europe, Pacific"
  2. 向量化存储:使用 Embedding 模型向量化

  3. 相似度检索:根据用户问题检索相关文档

  4. Prompt 增强:将检索结果注入提示词


七、完整实践案例:SQLBot 五大配置项

SQLBot是我目前见过国内比较优秀的一个Text2SQL的开源项目。这里以 SQLBot 开源项目为例,简单汇总了下企业级 Text2SQL 系统需要五大配置项:

7.1 配置项详解(AdventureWorksDW2016 实战)

配置项 作用 AdventureWorks 示例
表管理 字段描述、示例值 SalesAmount: 实际销售金额(含税、含运费)
表关联关系 多表 JOIN 逻辑 FactInternetSales.CustomerKey → DimCustomer.CustomerKey
术语配置 业务→技术映射 "自行车产品" = EnglishProductCategoryName = 'Bikes'
示例 SQL 复杂查询模板 提供 10-50 个高频查询示例(见下文)
自定义提示词 全局规则 "日期查询优先使用 DateKey 关联 DimDate"

7.2 AdventureWorksDW2016 示例 SQL 库

sql 复制代码
-- 示例1: 按产品类别统计销售额
SELECT 
    dc.EnglishProductCategoryName,
    COUNT(DISTINCT fis.SalesOrderNumber) AS OrderCount,
    SUM(fis.SalesAmount) AS TotalSales,
    AVG(fis.SalesAmount) AS AvgOrderAmount
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
    INNER JOIN DimProductCategory dc ON dps.ProductCategoryKey = dc.ProductCategoryKey
GROUP BY dc.EnglishProductCategoryName
ORDER BY TotalSales DESC;

-- 示例2: 按销售区域统计年度销售趋势
SELECT 
    dst.SalesTerritoryGroup,
    dst.SalesTerritoryCountry,
    dd.CalendarYear,
    SUM(fis.SalesAmount) AS YearlySales
FROM FactInternetSales fis
    INNER JOIN DimSalesTerritory dst ON fis.SalesTerritoryKey = dst.SalesTerritoryKey
    INNER JOIN DimDate dd ON fis.OrderDateKey = dd.DateKey
GROUP BY dst.SalesTerritoryGroup, dst.SalesTerritoryCountry, dd.CalendarYear
ORDER BY dst.SalesTerritoryGroup, dd.CalendarYear;

-- 示例3: 客户购买行为分析(RFM模型)
SELECT 
    dc.CustomerKey,
    dc.FirstName,
    dc.LastName,
    MAX(fis.OrderDate) AS LastPurchaseDate,
    COUNT(DISTINCT fis.SalesOrderNumber) AS PurchaseFrequency,
    SUM(fis.SalesAmount) AS MonetaryValue
FROM DimCustomer dc
    INNER JOIN FactInternetSales fis ON dc.CustomerKey = fis.CustomerKey
GROUP BY dc.CustomerKey, dc.FirstName, dc.LastName
ORDER BY MonetaryValue DESC;

-- 示例4: 产品销售排名(按子类别)
SELECT 
    dps.EnglishProductSubcategoryName,
    dp.EnglishProductName,
    SUM(fis.OrderQuantity) AS TotalQuantity,
    SUM(fis.SalesAmount) AS TotalSales
FROM FactInternetSales fis
    INNER JOIN DimProduct dp ON fis.ProductKey = dp.ProductKey
    INNER JOIN DimProductSubcategory dps ON dp.ProductSubcategoryKey = dps.ProductSubcategoryKey
GROUP BY dps.EnglishProductSubcategoryName, dp.EnglishProductName
ORDER BY dps.EnglishProductSubcategoryName, TotalSales DESC;

-- 示例5: 促销效果分析
SELECT 
    dp.EnglishPromotionName,
    dp.EnglishPromotionType,
    dp.EnglishPromotionCategory,
    COUNT(DISTINCT fis.SalesOrderNumber) AS OrderCount,
    SUM(fis.DiscountAmount) AS TotalDiscount,
    SUM(fis.SalesAmount) AS TotalSales
FROM FactInternetSales fis
    INNER JOIN DimPromotion dp ON fis.PromotionKey = dp.PromotionKey
WHERE fis.PromotionKey <> 1  -- 排除无促销的订单
GROUP BY dp.EnglishPromotionName, dp.EnglishPromotionType, dp.EnglishPromotionCategory
ORDER BY TotalSales DESC;

配置优先级

  1. 基础能力:表管理 + 表关联关系(必须配置)
  2. 沟通桥梁:术语配置(高频使用)
  3. 标准答案:示例 SQL(针对性强)
  4. 全局规则:自定义提示词(补充约束)

八、实战建议:如何选择优化路径?

8.1 按场景选择

场景 推荐方法 准确率预期 AdventureWorks 示例
简单单表查询 Few-Shot 示例 85%+ SELECT * FROM DimCustomer WHERE YearlyIncome > 100000
多表关联查询 Schema 增强 + 表关联配置 80%+ 产品销售分析(需关联 3-5 张表)
复杂业务查询 术语配置 + 示例 SQL + RAG 75%+ 客户 RFM 分析、促销效果分析
特定领域 模型微调 85%+ 针对 AdventureWorks 微调
高准确率要求 微调 + 推理时增强 90%+ 企业级报表系统

8.2 成本效益分析

方法 开发成本 标注成本 硬件需求 效果提升
提示工程 10-20%
Schema 增强 5-10%
RAG 5-15%
模型微调 15-25%
推理增强 5-10%

总结:准确率提升路线图

复制代码
第一阶段(快速见效)
  ├─ 优化 Schema 呈现(J-Schema)
  ├─ 添加字段描述和示例值
  └─ 配置 10-20 个 Few-Shot 示例(基于 AdventureWorks)

第二阶段(深度优化)
  ├─ 术语配置(业务语言映射)
  │   - "大客户" → YearlyIncome > 100000
  │   - "北美地区" → SalesTerritoryGroup = 'North America'
  ├─ 表关联关系管理
  │   - FactInternetSales 的所有外键关系
  │   - 产品层次结构(Product → Subcategory → Category)
  └─ 自定义提示词规则
      - 日期查询使用 DateKey 关联 DimDate
      - 产品查询考虑层次结构

第三阶段(持续改进)
  ├─ 构建 RAG 知识库
  │   - AdventureWorks 业务规则
  │   - 常见查询模式
  ├─ 收集反馈数据
  │   - 记录用户修正的 SQL
  │   - 分析错误模式
  └─ 模型微调(可选)
      - 使用 AdventureWorks 数据生成训练集
      - 针对特定查询类型优化

第四阶段(企业级)
  ├─ 推理时增强(多候选投票)
  ├─ 权限控制
  │   - 按销售区域限制数据访问
  │   - 敏感字段脱敏
  └─ 监控与优化
      - 查询性能监控
      - 准确率持续跟踪

参考资料

  1. SQLBot 最佳实践指南
  2. MCS-SQL: 多提示多选架构
  3. Text2SQL 准确率暴涨 22.6%
  4. DB-GPT-Hub 微调教程
  5. Alpha-SQL: 零样本 Text2SQL
  6. AdventureWorks 示例数据库官方文档

相关推荐
zc.z4 小时前
基于 LangChain4j 的 RAG 工作流智能体实战
langchain·大模型·springboot·rag智能体
Komorebi_99998 小时前
大模型学习day5
学习·大模型
Komorebi_99999 小时前
大模型学习day4
大模型
乔代码嘚9 小时前
2026 AI大模型全套资料免费领!30天从入门到架构部署,附面试真题与行业报告
人工智能·语言模型·面试·大模型·产品经理·ai大模型·大模型学习
xixixi7777711 小时前
深度解读:网信办“清朗·整治AI应用乱象”专项行动,AI产业告别野蛮生长,全面迈入合规治理深水区
人工智能·安全·ai·大模型·合规·深度伪造·网信办
Resistance丶未来12 小时前
Agency-Agents 多智能体协作系统落地指南
python·大模型·nlp·github·copilot·claude·gemini
.唉13 小时前
03. GraphRAG:当知识图谱遇见大语言模型
大模型·知识图谱·rag
程序员三明治16 小时前
【AI】Prompt 工程入门:从五要素框架到 RAG 生产级 Prompt 模板与 Java 实战
java·人工智能·后端·大模型·llm·prompt·agent
kabuto_hui16 小时前
【大模型系列】DLLM与Block Diffusion的区别与联系
人工智能·大模型