用DeepSeek生成SQL的关键在于补齐表结构、业务口径与数据库方言,再通过多轮追问、执行验证和权限控制,把自然语言需求稳定转成可执行查询。 很多初学者把 DeepSeek 当成“输入一句话就能吐 SQL”的黑盒,结果拿到一段看似完整却无法执行的语句。问题通常不在模型本身,而在于没有提供数据库方言、表结构和业务口径。以电商订单统计为例,把需求拆成结构上下文、提示词、复杂逻辑拆解、执行验证四个环节,才更接近可复用的保姆级流程。
1. 锁定数据库方言与表结构上下文
DeepSeek 本身并不连接你的数据库,它只能根据对话中出现的线索推断字段和语法。如果只说“帮我统计订单金额”,模型很可能编造 orders 表和 amount 字段,甚至在 MySQL 环境里写出 PostgreSQL 的 DATE_TRUNC。正确做法是在提问开头声明目标数据库,例如“目标数据库为 MySQL 8.0,请使用 MySQL 方言,不要使用 PostgreSQL 或 SQL Server 函数”。接着把相关表的 CREATE TABLE 语句贴进去,包括字段类型、主键、外键、索引和注释。以电商场景为例,至少提供 orders(id, user_id, status, paid_at, amount, refund_amount, address_id)、users(id, city, register_time)、order_items(id, order_id, product_id, quantity, price) 和 products(id, category_id, name),并说明 status 的枚举值为 pending、paid、completed、cancelled,refund_amount 大于零表示发生过退款。
只有表结构还不够,业务口径决定 SQL 是否正确。比如“销售额”可能指下单金额、支付金额、完成金额或扣除退款后的净 GMV,不同口径会生成完全不同的过滤条件和聚合字段。你需要明确时间范围是自然月还是滚动 30 天,城市按下单城市还是收货城市,是否排除测试账号,是否只统计已完成订单。把这些规则写成连续说明,而不是让模型猜测。可以给出示例提示:“请根据以下表结构生成一条 MySQL 查询,统计最近 30 天每个城市的已完成订单净 GMV,净 GMV 等于 sum(amount – refund_amount),排除 refund_amount 大于 0 的订单,按下单城市分组,按净 GMV 降序取前 10,并解释每个 JOIN 的作用。”这种提示把方言、表、口径和输出格式一次性交代清楚,DeepSeek 的初稿可用率会明显提高。
在内部教学演练中,零基础学员用“帮我查销售额”生成的 SQL,超过一半出现字段名猜错、漏掉状态过滤或把退款金额算进收入;补充 DDL 和口径后,多数错误集中在日期边界和 NULL 处理,而不是表名和字段。这个变化说明,DeepSeek 生成 SQL 的瓶颈通常不在语言能力,而在上下文完整性。更稳妥的做法是要求模型先输出“字段假设清单”和“业务口径确认项”,例如确认 city 来自 users 还是地址表、paid_at 是否可能为 NULL、退款订单是否仍保留在 orders 中。确认后再让它生成可执行 SQL,能把返工次数压到更低。
2. 用结构化提示词描述查询目标
结构化提示词可以按固定顺序组织:角色与数据库、表结构、字段语义、业务规则、输出要求、可选样例。角色可以写“你是拥有十年经验的 MySQL 数据分析师”,数据库写“MySQL 8.0,只使用 SELECT,不生成 DELETE 或 UPDATE”,表结构直接粘贴 DDL,字段语义说明 orders.status 和 refund_amount 的含义,业务规则说明时间窗口和聚合口径,输出要求写明“先输出 SQL,再输出三句话解释,不要使用 SELECT *”。以“近 30 天各城市已完成订单净 GMV Top10”为例,提示词中可以要求使用 CTE 提升可读性,金额用 ROUND(..., 2) 保留两位小数,城市字段来自 users.city。
多轮追问比一次性长提示更接近真实工作流。第一轮让 DeepSeek 生成骨架,只要求表关联和分组;第二轮补充过滤条件,例如 o.status = 'completed'、o.paid_at >= DATE_SUB(CURDATE, INTERVAL 30 DAY)、o.refund_amount = 0;第三轮要求改写成 CTE 并加上字段注释;第四轮让它检查边界,例如 paid_at 为空、amount 为负、city 为空时如何处理。每轮追问都要给出明确方向,不要只说“优化一下”,因为“优化”可能被理解为性能优化、可读性优化或业务口径优化。把期望说清楚,模型才能稳定收敛。例如“请在 WHERE 中避免对 paid_at 使用 DATE_FORMAT,保证索引可用”就比“优化日期条件”有效得多。
自然语言歧义是生成错误 SQL 的常见原因。“最近一个月”可能是自然月,也可能是滚动 30 天;“销售额”可能含税也可能不含税;“城市”可能来自用户资料,也可能来自收货地址。遇到这类词,要求 DeepSeek 先列出歧义点并给出默认假设,再由你确认。生成结果中如果出现 order、group 这类保留字,需要加反引号或改用别名。一个可运行的核心骨架类似 WITH recent_orders AS (SELECT o.id, o.user_id, o.amount, o.refund_amount FROM orders o WHERE o.status = 'completed' AND o.paid_at >= DATE_SUB(CURDATE, INTERVAL 30 DAY) AND o.refund_amount = 0) SELECT u.city, ROUND(SUM(r.amount - r.refund_amount), 2) AS gmv FROM recent_orders r JOIN users u ON r.user_id = u.id GROUP BY u.city ORDER BY gmv DESC LIMIT 10;。把这段骨架与业务口径逐项对照,比直接接受模型输出更可靠。
3. 拆解多表关联与复杂查询逻辑
复杂查询不要一次性丢给 DeepSeek 生成完整 SQL,而是先让它拆解逻辑步骤。以“统计每个城市复购用户贡献的净 GMV”为例,可以让模型先列出:找出时间窗口内已完成订单,按用户聚合订单次数和净 GMV,筛选订单次数大于 1 的用户,再关联用户城市并汇总。这个步骤清单确认后,再让 DeepSeek 把每一步写成 CTE。多表关联最容易出问题的是粒度膨胀:orders 一行代表一个订单,order_items 一行代表一个商品明细,直接把两者 JOIN 后再 SUM(o.amount) 会把同一订单金额重复计算多次。正确做法是先按 order_id 聚合明细,或者只在 orders 表内完成金额汇总,再与用户表关联。
窗口函数和日期过滤是提升复杂查询质量的关键。DeepSeek 对 ROW_NUMBER OVER (PARTITION BY city ORDER BY gmv DESC)、RANK、DENSE_RANK 的生成通常较稳定,但你需要说明排名口径:并列时是否跳号,取前 10 是每个城市前 10 还是全局前 10。日期过滤要尽量写成半开区间,例如 paid_at >= '2024-05-01' AND paid_at < '2024-06-01',避免 DATE_FORMAT(paid_at, '%Y-%m') = '2024-05' 导致索引失效。生成后把 SQL 放到测试库执行 EXPLAIN,观察 type 是否为 ALL、key 是否命中索引、rows 是否异常偏大、Extra 是否出现 Using temporary 或 Using filesort。如果 orders 表数据量达到千万级,过滤条件中的 status 和 paid_at 应建立联合索引,城市字段在 users 表上也需要索引,否则分组和排序会消耗大量临时空间。
让 DeepSeek 自查比人工逐行检查更高效。可以要求它回答三个问题:当前 SQL 是否可能重复计数,是否遗漏退款或未支付订单,是否在 JOIN 后改变了原始订单粒度。还可以让它生成验证 SQL,例如对比 COUNT(DISTINCT o.id) 与 COUNT(*) 是否一致,对比净 GMV 与财务对账表是否在可接受误差内。如果执行报错,把完整错误信息贴回去,例如 ERROR 1054 (42S22): Unknown column 'u.city' in 'field list',并补充“users 表实际字段为 user_city,请只修改字段引用,不要改变业务口径”。这种带错误上下文和约束的追问,能让 DeepSeek 快速修正而不至于重写整段逻辑。多轮拆解、粒度检查和验证 SQL 结合起来,复杂查询才会从“看起来能跑”变成“结果可信”。
4. 执行验证、性能优化与安全落地
DeepSeek 生成的 SQL 不能直接拿到生产库执行,先进入只读副本或测试库验证是底线。验证时先做小范围对账,把日期范围缩小到一天或一个城市,手工统计订单数、净 GMV、退款订单数,再与 SQL 结果比对。边界数据尤其重要:paid_at 为 NULL 的订单、amount 为 0 的订单、refund_amount 等于订单金额的全额退款、city 为空的用户,都可能让聚合结果偏离预期。对账通过后,再把时间窗口扩大到 30 天并对比 BI 报表。若结果不一致,先检查过滤条件和 JOIN 粒度,而不是马上怀疑 DeepSeek 的语法能力,因为语法正确但口径错误的 SQL 在业务上更危险。
安全落地需要从权限和语句类型两方面控制。给生成 SQL 的执行环境配置只读账号,禁用 INSERT、UPDATE、DELETE、DROP、TRUNCATE 等写操作,并在提示词中明确“只允许生成 SELECT 查询”。对于必须限制行数的探索性查询,强制加上 LIMIT,例如 LIMIT 1000,避免一次扫描拖垮数据库。若通过程序调用 DeepSeek 生成 SQL 再执行,不要把用户输入直接拼进 SQL,仍然要用参数化查询或白名单校验,防止自然语言中夹带恶意指令。审计层面记录每次生成的 SQL、执行人、执行时间、扫描行数和返回行数,便于出现数据偏差时回溯。教学场景中也应让学员养成先看权限、再看语句、再看结果的顺序,而不是看到 SQL 能运行就认为任务完成。
性能优化要结合执行计划和数据分布。常见的低效写法包括在 WHERE 中对日期字段使用 DATE_FORMAT 或 YEAR,在 JOIN 条件中对字段做类型转换,以及用 SELECT * 拉取大量无用列。把这些 SQL 交给 DeepSeek 优化时,要求它输出优化前后对比和索引建议,例如把 DATE_FORMAT(paid_at, '%Y-%m') = '2024-05' 改为 paid_at >= '2024-05-01' AND paid_at < '2024-06-01',并为 orders(status, paid_at) 建立联合索引。分页查询中,LIMIT 10000, 20 在数据量大时也会变慢,可以改成基于游标或自增 ID 的 WHERE id > last_id ORDER BY id LIMIT 20。把生成、验证、授权、监控串成闭环,DeepSeek 生成的 SQL 才能真正进入日常数据工作流。