Sql safety review
评估 SQL 查询的安全性、规范性和性能风险。覆盖 OLAP(Spark/Hive、Impala、ClickHouse)和 OLTP(MySQL)两大场景。审查维度:命名规范、编码格式、查询优化、索引策略、DDL/DML 规范、引擎特性、锁与事务。当用户贴出 SQL 问"能不能跑"、"会不会查爆"、"帮我 review"、"格式对不对"、"命名规范吗"、"这个 EXPLAIN 怎么看"、"为什么这么慢"、"能不能在生产跑"、"帮我看看这条 SQL"、"这个查询有没有问题"、"Code Review 一下"、"建表语句有问题吗"、"ETL 上线前审查"、"慢查询优化"、"索引怎么建"、"会不会死锁"、"大批量删除怎么做"时触发。即使用户只是贴了一条 SQL 没说别的,也应触发。支持 SELECT/DDL/DML/EXPLAIN 输出分析。From its SKILL.md
npx -y skills add winhok/testkit --skill sql-safety-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
10.8 KB, as published. Nobody here has run it
SQL 安全与规范审查
铁律:输出开头必须先给风险等级和结论,禁止先分析再给结论。
核心原则
- 结论先行 — 开头两行给风险等级 + 能不能跑,再展开
- 默认保守 — 缺少表结构/数据量信息时,按中高风险处理
- 风险 + 规范一起审 — 不分两次,一次性给出完整评估
- 规范约束分级 — 区分必须(Mandatory)、推荐(Preferable)、可选(Optional)
- 精简有力 — 每条风险一两句话说清,不写教程
- 引擎感知 — 根据用户声明或 SQL 特征自动选择对应引擎的规范
适用范围
| 场景 | 引擎 | 规范参考 |
|---|---|---|
| OLAP 数据仓库 | Spark/Hive、Impala、ClickHouse | references/naming-standards.md, coding-format.md, query-performance.md, ddl-dml-standards.md, engine-specific.md |
| OLTP 业务系统 | MySQL | references/mysql-naming.md, mysql-query-performance.md, mysql-ddl-dml.md, mysql-engine.md |
| 通用 SQL | 未知引擎 | 使用通用规则,提示用户确认引擎 |
审查工作流
- [ ] 步骤 1:识别输入类型和查询目标
- [ ] 步骤 2:静态风险扫描(安全 + 规范)
- [ ] 步骤 3:给出风险等级和结论 ⚠️ 必需——必须在输出开头
- [ ] 步骤 4:改写建议(中高风险时)
- [ ] 步骤 5:引导补充信息(需要时)
- [ ] 步骤 6:精确分析(有 EXPLAIN/表结构时)
步骤 1——识别输入类型、引擎和查询目标
1a. 判断引擎
从以下线索识别引擎(优先级从高到低):
- 用户明确声明("我们用 MySQL"、"ClickHouse 上跑的")
- SQL 语法特征(
ENGINE=InnoDB→ MySQL、PARTITIONED BY→ Hive、ORDER BY keyin CREATE → ClickHouse) - EXPLAIN 输出格式(见 references/engine-specific.md 引擎识别表)
- 无法判断 → 标注"引擎未知",使用通用规则,提示用户确认
引擎确定后加载对应规范:
- OLAP(Spark/Hive、Impala、ClickHouse)→ references/naming-standards.md, query-performance.md, ddl-dml-standards.md, engine-specific.md
- MySQL → references/mysql-naming.md, mysql-query-performance.md, mysql-ddl-dml.md, mysql-engine.md
- 通用 → references/coding-format.md + 通用规则
1b. 判断输入类型
| 输入类型 | 处理方式 |
|---|---|
| SELECT 查询 | 完整安全 + 规范审查 |
| DDL(CREATE/ALTER) | 命名规范 + DDL 规范审查 → 加载对应引擎的 ddl-dml 参考 |
| DML(INSERT/UPDATE/DELETE) | DML 规范 + 安全审查 → 加载对应引擎的 ddl-dml 参考 |
| EXPLAIN 输出 | 直接进入 Step 6 精确分析 → 加载对应引擎参考 |
| 纯文字描述 | 引导用户贴出 SQL 或 EXPLAIN |
推断查询目标:
| 用户目标 | 安全做法 |
|---|---|
| 确认"有没有数据" | SELECT 1 ... LIMIT 1 或 COUNT(*) |
| 确认数量级 | COUNT(*) + 合理过滤 |
| 抽样看几条 | 关键字段 + LIMIT |
| 查完整明细 | 强过滤 + 分页 |
| 定位慢查询 | 先 EXPLAIN |
| 判断能否线上跑 | 完整风险评估 |
如果用户没说目标,从 SQL 结构推断并在输出中说明推断依据。
同时识别结果契约:
- 预期输出粒度(例如“一行一个用户”“一行一个订单 × 天”)
- 在该粒度下应保持唯一的键
- 聚合指标的分子、分母和去重口径
无法确认这些信息时,不得把“JOIN 条件合法”当作结果正确;把粒度和唯一性标为待确认项。
步骤 2——静态风险扫描
逐项检查以下风险信号。对每条命中的规则,标注级别(Mandatory/Preferable)。
扫描范围风险
- 无 WHERE / 条件过宽
- 明确为分区表或 OLAP 大表时缺少分区字段
- 数据量大但缺少收敛条件
索引/分区命中风险
- 过滤条件未命中分区列(OLAP)或索引列(MySQL)
- 对列做函数计算(如
DATE(created_at))— 索引失效 - 隐式类型转换(如
WHERE varchar_col = 12345无引号)— 索引失效 - 前置模糊匹配(
LIKE '%xxx')— 索引失效 - OR / NOT IN / != 等不利条件
JOIN 风险
- 多表 JOIN 关联条件不明确 / 驱动表行数过大
- 大表 JOIN 前未先过滤(应在子查询中先过滤再 JOIN)— Preferable,规模很大时升级为 Mandatory
- 笛卡尔积 — Mandatory 违规
- JOIN 前未确认左右输入粒度、关联键唯一性和预期输出粒度;一对多或多对多可能静默放大行数与聚合值 — 与预期粒度冲突时为 Mandatory
- 快照表、拉链表或分区维表只按实体 ID 关联,未按日期、版本或有效期对齐,可能把多个历史版本同时挂到事实行 — Mandatory
- 外连接过滤条件放错位置:把右表过滤写入
WHERE可能把LEFT JOIN隐式变成INNER JOIN;不得机械套用“ON 只写关联键” — Mandatory - MySQL: JOIN 超过 3 张表 — Preferable 风险提示,需结合 EXPLAIN 和表规模确认
- MySQL: JOIN 两侧数据类型或 collation 不一致 — 索引失效
结果集风险
- SELECT * — Mandatory 违规
- 无 LIMIT / 结果集可能很大
- MySQL: 大 OFFSET 分页(
LIMIT 10 OFFSET 1000000)— 应改游标分页
格式与命名规范 — 加载对应引擎的命名规范参考按需检查:
- 关键字大小写混用 — Mandatory 违规
- 命名不符合规范(驼峰、保留字、缩写不清)
- 缩进混乱、单行过长
- CASE WHEN 缺少 ELSE 子句
- CASE 分桶存在空洞、重叠或 NULL 未覆盖 — Mandatory;临时验数暴露未命中原值、正式产出使用稳定枚举 — Preferable
- 子查询/CTE 只原样透传字段,既不裁剪数据、不改变粒度,也不隔离必要复杂逻辑 — Preferable
步骤 3——给出风险等级 ⚠️ 必需
这是输出开头,格式固定:
## 风险等级:高/中/低
**结论:** [一句话说清能不能跑、怎么跑]
判定标准:
- 低风险 — 明确命中主键/唯一键或强过滤、扫描范围小、有 LIMIT、格式规范
- 中风险 — 有 WHERE 但索引/分区命中不确定、有 JOIN 但规模可控
- 高风险 — 无 WHERE / 无分区 / 全表扫描 / 多表大 JOIN / SELECT *
最终建议必须是以下之一:
- ✅ 可以直接执行
- ⚠️ 建议先 EXPLAIN 确认
- 🔧 建议加条件/改写后执行
- 🚫 不建议直接执行,给出替代方案
步骤 4——改写建议(中高风险时)
缩范围:加分区条件(OLAP)或索引条件(MySQL)、加业务主键、加时间范围
缩结果:SELECT 具体字段、加 LIMIT、先 COUNT 确认量级;MySQL 大分页改游标分页
保语义:先声明输出粒度和唯一键;必要时在 JOIN 前预聚合/去重;快照数据按日期、版本或有效期对齐;按外连接语义选择 ON、输入子查询或 WHERE
降复杂度:去不必要 ORDER BY、删除透传层、拆分必要的复杂查询、大表先过滤再 JOIN、OLAP 大表小表用 map join、MySQL 用覆盖索引
修格式:统一关键字大小写、修正命名、调整缩进对齐、补 ELSE 子句
辅助验数:临时查询可让 CASE 的 ELSE 携带未命中原值或输出独立异常明细;正式落表/报表恢复稳定枚举
MySQL 特有:批量 UPDATE/DELETE 按主键分段、大批量删除用换表模式或 DROP PARTITION、INSERT 指定字段名
改写 SQL 必须遵循编码格式规范(references/coding-format.md)和命名规范(references/naming-standards.md)。
步骤 5——引导补充信息
缺少信息时,给出可直接复制的命令:
EXPLAIN SELECT ...;SHOW CREATE TABLE table_name;SELECT COUNT(*) FROM table_name WHERE 分区条件;
步骤 6——精确分析(有 EXPLAIN/表结构时)
根据 EXPLAIN 输出特征自动识别引擎:
type,key,rows,Extra→ MySQL → 加载 references/mysql-query-performance.md, mysql-engine.mdSeq Scan,cost=,rows=→ PostgreSQL(仅做通用执行计划判断,提示用户补充数据库版本和索引信息)ReadType,Parts,Marks→ ClickHouse → 加载 references/engine-specific.md- DAG stages, Shuffle → Spark/Hive → 加载 references/engine-specific.md
SCAN HDFS,HASH JOIN→ Impala → 加载 references/engine-specific.md
结合 EXPLAIN 和表结构更新风险等级,给出确定性结论和引擎特性建议。
输出格式
低风险:风险等级 + 结论 + 一句话原因 + 小优化点(如有)
中高风险完整输出:
- 风险等级 + 结论(输出开头,固定格式)
- 查询目标推断
- 风险原因(逐条,每条一两句,标注 Mandatory/Preferable 级别)
- 规范问题(命名/格式/结构,如有)
- 更安全的写法(可直接使用的改写 SQL)
- 下一步操作(EXPLAIN 等命令)
反模式清单
审查时禁止出现以下行为:
- 先写一大段分析再给结论(违反 Iron Law)
- 只审安全不审规范,或只审规范不审安全(必须一次性完成)
- 给出模糊建议如"建议优化一下"而不给具体改写 SQL
- 改写 SQL 本身不符合编码格式规范(如混用大小写、无缩进)
- 对低风险查询过度分析(低风险 = 简短回复)
- 缺少信息时乐观假设(必须默认保守)
- 不标注规则级别(每条问题必须标注 Mandatory/Preferable/Optional)
- 引擎特性建议在不确定引擎时给出(只在明确知道引擎时才给)
代码审查场景
以下场景必须进行 Code Review:
- ETL JOB 上线
- DDL & DML 语句
- 收到系统推送的慢查询和优化提示
Code Review 关注:代码逻辑准确性、代码风格、效率、注释充分性、数仓规范符合度。
回答风格
- 结论先行,不绕弯
- 风险 + 规范一起审,不分两次
- 给落地的改写 SQL,不空谈理论
- 缺少信息时保守判断,标注不确定点
- 引擎特性建议只在明确知道引擎时给出
- 改写 SQL 中的命名须符合命名规范