docs: 归档 demo 工程样例与插件实测报告
- 新增三个 demo 工程(demo-eslint / demo-sqlfluff / demo-stylelint):各含 src/ 样例代码与覆盖率映射(run-coverage.mjs、coverage_map.json、stylelint_line_map.json) - 归档各 demo 的插件实测导出报告(reports/): - demo-eslint:common.js / esm-demo.js / legacysyntax.cjs / typescript.ts 审查报告 + 覆盖率报告(292/292 条零偏差,126 种规则全覆盖) - demo-sqlfluff:11 个 SQL 样例审查报告 + 覆盖率报告(113/113 条零偏差,57 种规则全覆盖,含 JJ01 行号缺陷修复后的复测验证) - demo-stylelint:common.css / empty-source.css 审查报告 + 覆盖率报告(145/145 条零偏差,68 种规则全覆盖)
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
{
|
||||
"enabled": [
|
||||
"AL02",
|
||||
"AL03",
|
||||
"AL04",
|
||||
"AL05",
|
||||
"AL06",
|
||||
"AL08",
|
||||
"AL09",
|
||||
"AL10",
|
||||
"AM01",
|
||||
"AM02",
|
||||
"AM03",
|
||||
"AM05",
|
||||
"AM06",
|
||||
"AM08",
|
||||
"CP01",
|
||||
"CP02",
|
||||
"CP03",
|
||||
"CP04",
|
||||
"CP05",
|
||||
"CV01",
|
||||
"CV02",
|
||||
"CV03",
|
||||
"CV04",
|
||||
"CV05",
|
||||
"CV06",
|
||||
"CV08",
|
||||
"CV12",
|
||||
"JJ01",
|
||||
"LT01",
|
||||
"LT02",
|
||||
"LT05",
|
||||
"LT06",
|
||||
"LT07",
|
||||
"LT08",
|
||||
"LT10",
|
||||
"LT11",
|
||||
"LT12",
|
||||
"LT13",
|
||||
"LT14",
|
||||
"LT15",
|
||||
"RF01",
|
||||
"RF02",
|
||||
"RF04",
|
||||
"RF05",
|
||||
"RF06",
|
||||
"ST01",
|
||||
"ST02",
|
||||
"ST03",
|
||||
"ST04",
|
||||
"ST05",
|
||||
"ST06",
|
||||
"ST07",
|
||||
"ST08",
|
||||
"ST09",
|
||||
"ST10",
|
||||
"ST11",
|
||||
"ST12"
|
||||
],
|
||||
"mapping": {
|
||||
"AL02": [
|
||||
"01_aliasing.sql:5"
|
||||
],
|
||||
"AL03": [
|
||||
"03_caps.sql:21",
|
||||
"03_caps.sql:22",
|
||||
"03_caps.sql:36",
|
||||
"03_caps.sql:37",
|
||||
"07_structure.sql:50"
|
||||
],
|
||||
"AL04": [
|
||||
"01_aliasing.sql:21"
|
||||
],
|
||||
"AL05": [
|
||||
"01_aliasing.sql:27",
|
||||
"02_ambiguity.sql:60"
|
||||
],
|
||||
"AL08": [
|
||||
"01_aliasing.sql:32",
|
||||
"07_structure.sql:42"
|
||||
],
|
||||
"AL09": [
|
||||
"01_aliasing.sql:38"
|
||||
],
|
||||
"AL10": [
|
||||
"01_aliasing.sql:46"
|
||||
],
|
||||
"AM01": [
|
||||
"02_ambiguity.sql:2"
|
||||
],
|
||||
"AM02": [
|
||||
"02_ambiguity.sql:16",
|
||||
"05_layout.sql:41"
|
||||
],
|
||||
"AM03": [
|
||||
"02_ambiguity.sql:28"
|
||||
],
|
||||
"AM05": [
|
||||
"01_aliasing.sql:20",
|
||||
"02_ambiguity.sql:38",
|
||||
"02_ambiguity.sql:59",
|
||||
"04_convention.sql:50",
|
||||
"06_references.sql:12",
|
||||
"07_structure.sql:45",
|
||||
"07_structure.sql:62",
|
||||
"07_structure.sql:77"
|
||||
],
|
||||
"AM06": [
|
||||
"02_ambiguity.sql:50"
|
||||
],
|
||||
"AM08": [
|
||||
"02_ambiguity.sql:59"
|
||||
],
|
||||
"CP01": [
|
||||
"03_caps.sql:11",
|
||||
"03_caps.sql:14",
|
||||
"03_caps.sql:16",
|
||||
"03_caps.sql:20",
|
||||
"03_caps.sql:23",
|
||||
"03_caps.sql:27",
|
||||
"03_caps.sql:29",
|
||||
"03_caps.sql:31",
|
||||
"03_caps.sql:32",
|
||||
"03_caps.sql:32",
|
||||
"03_caps.sql:32",
|
||||
"03_caps.sql:35",
|
||||
"03_caps.sql:36",
|
||||
"03_caps.sql:37",
|
||||
"03_caps.sql:38"
|
||||
],
|
||||
"CP02": [
|
||||
"03_caps.sql:12",
|
||||
"03_caps.sql:17"
|
||||
],
|
||||
"CP03": [
|
||||
"03_caps.sql:22"
|
||||
],
|
||||
"CP04": [
|
||||
"03_caps.sql:32"
|
||||
],
|
||||
"CP05": [
|
||||
"03_caps.sql:37"
|
||||
],
|
||||
"CV01": [
|
||||
"04_convention.sql:7"
|
||||
],
|
||||
"CV02": [
|
||||
"04_convention.sql:11"
|
||||
],
|
||||
"CV03": [
|
||||
"04_convention.sql:18"
|
||||
],
|
||||
"CV04": [
|
||||
"04_convention.sql:24"
|
||||
],
|
||||
"CV05": [
|
||||
"04_convention.sql:34"
|
||||
],
|
||||
"CV06": [
|
||||
"04_convention.sql:61"
|
||||
],
|
||||
"CV08": [
|
||||
"04_convention.sql:41"
|
||||
],
|
||||
"CV12": [
|
||||
"04_convention.sql:50",
|
||||
"04_convention.sql:52"
|
||||
],
|
||||
"JJ01": [
|
||||
"08_jinja.sql:2"
|
||||
],
|
||||
"LT01": [
|
||||
"05_layout.sql:2",
|
||||
"05_layout.sql:19",
|
||||
"07_structure.sql:67"
|
||||
],
|
||||
"LT02": [
|
||||
"01_aliasing.sql:21",
|
||||
"02_ambiguity.sql:39",
|
||||
"04_convention.sql:42",
|
||||
"05_layout.sql:5",
|
||||
"05_layout.sql:25",
|
||||
"06_references.sql:13",
|
||||
"07_structure.sql:46",
|
||||
"07_structure.sql:63",
|
||||
"07_structure.sql:67",
|
||||
"07_structure.sql:78",
|
||||
"07_structure.sql:94"
|
||||
],
|
||||
"LT05": [
|
||||
"05_layout.sql:13",
|
||||
"07_structure.sql:1"
|
||||
],
|
||||
"LT06": [
|
||||
"05_layout.sql:19"
|
||||
],
|
||||
"LT07": [
|
||||
"05_layout.sql:25"
|
||||
],
|
||||
"LT08": [
|
||||
"05_layout.sql:26",
|
||||
"05_layout.sql:32",
|
||||
"07_structure.sql:23"
|
||||
],
|
||||
"LT10": [
|
||||
"07_structure.sql:66"
|
||||
],
|
||||
"LT11": [
|
||||
"05_layout.sql:41"
|
||||
],
|
||||
"LT12": [
|
||||
"01_aliasing.sql:47",
|
||||
"02_ambiguity.sql:61",
|
||||
"03_caps.sql:40",
|
||||
"04_convention.sql:63",
|
||||
"05_layout.sql:42",
|
||||
"05b_layout_misc.sql:10",
|
||||
"05c_file_boundaries.sql:4",
|
||||
"06_references.sql:32",
|
||||
"07_structure.sql:101",
|
||||
"08_jinja.sql:6"
|
||||
],
|
||||
"LT13": [
|
||||
"05c_file_boundaries.sql:1"
|
||||
],
|
||||
"LT14": [
|
||||
"05b_layout_misc.sql:2"
|
||||
],
|
||||
"LT15": [
|
||||
"05b_layout_misc.sql:8"
|
||||
],
|
||||
"RF01": [
|
||||
"06_references.sql:3"
|
||||
],
|
||||
"RF02": [
|
||||
"06_references.sql:9"
|
||||
],
|
||||
"RF04": [
|
||||
"06_references.sql:19",
|
||||
"07_structure.sql:35"
|
||||
],
|
||||
"RF05": [
|
||||
"06_references.sql:23"
|
||||
],
|
||||
"RF06": [
|
||||
"06_references.sql:29"
|
||||
],
|
||||
"ST01": [
|
||||
"07_structure.sql:3"
|
||||
],
|
||||
"ST02": [
|
||||
"07_structure.sql:13"
|
||||
],
|
||||
"ST03": [
|
||||
"07_structure.sql:20"
|
||||
],
|
||||
"ST04": [
|
||||
"07_structure.sql:32"
|
||||
],
|
||||
"ST05": [
|
||||
"07_structure.sql:46"
|
||||
],
|
||||
"ST06": [
|
||||
"07_structure.sql:49"
|
||||
],
|
||||
"ST07": [
|
||||
"07_structure.sql:63"
|
||||
],
|
||||
"ST08": [
|
||||
"07_structure.sql:67"
|
||||
],
|
||||
"ST09": [
|
||||
"07_structure.sql:78"
|
||||
],
|
||||
"ST10": [
|
||||
"07_structure.sql:86"
|
||||
],
|
||||
"ST11": [
|
||||
"04_convention.sql:42",
|
||||
"07_structure.sql:94"
|
||||
],
|
||||
"ST12": [
|
||||
"07_structure.sql:100"
|
||||
],
|
||||
"AL06": [
|
||||
"09_aliasing_length.sql:6"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\01_aliasing.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 102.5s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 12 | 错误: 10 | 警告: 1 | 建议: 1
|
||||
|
||||
静态分析 · 9 个问题
|
||||
|
||||
- 🔴 `sqlfluff:AL02` L5
|
||||
第 5 行:列别名使用隐式/显式混用的别名形式。
|
||||
建议: 请为列别名显式添加 AS 关键字,例如将 `col1 col1_alias` 改为 `col1 AS col1_alias`。
|
||||
- 🔴 `sqlfluff:AM05` L20
|
||||
第 20 行:JOIN 子句应使用完全限定的列引用。
|
||||
建议: 为每个表使用唯一别名,并确保 ON 条件中的列都以该表别名限定;例如 `ON t1.id = t2.ref_id`,而不是 `other_table.ref_id`。
|
||||
- 🔴 `sqlfluff:AL04` L21
|
||||
第 21 行:表别名 't1' 重复使用。表别名应保持唯一。
|
||||
建议: 将第二张表 other_table 的别名改为唯一别名(如 t2),并同步修改 ON 条件中的引用为 `t1.id = t2.ref_id`。
|
||||
- 🔴 `sqlfluff:LT02` L21
|
||||
第 21 行:'ON' 前应换行且不应有缩进。
|
||||
建议: 将 ON 放到新的行,并不加额外缩进:`other_table AS t1\nON t1.id = other_table.ref_id;`
|
||||
- 🔴 `sqlfluff:AL05` L27
|
||||
第 27 行:表别名 'unused_alias' 在 SELECT 语句中从未使用。
|
||||
建议: 移除未使用的表别名,直接使用表名;或若该别名有意义,在查询中引用它。
|
||||
- 🔴 `sqlfluff:AL08` L32
|
||||
第 32 行:列别名 'shared_alias' 与第 31 行的别名重复。
|
||||
建议: 为这两列设置不同的别名,例如将第 32 行的 `name AS shared_alias` 改为 `name AS name_alias`。
|
||||
- 🔴 `sqlfluff:AL09` L38
|
||||
第 38 行:列不应使用与列名相同的别名(自别名)。
|
||||
建议: 移除 `AS id`,直接写 `id`;或改成一个有意义的别名如 `id_alias`。
|
||||
- 🔴 `sqlfluff:AL10` L46
|
||||
第 46 行:派生表必须指定别名。
|
||||
建议: 为子查询添加表别名,例如 `(SELECT COUNT(*) AS cnt FROM my_table) AS cnt_table`。
|
||||
- 🔴 `sqlfluff:LT12` L47
|
||||
第 47 行:文件必须以单个尾随换行符结束。
|
||||
建议: 在文件末尾添加一个换行符,并确保没有多余的空行或行尾空格。
|
||||
|
||||
AI 审查 · 3 条建议
|
||||
|
||||
- 🔴 [AI] [bug] `invalid-alias-original-table-reference` L21
|
||||
**ON 条件中混用原始表名与别名**
|
||||
第 21 行中,`other_table AS t1` 为表设置了别名,但随后 ON 子句使用 `other_table.ref_id` 引用该表。大多数 SQL 方言中,一旦表指定别名,就必须用别名限定列,不能再用原始表名;且该查询还存在重复别名 t1,导致 `t1.id` 指代不明。
|
||||
建议: 为 other_table 使用唯一别名(如 t2),并将 ON 条件改为 `t1.id = t2.ref_id`,同时保持两个表别名唯一。
|
||||
- 🟡 [AI] [performance] `redundant-derived-table` L46
|
||||
**不必要的外层查询嵌套**
|
||||
第 43-46 行 `SELECT cnt FROM (SELECT COUNT(*) AS cnt FROM my_table)` 先用子查询计算 COUNT(*),再由外层选择 cnt;这层封装没有实际作用,反而让 SQL 更难阅读且可能妨碍优化器简化执行计划。
|
||||
建议: 直接写成 `SELECT COUNT(*) AS cnt FROM my_table;`,既避免派生表别名问题,也减少不必要的嵌套。
|
||||
- 🔵 [AI] [style] `missing-column-alias` L11
|
||||
**聚合列缺少别名**
|
||||
第 11 行 `SUM(amount)` 没有显式别名,返回列名会依赖具体数据库的默认命名规则,不利于后续代码维护与结果集访问。
|
||||
建议: 为聚合表达式添加有意义且稳定的别名,例如 `SUM(amount) AS total_amount`。
|
||||
@@ -0,0 +1,60 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\02_ambiguity.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 182.4s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 13 | 错误: 10 | 警告: 2 | 建议: 1
|
||||
|
||||
静态分析 · 10 个问题
|
||||
|
||||
- 🔴 `sqlfluff:AM01` L6
|
||||
第 6 行:在带有 GROUP BY 的 SELECT 语句中使用 DISTINCT 含义模糊。
|
||||
建议: 去掉 DISTINCT 或 GROUP BY 中的一个。若只需按 dept、region 去重,可保留 GROUP BY 并去掉 DISTINCT;若不需要分组聚合,则保留 DISTINCT 并去掉 GROUP BY。
|
||||
- 🔴 `sqlfluff:AM02` L20
|
||||
第 20 行:建议使用 UNION DISTINCT 或 UNION ALL,而不是裸 UNION。
|
||||
建议: 根据语义选择:若希望保留所有重复行,把 UNION 改为 UNION ALL;若希望去重,改为 UNION DISTINCT(需方言支持,如 ansi/bigquery/mysql)。
|
||||
- 🔴 `sqlfluff:AM03` L32
|
||||
第 32 行:ORDER BY 子句含义模糊;ORDER BY 应对所有列都指定排序方向,或所有列都不指定。
|
||||
建议: 若只想让 a 升序、b 降序,显式写为 ORDER BY a ASC, b DESC;如果想让两列都降序,则写为 ORDER BY a DESC, b DESC。
|
||||
- 🔴 `sqlfluff:AM05` L42
|
||||
第 42 行:JOIN 子句应使用完整限定形式。
|
||||
建议: 将 JOIN 改为 INNER JOIN(或根据语义使用 LEFT/RIGHT/FULL JOIN),明确连接类型。
|
||||
- 🔴 `sqlfluff:LT02` L43
|
||||
第 43 行:'ON' 前应有换行,且 'ON' 前不要有缩进。
|
||||
建议: 把 ON 子句放到新行,并确保 ON 前没有额外缩进,例如:JOIN
|
||||
other_table AS b
|
||||
ON a.id = b.id;
|
||||
- 🔴 `sqlfluff:AM06` L54
|
||||
第 54 行:GROUP BY 与 ORDER BY 中的列引用方式不一致。
|
||||
建议: 统一引用方式:要么都使用列名(GROUP BY a, b 和 ORDER BY a, b),要么都使用序号(GROUP BY 1, 2 和 ORDER BY 1, 2);推荐使用列名,避免 SELECT 列表变化时失效。
|
||||
- 🔴 `sqlfluff:AM05` L63
|
||||
第 63 行:JOIN 子句应使用完整限定形式。
|
||||
建议: 若确实要交叉连接,显式写 CROSS JOIN;若本意是内连接,请补充 ON 条件并写 INNER JOIN。
|
||||
- 🔴 `sqlfluff:AM08` L63
|
||||
第 63 行:检测到隐式交叉连接(JOIN 缺少 ON 条件)。
|
||||
建议: 如果意图是笛卡尔积,请显式写 CROSS JOIN;如果本意是按某列关联,请补上 ON 连接条件。
|
||||
- 🔴 `sqlfluff:AL05` L64
|
||||
第 64 行:别名 'b' 在 SELECT 语句中从未被使用。
|
||||
建议: 移除未使用的别名 b;若后续需要引用 other_table 的列,请使用 b.列名。
|
||||
- 🔴 `sqlfluff:LT12` L65
|
||||
第 65 行:文件必须以单个尾随换行符结束。
|
||||
建议: 在文件末尾保留且仅保留一个换行符,不要有多余空行或缺少换行。
|
||||
|
||||
AI 审查 · 3 条建议
|
||||
|
||||
- 🟡 [AI] [design] `dialect-dependent-lint-config` L2
|
||||
**AM02 规则受方言门控,默认配置下会漏报**
|
||||
顶部注释说明 AM02 仅对 ansi/bigquery/mysql 等支持 UNION DISTINCT 的方言生效,默认方言 oracle 不在白名单。如果 CI 使用默认 SQLFluff 配置,即使启用所有规则也不会报告第 20 行的裸 UNION,可能造成本地与 CI 的检查结果不一致。
|
||||
建议: 在项目 .sqlfluff 中显式设置目标方言(例如 ansi),并确保本地与 CI 使用同一配置;如果目标方言确为 oracle,应关闭或忽略 AM02。
|
||||
- 🟡 [AI] [bug] `unnecessary-cartesian-join` L63
|
||||
**最终查询的交叉连接可能完全是多余连接,产生重复和错误结果**
|
||||
第 59-64 行 SELECT a.id 只引用了 my_table 的别名 a,却对 other_table 做无 ON 条件的 JOIN(隐式交叉连接)。只要 other_table 非空,每个 a.id 会被重复 other_table 行数次;若 other_table 为空,查询反而返回空集。这通常不是预期语义,也会带来不必要的笛卡尔积开销。
|
||||
建议: 如果只想返回 my_table 的所有 id,直接删除 JOIN other_table;如果只想返回在 other_table 中有匹配的 id,应使用 INNER JOIN ... ON 并配合 SELECT DISTINCT 或 EXISTS,避免重复。
|
||||
- 🔵 [AI] [design] `stale-rule-comments` L14
|
||||
**修复后示例注释将与 SQL 语义不一致**
|
||||
代码中的 -- AM01、-- AM02 等注释标记的是静态分析规则样例。一旦按上述建议应用修复(例如删除 DISTINCT、把 UNION 改为 UNION ALL、把 JOIN 改为 INNER JOIN/CROSS JOIN),这些注释就不再准确,会误导后续阅读者。
|
||||
建议: 在应用修复后同步更新或删除对应注释;或将原始违规样例与修复后样例分开存放,并注明预期触发的规则。
|
||||
@@ -0,0 +1,107 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\03_caps.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 190.8s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 29 | 错误: 25 | 警告: 2 | 建议: 2
|
||||
|
||||
静态分析 · 25 个问题
|
||||
|
||||
- 🔴 `sqlfluff:CP01` L11
|
||||
第11行:关键字必须一律小写。
|
||||
建议: 将 SELECT 改为小写 select。
|
||||
- 🔴 `sqlfluff:CP02` L12
|
||||
第12行:未加引号的标识符必须一律小写。
|
||||
建议: 将 colA 改为小写 cola,并确保其他位置的同一标识符也统一小写。
|
||||
- 🔴 `sqlfluff:CP01` L14
|
||||
第14行:关键字必须一律小写。
|
||||
建议: 将 FROM 改为小写 from。
|
||||
- 🔴 `sqlfluff:CP01` L16
|
||||
第16行:关键字必须一律小写。
|
||||
建议: 将 WHERE 改为小写 where。
|
||||
- 🔴 `sqlfluff:CP02` L17
|
||||
第17行:未加引号的标识符必须一律小写。
|
||||
建议: 将 coLa 改为小写 cola,与第12行的 colA 统一。
|
||||
- 🔴 `sqlfluff:CP01` L20
|
||||
第20行:关键字必须一律小写。
|
||||
建议: 将 SELECT 改为小写 select。
|
||||
- 🔴 `sqlfluff:AL03` L21
|
||||
第21行:列表达式缺少别名。请使用显式 AS 子句。
|
||||
建议: 为 SUM(amount) 添加别名,例如:SUM(amount) AS total_amount。
|
||||
- 🔴 `sqlfluff:AL03` L22
|
||||
第22行:列表达式缺少别名。请使用显式 AS 子句。
|
||||
建议: 为 sum(quantity) 添加别名,例如:sum(quantity) AS total_quantity。
|
||||
- 🔴 `sqlfluff:CP03` L22
|
||||
第22行:函数名必须一律大写。
|
||||
建议: 将 sum 改为 SUM,与第21行的 SUM 保持一致。
|
||||
- 🔴 `sqlfluff:CP01` L23
|
||||
第23行:关键字必须一律小写。
|
||||
建议: 将 FROM 改为小写 from。
|
||||
- 🔴 `sqlfluff:CP01` L27
|
||||
第27行:关键字必须一律小写。
|
||||
建议: 将 SELECT 改为小写 select。
|
||||
- 🔴 `sqlfluff:CP01` L29
|
||||
第29行:关键字必须一律小写。
|
||||
建议: 将 FROM 改为小写 from。
|
||||
- 🔴 `sqlfluff:CP01` L31
|
||||
第31行:关键字必须一律小写。
|
||||
建议: 将 WHERE 改为小写 where。
|
||||
- 🔴 `sqlfluff:CP01` L32
|
||||
第32行:关键字必须一律小写。
|
||||
建议: 将 flag IS NULL 中的 IS 改为 is。
|
||||
- 🔴 `sqlfluff:CP01` L32
|
||||
第32行:关键字必须一律小写。
|
||||
建议: 将 OR 改为 or。
|
||||
- 🔴 `sqlfluff:CP01` L32
|
||||
第32行:关键字必须一律小写。
|
||||
建议: 将 other_flag IS null 中的 IS 改为 is。
|
||||
- 🔴 `sqlfluff:CP04` L32
|
||||
第32行:布尔/null 字面量必须一律大写。
|
||||
建议: 将小写 null 改为 NULL。
|
||||
- 🔴 `sqlfluff:CP01` L35
|
||||
第35行:关键字必须一律小写。
|
||||
建议: 将 SELECT 改为小写 select。
|
||||
- 🔴 `sqlfluff:AL03` L36
|
||||
第36行:列表达式缺少别名。请使用显式 AS 子句。
|
||||
建议: 为 CAST(id AS VARCHAR) 添加别名,例如:CAST(id AS VARCHAR) AS id_string。
|
||||
- 🔴 `sqlfluff:CP01` L36
|
||||
第36行:关键字必须一律小写。
|
||||
建议: 将 AS 改为小写 as。
|
||||
- 🔴 `sqlfluff:AL03` L37
|
||||
第37行:列表达式缺少别名。请使用显式 AS 子句。
|
||||
建议: 为 CAST(amount AS varchar) 添加别名,例如:CAST(amount AS varchar) AS amount_string。
|
||||
- 🔴 `sqlfluff:CP01` L37
|
||||
第37行:关键字必须一律小写。
|
||||
建议: 将 AS 改为小写 as。
|
||||
- 🔴 `sqlfluff:CP05` L37
|
||||
第37行:数据类型必须一律大写。
|
||||
建议: 将 varchar 改为 VARCHAR,与第36行的 VARCHAR 保持一致。
|
||||
- 🔴 `sqlfluff:CP01` L38
|
||||
第38行:关键字必须一律小写。
|
||||
建议: 将 FROM 改为小写 from。
|
||||
- 🔴 `sqlfluff:LT12` L40
|
||||
第40行:文件必须以单个尾部换行结束。
|
||||
建议: 在文件末尾添加一个换行符,并确保没有多余的空行。
|
||||
|
||||
AI 审查 · 4 条建议
|
||||
|
||||
- 🟡 [AI] [bug] `or-condition-maybe-and` L32
|
||||
**OR 条件可能将意图的 AND 写成 OR,导致结果范围扩大**
|
||||
第32行 `flag IS NULL OR other_flag IS null` 会返回只要任一标志为 NULL 的行;如果业务意图是查找两个标志都为 NULL 的行,应使用 AND。请确认实际语义。
|
||||
建议: 若意图是同时为 NULL,请将 OR 改为 AND。
|
||||
- 🟡 [AI] [bug] `cast-without-length` L36
|
||||
**CAST 未指定目标长度,可能导致数据截断**
|
||||
第36行和第37行的 CAST 将列转换为 VARCHAR 时未指定长度。不同数据库对无长度 VARCHAR 的默认行为不同(如 SQL Server 默认 30,Oracle 要求必须指定),若 id/amount 超出默认长度将产生静默截断。
|
||||
建议: 为 CAST 指定明确的长度,例如 CAST(id AS VARCHAR(20))、CAST(amount AS VARCHAR(20)),或根据业务使用更合适的类型。
|
||||
- 🔵 [AI] [bug] `sum-may-return-null` L21
|
||||
**聚合函数 SUM 在无行时返回 NULL,可能影响下游计算**
|
||||
第21、22行的 SUM(amount) 和 sum(quantity) 如果没有匹配行,将返回 NULL 而非 0。如果业务逻辑期望数值 0,应使用 COALESCE 或 IFNULL 包裹。
|
||||
建议: 使用 COALESCE(SUM(amount), 0) 和 COALESCE(SUM(quantity), 0) 以避免空值。
|
||||
- 🔵 [AI] [performance] `or-condition-index-performance` L32
|
||||
**WHERE 中使用 OR 可能阻止索引使用**
|
||||
第32行对两个可空字段使用 OR 条件,这类谓词通常难以利用索引,在数据量大时容易导致全表扫描。
|
||||
建议: 若两个条件的选择性都较低,可考虑改写为 UNION ALL 以分别利用索引(注意重复行问题),或在应用层拆分查询。如果数据量不大,保持 OR 也可接受。
|
||||
@@ -0,0 +1,63 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\04_convention.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 202.1s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 15 | 错误: 14 | 警告: 1 | 建议: 0
|
||||
|
||||
静态分析 · 13 个问题
|
||||
|
||||
- 🔴 `sqlfluff:CV01` L7
|
||||
L7:`!=` 与 `<>` 混用,“不等于”运算符应保持一致。
|
||||
建议: 将第 7 行中的 `<>` 改为 `!=`,使同一语句统一使用一种“不等于”写法。
|
||||
- 🔴 `sqlfluff:CV02` L11
|
||||
L11:应使用 `COALESCE` 而不是 `IFNULL`。
|
||||
建议: 将 `IFNULL(amount, 0)` 替换为 `COALESCE(amount, 0)`,以保持 SQL 标准兼容。
|
||||
- 🔴 `sqlfluff:CV03` L18
|
||||
L18:SELECT 列表中不允许出现尾随逗号。
|
||||
建议: 删除 `name` 后面的逗号,使其直接换行到 `FROM`。
|
||||
- 🔴 `sqlfluff:CV04` L24
|
||||
L24:统计行数应使用统一的 `COUNT(*)` 语法。
|
||||
建议: 将 `COUNT(1)` 改为 `COUNT(*)`。
|
||||
- 🔴 `sqlfluff:CV05` L34
|
||||
L34:与 NULL 比较应使用 `IS NULL` / `IS NOT NULL`,不能使用 `= NULL`。
|
||||
建议: 将 `end_date = NULL` 改为 `end_date IS NULL`;若想查非空则改为 `end_date IS NOT NULL`。
|
||||
- 🔴 `sqlfluff:CV08` L41
|
||||
L41:应使用 `LEFT JOIN` 而不是 `RIGHT JOIN`。
|
||||
建议: 将 `RIGHT JOIN` 改写为 `LEFT JOIN`;若需保持原语义,应调换表顺序并改写为 `other_table AS b LEFT JOIN my_table AS a ON a.id = b.ref_id`。
|
||||
- 🔴 `sqlfluff:ST11` L42
|
||||
L42:连接表 `other_table AS b` 在查询的其他位置未被引用。
|
||||
建议: 如果 `other_table AS b` 只是用于过滤,建议改为 `WHERE EXISTS (SELECT 1 FROM other_table AS b WHERE b.ref_id = a.id)`;若确实需要 b 的数据,应把 b 的字段加入 SELECT,否则移除该 JOIN。
|
||||
- 🔴 `sqlfluff:LT02` L42
|
||||
L42:`ON` 前应有换行,且 `ON` 不应缩进。
|
||||
建议: 在 `ON` 前插入换行,并让 `ON` 从行首开始,不要缩进。
|
||||
- 🔴 `sqlfluff:AM05` L50
|
||||
L50:JOIN 子句应使用完整的连接条件(ON),不能只依赖 WHERE。
|
||||
建议: 改为 `JOIN other_table AS b ON a.id = b.ref_id`,并从 WHERE 中移除该条件,避免产生隐式交叉连接。
|
||||
- 🔴 `sqlfluff:CV12` L50
|
||||
L50:连接条件应使用 `JOIN ... ON ...`,不要使用 `WHERE`。
|
||||
建议: 在 `JOIN` 后直接写 `ON a.id = b.ref_id`。
|
||||
- 🔴 `sqlfluff:CV12` L52
|
||||
L52:连接条件已写在 WHERE 中,应移入 JOIN 的 ON 子句。
|
||||
建议: 删除 WHERE,并把 `a.id = b.ref_id` 作为 JOIN 的 ON 条件。
|
||||
- 🔴 `sqlfluff:CV06` L61
|
||||
L61:语句必须以分号结束;分号应紧跟在语句最后,而不是单独成行。
|
||||
建议: 删除第 62 行单独的分号,并将第 61 行末尾补上分号:`status_code = 'active';`。
|
||||
- 🔴 `sqlfluff:LT12` L63
|
||||
L63:文件必须以一个单独的尾随换行符结尾。
|
||||
建议: 在文件末尾添加一个换行符,并确保不要出现多个空行。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🔴 [AI] [bug] `boolean-or-instead-of-and` L7
|
||||
**OR 条件导致状态过滤几乎恒真**
|
||||
`status_code != 'active' OR status_code != 'pending'` 对所有非 NULL 的 status_code 都为真:当 status_code='active' 时第二个条件为真,当 status_code='pending' 时第一个条件为真。因此实际会返回除 NULL 外的所有行,与预期的“排除 active/pending”很可能相反。
|
||||
建议: 改为 `status_code != 'active' AND status_code != 'pending'` 或 `status_code NOT IN ('active', 'pending')`。若需把 NULL 也包含,请使用 `COALESCE(status_code, '') NOT IN ('active', 'pending')`。
|
||||
- 🟡 [AI] [design] `right-join-unused-left-columns` L41
|
||||
**RIGHT JOIN 仅选择左表字段会产生 NULL 并可能产生重复行**
|
||||
`my_table AS a RIGHT JOIN other_table AS b` 会保留所有 other_table 行;未被匹配时 a.id 为 NULL,且若 other_table 中 ref_id 重复会导致 a.id 重复。当前 SELECT 只输出 a.id,未输出 b 的任何列,这个 RIGHT JOIN 的语义很可能不是期望的。
|
||||
建议: 若目标是保留 my_table 并以 other_table 过滤/补列,应改为 `my_table AS a LEFT JOIN other_table AS b`;若目标确实是保留 other_table 全量行,应把 other_table 放在 FROM 左侧并 `LEFT JOIN my_table`,同时检查是否需要输出 b 的列。
|
||||
@@ -0,0 +1,76 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\05_layout.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 176.0s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 16 | 错误: 12 | 警告: 3 | 建议: 1
|
||||
|
||||
静态分析 · 12 个问题
|
||||
|
||||
- 🔴 `sqlfluff:LT01` L2
|
||||
第2行:'*' 前应只有单个空格,实际有 2 个空格。
|
||||
建议: 删除 `SELECT` 与 `*` 之间的一个空格,改为 `SELECT * FROM my_table;`。
|
||||
- 🔴 `sqlfluff:LT02` L5
|
||||
第5行:该行不应有缩进。
|
||||
建议: 删除第5行行首的 4 个空格,使 `SELECT` 顶格书写。
|
||||
- 🔴 `sqlfluff:LT05` L13
|
||||
第13行:行太长(95 个字符超过 80 上限)。
|
||||
建议: 建议缩短该列名,或在 sqlfluff 配置中调整 `max_line_length`;如果这是演示代码,也可以在该行使用 `-- noqa: LT05` 忽略此规则。
|
||||
- 🔴 `sqlfluff:LT01` L19
|
||||
第19行:左括号 '(' 前存在多余空白。
|
||||
建议: 删除 `SUM` 与 `(` 之间的空格,改为 `SUM(amount)`。
|
||||
- 🔴 `sqlfluff:LT06` L19
|
||||
第19行:函数名 `SUM` 后面未紧接左括号。
|
||||
建议: 将 `SUM (amount)` 改为 `SUM(amount)`,函数名与左括号之间不要留空格。
|
||||
- 🔴 `sqlfluff:LT02` L25
|
||||
第25行:')' 前应换行且不应有缩进。
|
||||
建议: 把 `)` 移到下一行并顶格,即写成:
|
||||
` SELECT id FROM my_table
|
||||
)`。
|
||||
- 🔴 `sqlfluff:LT07` L25
|
||||
第25行:WITH 子句的右括号应单独另起一行。
|
||||
建议: 将 WITH 子句的 `)` 放到 `SELECT id FROM my_table` 之后的独立行,且行首不要缩进。
|
||||
- 🔴 `sqlfluff:LT08` L26
|
||||
第26行:CTE 闭括号之后缺少空行。
|
||||
建议: 在 `)` 与 `SELECT * FROM cte;` 之间插入一个空行。
|
||||
- 🔴 `sqlfluff:LT08` L32
|
||||
第32行:CTE 闭括号之后缺少空行。
|
||||
建议: 在 `)` 与 `SELECT * FROM cte;` 之间插入一个空行。
|
||||
- 🔴 `sqlfluff:AM02` L41
|
||||
第41行:建议使用 `UNION DISTINCT` 或 `UNION ALL`,不要单独使用 `UNION`。
|
||||
建议: 若需要去重,改用 `UNION DISTINCT`;若不需要去重,改用 `UNION ALL`(通常性能更好)。
|
||||
- 🔴 `sqlfluff:LT11` L41
|
||||
第41行:集合运算符前后都应加换行。
|
||||
建议: 将 `UNION` 放到单独一行,并让它前面和后面各有一行,例如:
|
||||
`SELECT id FROM my_table
|
||||
UNION
|
||||
SELECT id FROM other_table;`
|
||||
- 🔴 `sqlfluff:LT12` L42
|
||||
文件末尾:文件必须以一个单独的换行符结束。
|
||||
建议: 在最后一行之后补一个换行符,并确保末尾没有多余空行。
|
||||
|
||||
AI 审查 · 4 条建议
|
||||
|
||||
- 🟡 [AI] [design] `long-identifier` L13
|
||||
**过长的标识符影响可移植性与可维护性**
|
||||
第13行的列名本身接近80个字符,即使不考虑第13行的行长度限制,这个标识符也过长,可能超过部分数据库的标识符上限(如 MySQL 64、PostgreSQL 63、Oracle 128),并显著影响可读性与可维护性。
|
||||
建议: 在建表/查询中改用简短、有意义的列名(如 `long_total`),并在应用层建立字段映射;若不能改名,请确认目标数据库的标识符上限并添加注释。
|
||||
- 🟡 [AI] [performance] `redundant-distinct` L35
|
||||
**DISTINCT 可能冗余**
|
||||
第35行 `SELECT DISTINCT id FROM my_table;` 如果 `id` 是主键或唯一列,`DISTINCT` 不会改变结果,却会引入额外的排序/哈希操作,增加查询开销。请确认 `id` 的唯一性;若已唯一,应去掉 `DISTINCT`。
|
||||
建议: 若 `id` 已唯一,将 `SELECT DISTINCT id` 改为 `SELECT id`。
|
||||
- 🟡 [AI] [bug] `union-compatible-types` L41
|
||||
**UNION 两侧字段类型需保证兼容**
|
||||
第41行通过 `UNION` 合并 `my_table.id` 与 `other_table.id`。若两侧类型不同,数据库将进行隐式转换,可能造成转换错误、精度损失或性能下降。建议显式转换为同一类型后再合并。
|
||||
建议: 根据实际类型显式转换,例如:
|
||||
`SELECT CAST(id AS INTEGER) FROM my_table`
|
||||
`UNION`
|
||||
`SELECT CAST(id AS INTEGER) FROM other_table;`
|
||||
- 🔵 [AI] [design] `avoid-select-star` L2
|
||||
**避免使用 SELECT ***
|
||||
第2、26、32行使用了 `SELECT *`。如果表结构日后新增列,结果集会随之变化,可能意外暴露敏感字段并增加不必要的数据传输。建议在正式查询中显式列出所需字段。
|
||||
建议: 将 `SELECT *` 改为显式字段列表,例如只需要 `id` 时写 `SELECT id`。
|
||||
@@ -0,0 +1,34 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\05b_layout_misc.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 89.3s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 5 | 错误: 3 | 警告: 1 | 建议: 1
|
||||
|
||||
静态分析 · 3 个问题
|
||||
|
||||
- 🔴 `sqlfluff:LT14` L2
|
||||
L2:'GROUP' 关键字应始终另起一行。
|
||||
建议: 将 GROUP BY 子句移到新行。例如:SELECT id FROM my_table
|
||||
GROUP BY id;
|
||||
- 🔴 `sqlfluff:LT15` L8
|
||||
L8:连续空行过多。
|
||||
建议: 将第6-8行的连续空行减少为一个空行(删除第7和第8行)。
|
||||
- 🔴 `sqlfluff:LT12` L10
|
||||
L10:文件必须以单个尾随换行符结尾。
|
||||
建议: 确保文件末尾只有一个换行符,删除多余的空行或在末尾添加一个换行符。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🟡 [AI] [bug] `inconsistent-query-semantics` L5
|
||||
**去重逻辑不一致**
|
||||
第2行使用 GROUP BY id 去重,而第5行和第9行使用 SELECT id 返回所有行,导致相同的表在不同行返回的行数可能不同。如果意图是去重,应统一使用 SELECT DISTINCT id; 如果意图是返回所有行,则第2行应去掉 GROUP BY。
|
||||
建议: 统一使用 SELECT DISTINCT id FROM my_table; 以明确表达去重意图。
|
||||
- 🔵 [AI] [design] `group-by-without-aggregation` L2
|
||||
**GROUP BY 未使用聚合函数**
|
||||
第2行的 GROUP BY id 没有聚合函数,仅用于去重。使用 GROUP BY 去重可读性较差,且可能引入不必要的分组开销。建议使用 SELECT DISTINCT id 替代。
|
||||
建议: 将 SELECT id FROM my_table GROUP BY id; 替换为 SELECT DISTINCT id FROM my_table;,以更高效、清晰地表达去重语义。
|
||||
@@ -0,0 +1,30 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\05c_file_boundaries.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 87.6s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 4 | 错误: 2 | 警告: 1 | 建议: 1
|
||||
|
||||
静态分析 · 2 个问题
|
||||
|
||||
- 🔴 `sqlfluff:LT13` L1
|
||||
文件不能以换行或空白开头。
|
||||
建议: 删除文件开头的所有空行或空白字符,确保 SQL 语句从第一行开始。
|
||||
- 🔴 `sqlfluff:LT12` L4
|
||||
文件必须以单个尾随换行结束。
|
||||
建议: 删除文件末尾多余的空行,只保留一个换行符。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🟡 [AI] [performance] `unbounded-query` L2
|
||||
**查询无过滤和限制,可能返回全部数据**
|
||||
SELECT id FROM my_table 没有 WHERE 条件,也没有 LIMIT 子句。若表数据量大,会触发全表扫描并将全部 id 一次加载到应用内存,导致数据库和网络负载过高。
|
||||
建议: 根据业务需要添加 WHERE 过滤条件(如 WHERE status = 'active'),并用 LIMIT 限制返回行数(如 LIMIT 1000)或采用分页/流式查询。
|
||||
- 🔵 [AI] [design] `unqualified-table-reference` L2
|
||||
**表名未限定 schema**
|
||||
my_table 未使用 schema 前缀,在数据库搜索路径变化时可能解析到错误的表,降低可移植性与可维护性。
|
||||
建议: 建议使用 schema.table 的形式,例如 public.my_table,以明确表归属。
|
||||
@@ -0,0 +1,53 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\06_references.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 155.4s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 11 | 错误: 8 | 警告: 2 | 建议: 1
|
||||
|
||||
静态分析 · 8 个问题
|
||||
|
||||
- 🔴 `sqlfluff:RF01` L3
|
||||
第 3 行:引用 'missing_tbl.column_a' 指向在 FROM 子句(或上层语句)中不存在的表/视图。
|
||||
建议: 若该列实际属于 my_table,请将引用改为 my_table.column_a;若确实需要查询 missing_tbl,请将其加入 FROM 子句,例如 JOIN missing_tbl ON ...。
|
||||
- 🔴 `sqlfluff:RF02` L9
|
||||
第 9 行:SELECT 中存在未限定的引用 'column_a',但语句中引用了多张表/视图。
|
||||
建议: 为该列添加表名或别名前缀:若来自 my_table,请使用 a.column_a;若来自 other_table,请使用 b.column_a。
|
||||
- 🔴 `sqlfluff:AM05` L12
|
||||
第 12 行:JOIN 子句应使用完全限定名称。
|
||||
建议: 确保 JOIN 条件中所有列都带表别名/表名限定,并将 JOIN 表表达式与其目标表放在同一行;推荐写法:JOIN other_table AS b ON a.id = b.ref_id。
|
||||
- 🔴 `sqlfluff:LT02` L13
|
||||
第 13 行:'ON' 前应有换行,且 'ON' 前不应有缩进。
|
||||
建议: 将 JOIN 条件和表表达式分开:先把 other_table AS b 放在 JOIN 所在行,再在下一行写 ON a.id = b.ref_id,例如 JOIN other_table AS b
|
||||
ON a.id = b.ref_id;。
|
||||
- 🔴 `sqlfluff:RF04` L19
|
||||
第 19 行:关键字不应作为标识符使用(此处将 'sum' 用作别名)。
|
||||
建议: 将表别名从 sum 改为非关键字名称,例如 s 或 summary,并把 SELECT 中的 sum.a 改为 s.a。
|
||||
- 🔴 `sqlfluff:RF05` L23
|
||||
第 23 行:标识符中不应包含特殊字符。
|
||||
建议: 如果 'my-column' 是列名,请将其重命名为不含连字符的名称,如 my_column,然后使用 my_column;如果它只是字符串字面量,请改用单引号:'my-column'。
|
||||
- 🔴 `sqlfluff:RF06` L29
|
||||
第 29 行:标识符 "SIMPLE_COL" 被不必要地加了引号。
|
||||
建议: 直接去掉双引号:SELECT SIMPLE_COL FROM my_table;。
|
||||
- 🔴 `sqlfluff:LT12` L32
|
||||
第 32 行:文件必须以单个换行符结束。
|
||||
建议: 检查文件末尾,确保最后一行后只有一个换行符;删除多余的空白行或在缺少换行符的文件末尾补一个换行。
|
||||
|
||||
AI 审查 · 3 条建议
|
||||
|
||||
- 🟡 [AI] [design] `demo-run-error-aborts-script` L3
|
||||
**起始语句是无效引用,整个演示脚本会在第一条语句处终止**
|
||||
第 3 行引用 missing_tbl.column_a,但 FROM 子句中只有 my_table。若按文件顺序执行,多数 SQL 引擎会在第一条语句报错并停止,后续用于演示 RF02/RF04/RF05/RF06 的语句都无法执行。
|
||||
建议: 将故意写错的语句放到文件末尾,或为每个演示准备独立测试用例/事务,并捕获预期错误;确保合法语句先执行,非法语句最后执行。
|
||||
- 🟡 [AI] [bug] `quoted-identifier-case-sensitivity` L29
|
||||
**带引号的标识符 SIMPLE_COL 是大小写敏感的,可能匹配不到实际列**
|
||||
双引号标识符在多数数据库中按精确大小写匹配。如果 my_table 中实际列名是 simple_col 或 Simple_Col,SELECT "SIMPLE_COL" 会失败;RF06 只提示引号多余,未覆盖这一大小写风险。
|
||||
建议: 先确认数据字典中的真实列名。若列名确为 SIMPLE_COL,直接去掉引号;否则改成与实际列名一致的大小写并去掉引号。
|
||||
- 🔵 [AI] [design] `demo-comment-incomplete` L1
|
||||
**演示注释未列出实际触发的全部规则**
|
||||
第 1 行注释声明仅演示 RF01、RF02、RF04、RF05、RF06,但静态分析还发现 AM05、LT02、LT12;如果这是规则演示文件,注释不完整且会误导维护者。
|
||||
建议: 更新注释以列出所有实际触发的规则,或修复 AM05/LT02/LT12 等非预期问题,使文件只演示声明的规则。
|
||||
@@ -0,0 +1,111 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\07_structure.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 226.0s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 30 | 错误: 29 | 警告: 1 | 建议: 0
|
||||
|
||||
静态分析 · 28 个问题
|
||||
|
||||
- 🔴 `sqlfluff:LT05` L1
|
||||
L1: 行过长(97 > 80)。
|
||||
建议: 将该行注释拆分为多行或精简文字,使长度不超过 80 个字符。
|
||||
- 🔴 `sqlfluff:ST01` L3
|
||||
L3: 不要在 CASE WHEN 语句中指定 'ELSE NULL'(冗余)。
|
||||
建议: 删除 `ELSE NULL` 分支;当条件不满足时 CASE 会自然返回 NULL。
|
||||
- 🔴 `sqlfluff:ST02` L13
|
||||
L13: 不必要的 CASE 语句,建议改用 COALESCE 函数。
|
||||
建议: 改为 `COALESCE(amount > 0, FALSE) AS positive`,效果与当前 CASE 一致。
|
||||
- 🔴 `sqlfluff:ST03` L20
|
||||
L20: 查询定义了 CTE "unused_cte" 但未使用。
|
||||
建议: 删除未使用的 CTE `WITH unused_cte AS (...)`,直接从 `SELECT` 开始查询。
|
||||
- 🔴 `sqlfluff:LT08` L23
|
||||
L23: CTE 结束括号之后应有空行,但未找到。
|
||||
建议: 在 CTE 的闭合括号 `)` 与后续 `SELECT` 之间插入一个空行。
|
||||
- 🔴 `sqlfluff:ST04` L32
|
||||
L32: ELSE 子句中的嵌套 CASE 语句可以展平。
|
||||
建议: 合并为一个 CASE:`CASE WHEN a = 1 THEN b WHEN c = 1 THEN d END AS result`。
|
||||
- 🔴 `sqlfluff:RF04` L35
|
||||
L35: 不应将关键字用作标识符。
|
||||
建议: 将别名 `result` 改为非关键字名称,例如 `result_value`。
|
||||
- 🔴 `sqlfluff:AL08` L42
|
||||
L42: 列别名 'id' 与第 41 行的列名重复。
|
||||
建议: 为 `sub.id` 指定唯一别名,例如 `AS sub_id`,避免结果集列名冲突。
|
||||
- 🔴 `sqlfluff:AM05` L45
|
||||
L45: JOIN 子句应使用全限定名。
|
||||
建议: 明确使用 `INNER JOIN`,并确保 ON 条件中的列均带表别名限定。
|
||||
- 🔴 `sqlfluff:ST05` L46
|
||||
L46: select_statement 子句中不应包含子查询,应改用 CTE。
|
||||
建议: 将 JOIN 中的派生表提取为 CTE:`WITH sub AS (SELECT id FROM other_table) SELECT ... JOIN sub ON ...`。
|
||||
- 🔴 `sqlfluff:LT02` L46
|
||||
L46: 应在 'ON' 前换行,且 'ON' 前不应有缩进。
|
||||
建议: 将 ON 子句放到新行,放在与 JOIN 同一层级:`(SELECT ...) AS sub
|
||||
ON a.id = sub.id;`
|
||||
- 🔴 `sqlfluff:ST06` L49
|
||||
L49: SELECT 中应先写通配符和简单目标,再写计算与聚合。
|
||||
建议: 将 `*` 移到最前,再写普通列,最后写 `SUM(amount)`,并给聚合结果命名。注意当前查询还缺少 GROUP BY 或窗口函数。
|
||||
- 🔴 `sqlfluff:AL03` L50
|
||||
L50: 列表达式没有别名,请使用显式 `AS` 子句。
|
||||
建议: 为 `SUM(amount)` 添加别名:`SUM(amount) AS total_amount`。
|
||||
- 🔴 `sqlfluff:AM05` L62
|
||||
L62: JOIN 子句应使用全限定名。
|
||||
建议: 将 `USING (id)` 改写为带表别名限定的 ON 条件,并可加上 `INNER`。
|
||||
- 🔴 `sqlfluff:LT02` L63
|
||||
L63: 应在 'USING' 前换行,且不应缩进。
|
||||
建议: 将 `USING (id)` 移到新行(若改写为 ON 也保持换行)。
|
||||
- 🔴 `sqlfluff:ST07` L63
|
||||
L63: 发现 USING 语句,应只使用 ON 语句。
|
||||
建议: 将 `USING (id)` 改写为 `ON a.id = b.id`。
|
||||
- 🔴 `sqlfluff:LT10` L66
|
||||
L66: 'SELECT' 修饰符(如 'DISTINCT')必须与 'SELECT' 在同一行。
|
||||
建议: 将 `DISTINCT` 移到 `SELECT` 同一行:`SELECT DISTINCT id`。
|
||||
- 🔴 `sqlfluff:LT02` L67
|
||||
L67: 该行不应缩进。
|
||||
建议: 将 `DISTINCT` 移到 `SELECT` 同一行,消除缩进。
|
||||
- 🔴 `sqlfluff:LT01` L67
|
||||
L67: 'DISTINCT' 关键字与左括号 '(' 之间应有单个空格。
|
||||
建议: 不要使用函数式写法;最好是去掉括号写成 `DISTINCT id`。
|
||||
- 🔴 `sqlfluff:ST08` L67
|
||||
L67: 'DISTINCT' 不能与括号一起使用。
|
||||
建议: 去掉括号:`SELECT DISTINCT id`。
|
||||
- 🔴 `sqlfluff:AM05` L77
|
||||
L77: JOIN 子句应使用全限定名。
|
||||
建议: 明确写出 `INNER JOIN`,并保持 ON 条件使用表别名限定。
|
||||
- 🔴 `sqlfluff:LT02` L78
|
||||
L78: 应在 'ON' 前换行,且不应缩进。
|
||||
建议: 将 `ON` 子句放到新行:`other_table AS b
|
||||
ON b.id = a.id;`
|
||||
- 🔴 `sqlfluff:ST09` L78
|
||||
L78: JOIN 条件应把先出现的表放在前面。
|
||||
建议: 将 `ON b.id = a.id` 调整为 `ON a.id = b.id`。
|
||||
- 🔴 `sqlfluff:ST10` L86
|
||||
L86: 冗余常量表达式。
|
||||
建议: 删除 `WHERE` 子句,或改为 `WHERE TRUE`;如果动态生成条件,请不要保留恒真条件。
|
||||
- 🔴 `sqlfluff:ST11` L94
|
||||
L94: JOIN 表 'other_table AS b' 未在查询其他位置引用。
|
||||
建议: 若确实不需要该表,删除整个 LEFT JOIN;若只想判断存在性,请改用 EXISTS 子查询。
|
||||
- 🔴 `sqlfluff:LT02` L94
|
||||
L94: 应在 'ON' 前换行,且不应缩进。
|
||||
建议: 将 `ON` 子句放到新行:`other_table AS b
|
||||
ON a.id = b.id;`
|
||||
- 🔴 `sqlfluff:ST12` L100
|
||||
L100: 检测到连续分号(计数 2)。
|
||||
建议: 删除多余的分号,只保留一个 `;`。
|
||||
- 🔴 `sqlfluff:LT12` L101
|
||||
L101: 文件必须以单个尾随换行符结束。
|
||||
建议: 确保文件末尾恰好有一个换行符,不要多出空行或缺少换行。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🔴 [AI] [bug] `aggregate-without-group-by` L49
|
||||
**聚合函数与未分组列/通配符混用**
|
||||
第49行的 SELECT 将 `SUM(amount)` 与 `id`、`*` 放在同一个 SELECT 列表中,但没有 GROUP BY,也没有把 `SUM` 改为窗口函数。在大多数 SQL 引擎中这是非法查询;即使某些引擎允许,也会随机返回非聚合列的任意行,结果不可预测。
|
||||
建议: 如果希望保留每一行并附带总额,使用窗口函数 `SUM(amount) OVER () AS total_amount`;如果希望做聚合,则需要列出所有非聚合列并加 `GROUP BY`,或显式只保留聚合结果。
|
||||
- 🟡 [AI] [bug] `unused-left-join-cardinality` L94
|
||||
**未使用的 LEFT JOIN 可能使结果行数倍增**
|
||||
第93-94行的 LEFT JOIN 虽然未在 SELECT 或 WHERE 中引用 `other_table AS b`,但 SQL 仍会执行关联;若 `other_table.id` 有重复值,每条匹配都会复制左表行,导致结果集意外膨胀。静态规则只报告了表未被引用,但没有强调行数变化风险。
|
||||
建议: 完全移除该 LEFT JOIN;若需要检查是否存在匹配,使用 `WHERE EXISTS (SELECT 1 FROM other_table b WHERE b.id = a.id)`。
|
||||
@@ -0,0 +1,30 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\08_jinja.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 56.7s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 4 | 错误: 2 | 警告: 1 | 建议: 1
|
||||
|
||||
静态分析 · 2 个问题
|
||||
|
||||
- 🔴 `sqlfluff:JJ01` L2
|
||||
第2行:Jinja 标签应在两侧各有一个空格:{{1}}
|
||||
建议: 将 {{1}} 改为 {{ 1 }},即表达式与花括号之间各留一个空格。
|
||||
- 🔴 `sqlfluff:LT12` L6
|
||||
第6行:文件必须以单个尾随换行符结尾。
|
||||
建议: 在文件末尾(当前最后一行 my_table; 后)补一个换行符,并确保没有多余空行。
|
||||
|
||||
AI 审查 · 2 条建议
|
||||
|
||||
- 🟡 [AI] [performance] `unfiltered-select-all` L3
|
||||
**查询未加过滤条件,可能全表扫描并返回全部数据**
|
||||
第3~6行的 SELECT 仅选择 id 且没有 WHERE 或 LIMIT;若直接用于生产,会读取 my_table 全表数据,可能造成性能问题,同时将全部 id 暴露给调用方。
|
||||
建议: 如果只需要部分数据,增加明确的 WHERE 条件(如按时间或状态过滤);若用于分页/展示,应添加 LIMIT;若确实需要全量 id,请补充注释说明用途及执行窗口。
|
||||
- 🔵 [AI] [design] `unqualified-table-reference` L6
|
||||
**表名未限定 schema/database**
|
||||
第6行直接使用 my_table,未加 schema 或 database 限定;当数据库搜索路径变化时,可能解析到非预期表,导致查询错误。
|
||||
建议: 将表名改为明确的限定名,例如 my_schema.my_table(按实际环境替换 schema 名)。
|
||||
@@ -0,0 +1,31 @@
|
||||
# 代码审查报告
|
||||
|
||||
**文件:** `data\demo-sqlfluff\src\09_aliasing_length.sql`
|
||||
**语言:** sql
|
||||
**耗时:** 132.7s
|
||||
**分析工具:** sqlfluff
|
||||
|
||||
---
|
||||
|
||||
总计: 4 | 错误: 1 | 警告: 1 | 建议: 2
|
||||
|
||||
静态分析 · 1 个问题
|
||||
|
||||
- 🔴 `sqlfluff:AL06` L8
|
||||
L8: 别名长度不应超过30个字符。
|
||||
建议: 将别名 this_alias_is_way_longer_than_thirty_characters_ok 缩短为不超过30字符的语义化名称(如 long_alias),并确保第6行和第8行的引用同步更新。
|
||||
|
||||
AI 审查 · 3 条建议
|
||||
|
||||
- 🟡 [AI] [design] `repeated-alias-reference` L6
|
||||
**长别名在 SELECT 和 FROM 中重复出现,修改时易遗漏**
|
||||
同一个超长别名在 SELECT 子句(第6行)和 FROM 子句(第8行)中出现了两次。静态分析仅标记第8行的定义超长,但修复时若只修改定义处而遗漏引用处,SQL 会因别名不匹配而失败。
|
||||
建议: 将 this_alias_is_way_longer_than_thirty_characters_ok 统一替换为简短别名(如 long_alias),并同时更新两处引用。
|
||||
- 🔵 [AI] [performance] `missing-where-clause` L8
|
||||
**缺少 WHERE 子句可能导致全表扫描**
|
||||
查询未包含 WHERE 或 LIMIT,会读取并返回 my_table 的全部行。当表数据量大时,可能产生较高的 I/O 和网络开销。
|
||||
建议: 根据业务需求添加过滤条件(例如 WHERE id > ?),或添加 LIMIT 限制返回行数。
|
||||
- 🔵 [AI] [design] `missing-order-by` L8
|
||||
**缺少 ORDER BY 导致结果顺序不确定**
|
||||
查询未指定排序字段,数据库返回结果集的顺序可能因执行计划而变化,影响结果的可预测性。
|
||||
建议: 在查询末尾添加 ORDER BY this_alias_is_way_longer_than_thirty_characters_ok.id(或根据业务选择排序列)。
|
||||
@@ -0,0 +1,148 @@
|
||||
# demo-sqlfluff 插件实测覆盖率报告
|
||||
|
||||
> **工程**:`demo-sqlfluff`(11 个 SQL 样例文件,无 `.sqlfluff` 工程配置 → 插件回退内置配置)
|
||||
> **测试对象**:插件 SQLFluff 适配器(实测方言 ansi,内置规则集 `core` + 25 条精选 = 57 条,`max_alias_length = 30`)
|
||||
> **实测日期**:2026-08-25
|
||||
> **比对基线**:sqlfluff 4.3.0 + 插件内置配置逐字复刻(比对键 = 规则码 + 行号)
|
||||
|
||||
---
|
||||
|
||||
## 一、核心结论
|
||||
|
||||
| 指标 | 结果 | 状态 |
|
||||
|---|---|---|
|
||||
| 规则覆盖率(内置启用 57 条) | **57/57 种全部触发(100%)** | ✅ 全覆盖 |
|
||||
| 诊断总数 | **113 处**(与基线完全一致) | ✅ |
|
||||
| 逐条一致性(规则 + 行号) | **113/113 条完全一致(零偏差)** | ✅ |
|
||||
| 解析错误(PRS) | **0 处** | ✅ |
|
||||
| 插件缺陷 | **0 个**(JJ01 行号缺陷已修复并复测验证) | ✅ |
|
||||
|
||||
**实测完全达成全覆盖基准**:插件在「工程无配置文件 → 回退内置配置」路径下,内置启用的 57 条规则全部触发、113 处诊断与模拟基线逐条零偏差。此前发现的 **JJ01 行号显示 LNaN 缺陷已修复**——适配器对 sqlfluff JSON 中缺失 `end_line_no` 字段的回退已生效,复测 `08_jinja.sql` 确认 JJ01 正确显示为 `L2`。**SQLFluff 适配器静态分析链路验证通过,缺陷清零。**
|
||||
|
||||
---
|
||||
|
||||
## 二、逐文件比对(实测 vs 基线)
|
||||
|
||||
| 文件 | 实测 | 基线 | 一致性 | 覆盖规则 |
|
||||
|---|---:|---:|---|---|
|
||||
| `01_aliasing.sql` | 9 | 9 | ✅ 逐条一致 | AL02/AL04/AL05/AL08/AL09/AL10、AM05、LT02、LT12 |
|
||||
| `02_ambiguity.sql` | 10 | 10 | ✅ 逐条一致 | AM01/AM02/AM03/AM05/AM06/AM08、AL05、LT02、LT12 |
|
||||
| `03_caps.sql` | 25 | 25 | ✅ 逐条一致 | CP01-CP05、AL03、LT12 |
|
||||
| `04_convention.sql` | 13 | 13 | ✅ 逐条一致 | CV01-CV06/CV08/CV12、AM05、LT02、ST11、LT12 |
|
||||
| `05_layout.sql` | 12 | 12 | ✅ 逐条一致 | LT01/LT02/LT05-LT08/LT11/LT12、AM02 |
|
||||
| `05b_layout_misc.sql` | 3 | 3 | ✅ 逐条一致 | LT14、LT15、LT12 |
|
||||
| `05c_file_boundaries.sql` | 2 | 2 | ✅ 逐条一致 | LT13、LT12 |
|
||||
| `06_references.sql` | 8 | 8 | ✅ 逐条一致 | RF01/RF02/RF04/RF05/RF06、AM05、LT02、LT12 |
|
||||
| `07_structure.sql` | 28 | 28 | ✅ 逐条一致 | ST01-ST12、LT01/LT02/LT05/LT08/LT10、AL03/AL08、AM05、RF04、LT12 |
|
||||
| `08_jinja.sql` | 2 | 2 | ✅ 逐条一致 | JJ01(L2,复测确认修复生效)、LT12 |
|
||||
| `09_aliasing_length.sql` | 1 | 1 | ✅ 逐条一致 | AL06 |
|
||||
| **合计** | **113** | **113** | **✅ 零偏差(113/113)** | **57/57 种** |
|
||||
|
||||
> 比对方法:将插件报告中每条静态诊断(规则码 + 行号)与基线做多重集合比对;11 个文件全部 `missing = 0`、`extra = 0`(含 JJ01 行号缺陷修复后的复测确认)。
|
||||
|
||||
---
|
||||
|
||||
## 三、规则分组覆盖(57/57 = 100%)
|
||||
|
||||
| 分组 | 规则数 | 触发规则明细 |
|
||||
|---|---:|---|
|
||||
| AL(aliasing 别名) | 8 | AL02、AL03、AL04、AL05、AL06、AL08、AL09、AL10 |
|
||||
| AM(ambiguous 歧义) | 6 | AM01、AM02、AM03、AM05、AM06、AM08 |
|
||||
| CP(capitalisation 大小写) | 5 | CP01、CP02、CP03、CP04、CP05 |
|
||||
| CV(convention 约定) | 8 | CV01、CV02、CV03、CV04、CV05、CV06、CV08、CV12 |
|
||||
| JJ(jinja 模板) | 1 | JJ01 |
|
||||
| LT(layout 布局) | 12 | LT01、LT02、LT05、LT06、LT07、LT08、LT10、LT11、LT12、LT13、LT14、LT15 |
|
||||
| RF(references 引用) | 5 | RF01、RF02、RF04、RF05、RF06 |
|
||||
| ST(structure 结构) | 12 | ST01-ST12 全部 |
|
||||
|
||||
**严重级别**:113 处全部为 error(🔴)——内置启用的 57 条规则在插件分级表中均为 P0/P1 级(P0 × 32 + P1 × 25),与实测全 error 一致。
|
||||
|
||||
**诊断分布**:`07_structure.sql` 28 处最多,其次 `03_caps.sql` 25 处、`04_convention.sql` 13 处、`05_layout.sql` 12 处。
|
||||
|
||||
> 注:插件报告中另有 AI 审查建议 29 条(03_caps 4 / 05_layout 4 / 01_aliasing 3 / 02_ambiguity 3 / 06_references 3 / 09_aliasing_length 3 / 05b 2 / 05c 2 / 07_structure 2 / 04_convention 2 / 08_jinja 1),属 AI 增强能力,不计入静态规则覆盖口径。
|
||||
|
||||
---
|
||||
|
||||
## 四、全覆盖的达成要点
|
||||
|
||||
内置规则集(`core` 组 31 条 + 精选追加 25 条,合并去重 57 条)中有两条规则需要特别条件,本轮均已触发:
|
||||
|
||||
1. **AM02(ambiguous.union,裸 UNION 应写明 DISTINCT/ALL)**:方言门控规则,仅对 ansi/bigquery/mysql/snowflake 等支持 `UNION DISTINCT` 语法的方言生效,oracle 不在白名单。实测机按指引将插件设置 `sqlfluff.dialect` 设为 **ansi**,AM02 在 `02_ambiguity.sql:20` 与 `05_layout.sql:41` 正常触发(若用默认 oracle 方言,该规则不报,覆盖率为 56/57)。
|
||||
2. **AL06(aliasing.length,别名长度上限)**:内置配置设 `max_alias_length = 30`,由补充样例 `09_aliasing_length.sql` 的 51 字符别名触发(实测报在 L8 别名定义处)。
|
||||
|
||||
**实测环境判定**:实测报告路径 `data\demo-sqlfluff\src\` 为单层目录,11 个文件与当前工作区版本一致(`02_ambiguity.sql` 顶部含 5 行方言说明注释、`09_aliasing_length.sql` 存在);方言判定为 ansi 的依据:AM02 触发 + 全部 113 处行号与 ansi 基线逐条吻合(oracle 基线为 111 处且无 AM02)。
|
||||
|
||||
---
|
||||
|
||||
## 五、插件缺陷记录:JJ01 行号 LNaN(已修复验证 ✅)
|
||||
|
||||
**发现过程**:首轮实测中 `08_jinja.sql` 报告显示 `` `sqlfluff:JJ01` LNaN ``——规则触发正确,但行号显示为 NaN(真实位置 L2)。
|
||||
|
||||
**根因链**(已逐环节实证):
|
||||
|
||||
1. **sqlfluff JSON 输出**:JJ01 违规项(jinja 模板类违规)只有 `start_line_no: 2` / `start_line_pos: 1`,**顶层缺失 `end_line_no` / `end_line_pos` 字段**;其余 112 处违规均有完整 end 字段(已对 11 个文件全量验证:缺失 end 字段的违规仅 JJ01 这 1 条)。
|
||||
2. **插件适配器**(`sqlfluff.ts`)直接计算:`new vscode.Range(v.start_line_no - 1, v.start_line_pos - 1, v.end_line_no - 1, v.end_line_pos - 1)` → end 传入 `undefined - 1 = NaN`。
|
||||
3. **vscode.Range 构造函数**:`start.isBefore(end)` 对 NaN 比较恒为 false → **交换 start/end** → `range.start` 变成 `Position(NaN, NaN)`。
|
||||
4. **报告生成器**(`report.ts`)读取 `diag.range.start.line` → `L${NaN + 1}` → 显示 "LNaN"。
|
||||
|
||||
**修复方案**(适配器侧对缺失 end 字段回退到 start 坐标):
|
||||
|
||||
```typescript
|
||||
const endLine = (v.end_line_no ?? v.start_line_no) - 1;
|
||||
const endPos = (v.end_line_pos ?? v.start_line_pos) - 1;
|
||||
```
|
||||
|
||||
**修复验证**(2026-08-25 复测 `08_jinja.sql`):
|
||||
|
||||
| 项 | 修复前 | 修复后 |
|
||||
|---|---|---|
|
||||
| `sqlfluff:JJ01` 行号 | `LNaN` | **`L2`** ✅ |
|
||||
| 建议文案 | 正常(`Jinja 标签两侧应各有一个空格:{{1}}`) | 正常(新增行号前缀「第2行:」) |
|
||||
| `sqlfluff:LT12` | L6 ✅ | L6 ✅ |
|
||||
| 文件诊断合计 | 2 处 | 2 处 ✅ 与基线逐条一致 |
|
||||
|
||||
修复后 `08_jinja.sql` 复测 2/2 条与基线(JJ01 L2 + LT12 L6,sqlfluff 4.3.0 复现)逐条一致,demo-sqlfluff 整体升至 **113/113 条零偏差**。
|
||||
|
||||
---
|
||||
|
||||
## 六、工程数据勘误(coverage_map.json)
|
||||
|
||||
实测过程中发现工程自带的 `coverage_map.json` 存在 10 处陈旧行号(工程蓝图数据问题,非插件问题,已修正):
|
||||
|
||||
| 类型 | 涉及条目 | 原因 |
|
||||
|---|---|---|
|
||||
| `02_ambiguity.sql` 行号整体 +4 | AM01(2→6)、AM02(16→20)、AM03(28→32)、AM05(38→42, 59→63)、AM06(50→54)、AM08(59→63)、AL05(60→64)、LT02(39→43)、LT12(61→65) | 文件顶部补充 5 行方言说明注释后未同步更新映射 |
|
||||
| AL06 行号 | 6→8 | sqlfluff 实际报在 FROM 别名定义处(L8),蓝图误记为 SELECT 引用处(L6) |
|
||||
|
||||
---
|
||||
|
||||
## 七、数据口径说明
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 覆盖率口径 | 57 条 = 插件内置配置启用规则数(`core` 组 31 + 精选追加 25,合并去重);规则触发即计入,与 P0/P1 分级(32+25=57)完全对应 |
|
||||
| 方言口径 | 实测为 **ansi**(按上轮指引设置);默认 oracle 下 AM02 不触发(56/57 = 98.2%),属 sqlflutt 方言能力差异而非插件缺陷 |
|
||||
| 比对引擎 | sqlfluff 4.3.0(4.2.2 实测行为一致:同为 113 处、行号相同、JJ01 同样缺失 end 字段),内置配置逐字复刻插件 `buildBuiltinSqlfluffConfig` |
|
||||
| 基线数据 | `/data/user/work/results/sqlfluff-final-compare.json`(逐文件比对明细);`/data/user/work/results/sqlfluff-actual-parsed.json`(实测解析结果) |
|
||||
| 已知差异 | 无(JJ01 行号缺陷已修复并复测验证:LNaN → L2;113/113 条规则码 + 行号零偏差) |
|
||||
|
||||
---
|
||||
|
||||
## 八、结论与后续
|
||||
|
||||
**demo-sqlfluff 工程的插件实测验证完成**:
|
||||
|
||||
1. ✅ 内置规则全覆盖达成(57/57 种 · 113 处 · 零解析错误)
|
||||
2. ✅ 插件行为零偏差(113/113 条逐条一致,含 JJ01 缺陷修复后的复测确认)
|
||||
3. ✅ 插件缺陷闭环:JJ01 行号 LNaN → 修复 → 复测验证 L2(发现、定位、修复、验证全流程完成)
|
||||
4. ✅ 「内置配置回退」路径验证通过(工程无 `.sqlfluff` 配置文件场景)
|
||||
|
||||
**后续建议**:
|
||||
|
||||
- ~~插件侧修复 JJ01~~(✅ 已完成并验证)
|
||||
- **其他工程**:`demo-pmd` 按同样方法实测(先本地基线复现 → 实测逐条比对),注意 Java 版本与 ruleset
|
||||
- **可选**:若需验证「工程配置优先」路径,可在工程根添加 `.sqlfluff` 配置文件复测(预期插件改用工程配置)
|
||||
|
||||
---
|
||||
|
||||
*报告生成于 2026-08-25 · demo-sqlfluff 插件实测 · 比对脚本:`sqlfluff-final-compare.mjs` · 数据:`sqlfluff-final-compare.json`*
|
||||
@@ -0,0 +1,47 @@
|
||||
-- Demo: aliasing rules (AL02, AL03, AL04, AL05, AL08, AL09, AL10)
|
||||
-- AL06 样例见 09_aliasing_length.sql:内置配置 max_alias_length = 30,
|
||||
-- 别名超长即可触发(min_alias_length 未设置,短别名不检查)。
|
||||
SELECT
|
||||
col1 col1_alias
|
||||
FROM
|
||||
my_table;
|
||||
-- AL02: column alias should use explicit AS
|
||||
|
||||
SELECT
|
||||
SUM(amount)
|
||||
FROM
|
||||
my_table;
|
||||
-- AL03: column expression without alias
|
||||
|
||||
SELECT
|
||||
t1.id
|
||||
FROM
|
||||
my_table AS t1
|
||||
JOIN
|
||||
other_table AS t1 ON t1.id = other_table.ref_id;
|
||||
-- AL04: duplicate table alias
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table AS unused_alias;
|
||||
-- AL05: table alias never used
|
||||
|
||||
SELECT
|
||||
id AS shared_alias,
|
||||
name AS shared_alias
|
||||
FROM
|
||||
my_table;
|
||||
-- AL08: duplicate column alias
|
||||
|
||||
SELECT
|
||||
id AS id
|
||||
FROM
|
||||
my_table;
|
||||
-- AL09: column alias identical to column name
|
||||
|
||||
SELECT
|
||||
cnt
|
||||
FROM
|
||||
(SELECT COUNT(*) AS cnt FROM my_table);
|
||||
-- AL10: derived table must have an alias
|
||||
@@ -0,0 +1,65 @@
|
||||
-- Demo: ambiguity rules (AM01, AM02, AM03, AM05, AM06, AM08)
|
||||
-- 注:AM02(ambiguous.union)为方言门控规则——规则仅对 ansi/bigquery/mysql
|
||||
-- 等支持 UNION DISTINCT 的方言生效;插件默认方言 oracle 不在白名单内,
|
||||
-- 默认设置下本样例不会报 AM02。手动测试时将 sqlfluff.dialect 设为 ansi
|
||||
-- 即可触发(经实测 ansi 方言下启用规则 57/57 全覆盖)。
|
||||
SELECT DISTINCT
|
||||
dept,
|
||||
region
|
||||
FROM
|
||||
my_table
|
||||
GROUP BY
|
||||
dept,
|
||||
region;
|
||||
-- AM01: DISTINCT combined with GROUP BY
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table
|
||||
UNION
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
other_table;
|
||||
-- AM02: bare UNION instead of UNION ALL/DISTINCT
|
||||
|
||||
SELECT
|
||||
a,
|
||||
b
|
||||
FROM
|
||||
my_table
|
||||
ORDER BY
|
||||
a,
|
||||
b DESC;
|
||||
-- AM03: mixed explicit/implicit ordering directions
|
||||
|
||||
SELECT
|
||||
a.id,
|
||||
b.name
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
other_table AS b ON a.id = b.id;
|
||||
-- AM05: JOIN clause not fully qualified (needs INNER)
|
||||
|
||||
SELECT
|
||||
a,
|
||||
b
|
||||
FROM
|
||||
my_table
|
||||
GROUP BY
|
||||
a,
|
||||
b
|
||||
ORDER BY
|
||||
1,
|
||||
2;
|
||||
-- AM06: inconsistent column references (GROUP BY names vs ORDER BY positions)
|
||||
|
||||
SELECT
|
||||
a.id
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
other_table AS b;
|
||||
-- AM08: implicit cross join (JOIN without ON condition)
|
||||
@@ -0,0 +1,40 @@
|
||||
-- Demo: capitalisation rules (CP01, CP02, CP03, CP04, CP05)
|
||||
select
|
||||
id,
|
||||
status_code
|
||||
from
|
||||
my_table
|
||||
where
|
||||
status_code = 'active';
|
||||
-- CP01: lowercase keywords
|
||||
|
||||
SELECT
|
||||
colA,
|
||||
other_col
|
||||
FROM
|
||||
my_table
|
||||
WHERE
|
||||
coLa = 1;
|
||||
-- CP02: inconsistent capitalisation of identifiers
|
||||
|
||||
SELECT
|
||||
SUM(amount),
|
||||
sum(quantity)
|
||||
FROM
|
||||
my_table;
|
||||
-- CP03: inconsistent capitalisation of function names
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table
|
||||
WHERE
|
||||
flag IS NULL OR other_flag IS null;
|
||||
-- CP04: inconsistent capitalisation of boolean/null literals
|
||||
|
||||
SELECT
|
||||
CAST(id AS VARCHAR),
|
||||
CAST(amount AS varchar)
|
||||
FROM
|
||||
my_table;
|
||||
-- CP05: inconsistent capitalisation of datatypes
|
||||
@@ -0,0 +1,63 @@
|
||||
-- Demo: convention rules (CV01, CV02, CV03, CV04, CV05, CV08, CV12, CV06)
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table
|
||||
WHERE
|
||||
status_code != 'active' OR status_code <> 'pending';
|
||||
-- CV01: inconsistent "not equal to" operator
|
||||
|
||||
SELECT
|
||||
IFNULL(amount, 0) AS amount_or_zero
|
||||
FROM
|
||||
my_table;
|
||||
-- CV02: use COALESCE instead of IFNULL
|
||||
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
FROM
|
||||
my_table;
|
||||
-- CV03: trailing comma in select clause
|
||||
|
||||
SELECT
|
||||
COUNT(1) AS total_rows
|
||||
FROM
|
||||
my_table;
|
||||
-- CV04: use COUNT(*) instead of COUNT(1)
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table
|
||||
WHERE
|
||||
end_date = NULL;
|
||||
-- CV05: comparisons with NULL should use IS
|
||||
|
||||
SELECT
|
||||
a.id
|
||||
FROM
|
||||
my_table AS a
|
||||
RIGHT JOIN
|
||||
other_table AS b ON a.id = b.ref_id;
|
||||
-- CV08: use LEFT JOIN instead of RIGHT JOIN
|
||||
|
||||
SELECT
|
||||
a.id,
|
||||
b.name
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
other_table AS b
|
||||
WHERE
|
||||
a.id = b.ref_id;
|
||||
-- CV12: use JOIN ... ON ... instead of WHERE for join conditions
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table
|
||||
WHERE
|
||||
status_code = 'active'
|
||||
;
|
||||
-- CV06: statement terminator on its own line (should follow the statement)
|
||||
@@ -0,0 +1,42 @@
|
||||
-- Demo: layout rules (LT01, LT02, LT05, LT06, LT07, LT08, LT10, LT11)
|
||||
SELECT * FROM my_table;
|
||||
-- LT01: extra spacing after SELECT
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table;
|
||||
-- LT02: inappropriate indentation
|
||||
|
||||
SELECT
|
||||
id,
|
||||
a_very_long_column_name_that_pushes_the_total_line_length_beyond_the_allowed_maximum_eighty
|
||||
FROM
|
||||
my_table;
|
||||
-- LT05: line too long
|
||||
|
||||
SELECT
|
||||
SUM (amount) AS total_amount
|
||||
FROM
|
||||
my_table;
|
||||
-- LT06: function name not immediately followed by parenthesis
|
||||
|
||||
WITH cte AS (
|
||||
SELECT id FROM my_table)
|
||||
SELECT * FROM cte;
|
||||
-- LT07: CTE closing bracket should be on a new line
|
||||
|
||||
WITH cte AS (
|
||||
SELECT id FROM my_table
|
||||
)
|
||||
SELECT * FROM cte;
|
||||
-- LT08: blank line expected after CTE closing bracket
|
||||
|
||||
SELECT DISTINCT
|
||||
id
|
||||
FROM
|
||||
my_table;
|
||||
-- LT10: SELECT modifier must be on same line
|
||||
|
||||
SELECT id FROM my_table UNION SELECT id FROM other_table;
|
||||
-- LT11: set operators should be surrounded by newlines
|
||||
@@ -0,0 +1,10 @@
|
||||
-- Demo: layout rules (LT14, LT15)
|
||||
SELECT id FROM my_table GROUP BY id;
|
||||
-- LT14: GROUP keyword should start a new line
|
||||
|
||||
SELECT id FROM my_table;
|
||||
|
||||
|
||||
|
||||
SELECT id FROM my_table;
|
||||
-- LT15: too many consecutive blank lines
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
SELECT id FROM my_table;
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
-- Demo: references rules (RF01, RF02, RF04, RF05, RF06)
|
||||
SELECT
|
||||
missing_tbl.column_a
|
||||
FROM
|
||||
my_table;
|
||||
-- RF01: reference to object not present in FROM
|
||||
|
||||
SELECT
|
||||
column_a
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
other_table AS b ON a.id = b.ref_id;
|
||||
-- RF02: unqualified column with multiple referenced tables
|
||||
|
||||
SELECT
|
||||
sum.a
|
||||
FROM
|
||||
my_table AS sum;
|
||||
-- RF04: keyword used as identifier (alias)
|
||||
|
||||
SELECT
|
||||
"my-column"
|
||||
FROM
|
||||
my_table;
|
||||
-- RF05: special characters in identifier
|
||||
|
||||
SELECT
|
||||
"SIMPLE_COL"
|
||||
FROM
|
||||
my_table;
|
||||
-- RF06: unnecessary quoted identifier
|
||||
@@ -0,0 +1,101 @@
|
||||
-- Demo: structure rules (ST01, ST02, ST03, ST04, ST05, ST06, ST07, ST08, ST09, ST10, ST11, ST12)
|
||||
SELECT
|
||||
CASE
|
||||
WHEN status_code = 'active' THEN 1
|
||||
ELSE NULL
|
||||
END AS is_active
|
||||
FROM
|
||||
my_table;
|
||||
-- ST01: do not specify 'else null'
|
||||
|
||||
SELECT
|
||||
CASE
|
||||
WHEN amount > 0 THEN TRUE
|
||||
ELSE FALSE
|
||||
END AS positive
|
||||
FROM
|
||||
my_table;
|
||||
-- ST02: unnecessary CASE statement
|
||||
|
||||
WITH unused_cte AS (
|
||||
SELECT id FROM my_table
|
||||
)
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table;
|
||||
-- ST03: unused CTE
|
||||
|
||||
SELECT
|
||||
CASE
|
||||
WHEN a = 1 THEN b
|
||||
ELSE CASE
|
||||
WHEN c = 1 THEN d
|
||||
END
|
||||
END AS result
|
||||
FROM
|
||||
my_table;
|
||||
-- ST04: nested CASE in ELSE clause
|
||||
|
||||
SELECT
|
||||
a.id,
|
||||
sub.id
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
(SELECT id FROM other_table) AS sub ON a.id = sub.id;
|
||||
-- ST05: subquery in JOIN clause
|
||||
|
||||
SELECT
|
||||
SUM(amount),
|
||||
id,
|
||||
*
|
||||
FROM
|
||||
my_table;
|
||||
-- ST06: column order (aggregate before wildcard/simple)
|
||||
|
||||
SELECT
|
||||
a.id,
|
||||
b.name
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
other_table AS b USING (id);
|
||||
-- ST07: prefer join keys instead of USING
|
||||
|
||||
SELECT
|
||||
DISTINCT(id)
|
||||
FROM
|
||||
my_table;
|
||||
-- ST08: DISTINCT used with parentheses
|
||||
|
||||
SELECT
|
||||
a.id,
|
||||
b.name
|
||||
FROM
|
||||
my_table AS a
|
||||
JOIN
|
||||
other_table AS b ON b.id = a.id;
|
||||
-- ST09: join condition order (later table listed first)
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table
|
||||
WHERE
|
||||
2 = 2;
|
||||
-- ST10: redundant constant expression
|
||||
|
||||
SELECT
|
||||
a.id
|
||||
FROM
|
||||
my_table AS a
|
||||
LEFT JOIN
|
||||
other_table AS b ON a.id = b.id;
|
||||
-- ST11: unused join
|
||||
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table;;
|
||||
-- ST12: consecutive semicolons
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Demo: jinja padding rule (JJ01)
|
||||
-- {{1}}
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
my_table;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Demo: aliasing.length (AL06)
|
||||
-- 补充样例:内置配置设置了 max_alias_length = 30,别名超过 30 字符即触发
|
||||
-- AL06(此前 demo 误认为该规则无法触发;min_alias_length 未设置时短别名
|
||||
-- 不报,但超长别名仍会被 max_alias_length 拦截)。
|
||||
SELECT
|
||||
this_alias_is_way_longer_than_thirty_characters_ok.id
|
||||
FROM
|
||||
my_table this_alias_is_way_longer_than_thirty_characters_ok;
|
||||
-- AL06: alias exceeds max_alias_length (30)
|
||||
Reference in New Issue
Block a user