数据库变更脚本预检 (ym-sql-change-precheck)
上线前把 SQL 变更脚本里会出事的地方翻出来——不是执行完再救火。核心是静态风险扫描:只读脚本、给风险等级与可执行的改写建议,绝不执行 SQL、绝不连库。
何时使用
- 用户说「看下这个 SQL 能不能上」「变更脚本预检」「上线前检查 SQL」「这段 DDL 有风险吗」时
- 用户贴出一段 DDL/DML 变更脚本,准备上线前想做评审时
- 团队做上线变更评审,需要一份风险清单与回滚方案时
运行前提
- 依赖:无第三方库,纯静态文本分析
- 不连接数据库、不执行任何 SQL
- 不读取本地文件,除非用户给出路径
- 不联网、不上传脚本内容
输入契约
| 项 | 必需 | 说明 |
|---|---|---|
| SQL 变更脚本 | 必需 | DDL/DML 均可,可含多条语句 |
| 数据库类型与版本 | 可选 | MySQL/PostgreSQL/Oracle/SQL Server;缺省按通用 SQL 审查 |
| 表量级 | 可选 | 用于判断大表风险;缺省按"若超百万行"条件化提示 |
| 是否允许锁表 | 可选 | 默认不允许,锁表相关标高危 |
| 是否有回滚要求 | 可选 | 默认要求回滚方案 |
缺失处理:
- 缺库类型 → 按通用 SQL 审查,对有方言差异的点同时给几种写法并标注"需确认具体库"
- 缺表量级 → 大表风险项按"若超过百万行"条件化提示,不假设量级
- 无脚本只有自然语言描述 → 请用户提供实际 SQL,不凭描述臆测风险
执行步骤
-
逐条编号与分类 — 给每条语句编号(S1、S2…),识别类型:CREATE / ALTER / DROP / TRUNCATE / UPDATE / DELETE / INSERT / 数据迁移。类型决定后续扫描重点。
-
按风险清单扫描 — 对照
references/风险规则清单.md的 7 类规则逐项过:破坏性、锁与可用性、数据一致性、隐式类型转换、兼容性、可回滚、命名规范。每条命中记录:编号、位置、类型、等级、后果。 -
给风险等级与改写 — 每条风险标 高/中/低,写清后果,并给可执行的 SQL 改写片段(如
UPDATE ... SET ... WHERE id = ?)。需人工确认的标"需人工确认"。 -
排执行顺序建议 — 哪些必须先跑(如先建索引再写数据)、哪些要分批次(大表 DDL 单独批次)、哪些需先备份。给出顺序与批次划分。
-
出回滚方案骨架 — 若用户未提供,按
templates/回滚方案模板.md给每类操作的回滚(DROP 的列先备份、UPDATE 前记录旧值等)。不可逆操作(DROP、数据清理)明确标注"无安全回滚,需备份兜底"。 -
自检 — 逐条核对:高危项是否都给了改写建议?不可逆操作是否都提示了备份/无回滚?有没有出现"已执行""连库检查"这类越界动作(不应出现)?
输出格式
Markdown,套用 templates/风险报告模板.md:
## 一、脚本概览
- 语句数:[N],类型分布:[ALTER x2, UPDATE x1, DROP x1]
- 库类型:[MySQL 8.0 / 未指定按通用]
## 二、风险清单
| 编号 | 位置 | 类型 | 等级 | 后果 | 建议 |
|------|------|------|------|------|------|
| S2 | UPDATE user | 破坏性 | 高 | 全表 status 被置 1 | 加 WHERE 条件 |
## 三、高危项展开
- [S2] 原语句:…… 改写:…… 为什么:……
## 四、执行顺序建议
1. [先跑什么] 2. [再跑什么,分批说明]
## 五、回滚方案
[逐类操作回滚 SQL / 备份兜底说明]
## 六、需人工确认项
- [方言差异 / 表量级未知 / 是否允许锁表]
能力边界
- 不执行任何 SQL,不连接数据库,不分析真实执行计划
- 不保证覆盖所有数据库方言细节;方言差异点标注"需确认"
- 不代替 DBA 审批;最终上线决策由人做
- 不假设表结构;结构信息必须由用户提供或从脚本推断并标注"推断"
- 默认不跨次存储
常见坑
- UPDATE/DELETE 没 WHERE 还放行 — 现象:模型看漏了缺 WHERE 的更新,写成"低风险"。原因:没逐条扫破坏性规则。规避:每条 UPDATE/DELETE 强制检查 WHERE 是否存在且非恒真(
WHERE 1=1等同无 WHERE)。 - 大表加索引没提示在线 DDL — 现象:直接说"加索引没问题"。原因:忽略锁与可用性维度。规避:大表(或"若超百万行")的加索引必须提示用在线 DDL(如 MySQL
ALGORITHM=INPLACE, LOCK=NONE)或低峰分批。 - 新增 NOT NULL 列无默认值当小事 — 现象:表非空时加 NOT NULL 无默认值会直接报错或锁表,被轻描淡写。原因:没过数据一致性维度。规避:非空表加 NOT NULL 必须给"先加可空列 + 默认值回填 + 再改 NOT NULL"方案。
- 隐式类型转换导致索引失效被忽略 — 现象:WHERE 里
varchar字段传了数字,模型没提示。原因:只看了语法没看类型匹配。规避:WHERE 条件字段类型与传入值类型不匹配时,提示会触发隐式转换、索引失效、全表扫。 - DROP/TRUNCATE 没标无回滚 — 现象:给了回滚 SQL 但 DROP 列其实无法无损回滚。原因:回滚方案模板套错。规避:DROP/TRUNCATE/数据清理标"不可逆,需先全量备份兜底",不给虚假回滚。
- 方言混用当成通用 — 现象:把 MySQL 的
LIMIT或 Oracle 的ROWNUM当通用语法。原因:缺库类型时默认通用却没标差异。规避:缺库类型时每种方言差异点并列给出并标"需确认具体库"。 - 结构靠猜 — 现象:脚本用到的表/字段模型凭空假设存在。原因:违反"不假设表结构"约束。规避:结构信息只能来自用户提供或脚本内推断,并明确标注"推断,待你确认"。
验收用例
- 正常:
UPDATE user SET status=1无 WHERE → 判高危,给带 WHERE 的改写 - 边界 1:新增 NOT NULL 列无默认值且表非空 → 判高危,给"加默认值再回填"方案
- 边界 2:仅
CREATE TABLE无风险 → 明确输出"未发现高危项",不硬凑风险 - 失败:只有自然语言描述无 SQL → 请用户提供实际脚本
目录说明
references/— 风险规则清单(7 类,含判定特征/后果/改写建议)templates/— 风险报告模板、回滚方案模板
