Files
opencode-build/skills/sql-review/SKILL.md
T
2026-06-22 10:08:52 +08:00

6.8 KiB

name, description
name description
sql-review 对执行前生成好的 PySpark SQL 字符串做静态评审时使用。触发词包括"review this SQL"、"SQL 评审"、"静态审查"、"risk level"、"check join cardinality"、"数据膨胀"、"重复聚合"、"分区裁剪"。消费 SQL 字符串及其产出的 SQL Context,产出按风险分级的评审结果(HIGH / MEDIUM / LOW)以及具体修复建议。**不修改 SQL** — 只报告发现。

SQL Review(SQL 评审)

概述(Overview)

对一条生成好的 PySpark SQL 字符串做执行前的静态评审。评审者改写 SQL — 它只输出发现项、严重程度和建议修复。由 SQL 的作者决定采纳哪些。

核心原则:对照 SQL Context 评审,而不是凭直觉。 SQL 中引用的每一个字段、alias、join key 都必须存在于产出它的 SQL Context。如果不存在,这就是一项 HIGH 风险(SQL 绕过了契约)。

何时使用(When to Use)

使用场景:

  • 一条 SQL 字符串已经从 sql_context 生成出来,即将执行
  • 用户粘贴一条 SQL 字符串,要求评审、优化或风险评估
  • 代码改动为 PySpark 脚本新增了 spark.sql(...) 调用

不要使用场景:

  • 这条 SQL 不是 Spark SQL(例如纯 Postgres、HiveQL 在 Spark 之外)— 至少要把 Spark 特有的检查替换成对应引擎的等价检查,并标注出来
  • 是一次性、不会被复用的交互查询 — 用户可以主动选择跳过

输入(Inputs)

  1. 要评审的 SQL 字符串
  2. 产出这条 SQL 的 SQL Context(如果缺失,需要显式声明"该 SQL 是不可信 / 临时拼凑的")

如果 SQL Context 缺失,抛出一项 HIGH 风险:"SQL 没有对应的 SQL Context;下游 skill 无法保证字段 / join 的正确性。" 然后继续做结构性检查。

严重程度模型(Severity Model)

等级 含义 处置
HIGH 会导致数字错误、执行失败或违反策略 阻断执行,必须修。
MEDIUM 大概率对,但低效、脆弱或大规模场景有风险 上线前修,开发环境可放过。
LOW 风格 / 最佳实践小瑕疵 可选修。

按以下顺序输出发现项:HIGHMEDIUMLOW。任一 HIGH 让整体评审结论为 FAIL

工作流(Workflow)

digraph sql_review {
    "解析 SQL(逻辑层面,不只是正则)" [shape=box];
    "对照 SQL Context 交叉校验 alias、字段、join key" [shape=box];
    "检测多语句 / 禁用 verb(防护栏)" [shape=box];
    "分析 join 基数与膨胀风险" [shape=box];
    "检测同一度量的重复聚合" [shape=box];
    "检测全表扫描与缺失分区过滤" [shape=box];
    "校验 GROUP BY 与 SELECT 一致" [shape=box];
    "检查 join key 与度量的空值处理" [shape=box];
    "检查字段类型与运算的匹配" [shape=box];
    "汇总成评审结论" [shape=box];

    "解析 SQL(逻辑层面,不只是正则)" -> "对照 SQL Context 交叉校验 alias、字段、join key";
    "对照 SQL Context 交叉校验 alias、字段、join key" -> "检测多语句 / 禁用 verb(防护栏)";
    "检测多语句 / 禁用 verb(防护栏)" -> "分析 join 基数与膨胀风险";
    "分析 join 基数与膨胀风险" -> "检测同一度量的重复聚合";
    "检测同一度量的重复聚合" -> "检测全表扫描与缺失分区过滤";
    "检测全表扫描与缺失分区过滤" -> "校验 GROUP BY 与 SELECT 一致";
    "校验 GROUP BY 与 SELECT 一致" -> "检查 join key 与度量的空值处理";
    "检查 join key 与度量的空值处理" -> "检查字段类型与运算的匹配";
    "检查字段类型与运算的匹配" -> "汇总成评审结论";
}

检查项(Checks,全部执行)

  1. 引用完整性(Reference integrity) — SQL 中的每个 <alias>.<column> 都必须存在于 SQL Context。不匹配即 HIGH
  2. Join key 匹配 — 每个 ON 谓词都必须对得上 SQL Context 中已固化的 join。凭空发明的 join 算 HIGH
  3. 禁用 verb / 多语句 — 交给 pyspark-sql-guardrails。任何 DDL/DML/维护 verb,或任何 ; 堆叠的语句,均为 HIGH
  4. Join 基数风险 — 对每个 join,如果 SQL Context 标记基数为 1:NN:N 且没有去重,分别抛 MEDIUM(1:N)或 HIGH(N:N)
  5. 重复聚合 — 同一度量被聚合两次(例如子查询里一次、外层再一次)且非有意重新聚合,抛 HIGH
  6. 分区 / 时间过滤 — 对任何有已知时间字段的事实表,缺少对该字段的 WHERE 过滤即为 HIGH(全表扫描风险)
  7. GROUP BY 完整性 — SELECT 中每个未聚合的列都必须出现在 GROUP BY 中。不匹配为 HIGH
  8. 空值处理 — 可空 join key 上做 INNER JOIN 会静默丢行(MEDIUM);可空度量未用 COALESCE 聚合,可能产出 NULL 进而破坏下游 WHERE ... > 0(MEDIUM)
  9. 类型不匹配 — 字符串列未做 cast 就参与数值聚合(MEDIUM);日期字符串和 TIMESTAMP 字面量比较(MEDIUM)
  10. Spark 最佳实践COUNT(*) vs COUNT(1) 属于风格(LOW);生产环境用 SELECT *MEDIUM;数据倾斜 join 缺 DISTRIBUTE BY / CLUSTER BYLOWMEDIUM

输出契约(Output Contract)

sql_review:
  verdict: <PASS | FAIL>
  highest_severity: <HIGH | MEDIUM | LOW>
  summary: "<一句话总结>"
  findings:
    - id: F1
      severity: HIGH
      check: reference_integrity
      location: "<行/列号或 SQL 片段>"
      message: "<错在哪>"
      fix: "<具体的建议修改>"
    - id: F2
      severity: MEDIUM
      check: join_cardinality
      location: "JOIN customer_info"
      message: "1:N join without pre-aggregation; risk of row inflation"
      fix: "Aggregate loan_order by customer_id before joining"
  notes:
    - "SQL Context provided: yes (sql_context.aliases = [...])"
    - "Reviewed by: sql-review v1"

禁止行为(Forbidden Behaviors)

自我说服 现实
"SQL 看起来没问题,不用对一遍 context" 字段可能"看起来对"但源表里根本没有,必须做交叉校验。
"我顺手在评审时把 SQL 修了" 评审只报告,修是作者的事。职责分离。
"没给 SQL Context,那字段检查就跳过" 缺 context 本身就是 HIGH,绝不能悄悄跳过。
"只是开发查询,分区检查跳过" 这一项成本极低,跳过就会留下静默的全表扫描。
"1:N join 是常态,没风险" 在保留粒度的前提下 1:N 是 OK 的,但评审必须验证,不能假设。
"用正则就够了,能查 GROUP BY" GROUP BY 成员关系是结构性的,要用解析器,不要用正则。

完成判定(Completion Criteria)

评审完成的条件:

  • 全部 10 项检查都已评估
  • 每一条 HIGH 风险都有 fix
  • 当且仅当 highest_severityMEDIUMLOW 时,verdict 才为 PASS
  • 输出的 YAML 可直接贴到 PR 评论,无需修改