用途
该技能针对一种常见图表缺陷:以可空外键或可解析字段为维度绘制的“按类别份额”分布图,本身是部分的——没有分组 id、分组行未缓存、存储的年份无法解析的事件不会出现在任何桶中。柱状图之间仍能自洽相加,因此问题不易被察觉。该技能的立场是:部分分布必须自报家门,用一行文字披露“有多少输入实际被分类”。
主要能力
- 要求分桶查询与覆盖率查询共享逐字相同的谓词,并建议将两条查询相邻书写,便于用肉眼 diff WHERE 子句。
- 指出覆盖率必须由构建桶的同一组谓词计算,而不是简化版本:资料示例中存在三类排除条件(空 id、连接不到行、值无法解析),只复现其一的覆盖率查询会高估图表实际覆盖。
- 要求校验不变量:桶计数之和等于覆盖率分子。
- 处理整数除法问题:
(classified * 100) / total会截断,使 4/1000 显示为“0%”,而柱状图可见,屏幕自相矛盾;也会把 99.6% 压成 99%。建议四舍五入,或在格式分辨率以下打印“<1%”。 - 明确分母含义:“68% 的事件可被定年”只有在分母是窗口内全部事件时才诚实;悄悄换成“有分组的事件”会得到舒适但答非所问的数字。
- 指出把解析写成范围测试的副作用:SQLite 中
CAST(SUBSTR(x, 1, 4) AS INTEGER)对非数字文本返回 0,因此> 1900同时充当解析检查和合理性检查;需记录该哨兵值实际排除了什么。 - 指出整数除法的桶键会把解析失败值映射到“0s”桶并排在最前,只有 WHERE 中的范围过滤在阻止它;放松或重排过滤会让解析失败变成一个类别。
- 指出分子分母分两次扫描可能不一致:分类计数来自独立查询,总数来自更早、事务外获取的样本列表长度,其间写入可使覆盖率超过 100%;建议同一事务内取数,或钳制并承认该钳制是关键。
- 禁止把部分分布归一化为 100%:应按最大桶缩放并在每根柱旁打印原始计数;按已分类总数缩放会产生“占子集 100%”却看似“占全部 100%”的百分比。
- 指出图表也可能因富化而缩短:读时把 id 解析为展示行会静默丢弃已消失的条目,“top 100”悄悄变成 top 60。
- 指出桶集还可能在时间上部分:最新桶只覆盖一个月中的三天,却画出与完整桶等宽的柱,需要披露不完整或干脆不画该桶。
使用方式
资料提供的是检查与验证流程,而非安装步骤。核心做法是:在图表旁加一行覆盖率披露文字,并确保其在最需要时仍可到达。资料特别指出“陷阱”:披露通常被放在最需要它时却被跳过的代码块里——if (buckets.isEmpty()) return 恰好在什么都无法分类时触发,于是 0% 覆盖率时柱子、区块标题和“N% 的事件可被定年”一行全部一起消失;空情况需要独立分支,在不画图的情况下渲染这句话。同时要留意区块外层重复的守卫,两份副本意味着两处要修。
验证方法
资料给出从仓库根目录运行、均为只读的检查命令:
grep -rn --include='*.kt' "SUBSTR(" . | grep -v '/build/':把两条谓词并排对照,第一个命中之后的每个 AND 都必须原样出现在覆盖率查询中。grep -rn --include='*.kt' -E "decades.isEmpty\(\)|datedPlays" . | grep -v '/build/':检查披露文字能否被到达;任何位于覆盖率文字之上的“无桶即返回”都是渲染为空的 0% 覆盖率场景。预期看到两个 isEmpty() 守卫(区块外层与图表本身),覆盖率表达式在图表守卫下方两行;grep 按文件排序,所以源码中守卫先执行,0% 时其下那行永远不会执行。LC_ALL=C awk 'BEGIN{printf "996/1000 -> %d%%\n 4/1000 -> %d%%\n", (996*100)/1000, (4*100)/1000}':演示截断,打印 99% 和 0%。
适用场景
适用于使用 Kotlin 与 SQL 构建分析图表、仪表盘或统计报表的开发者,尤其是涉及可空外键、连接、日期或年份解析、分桶聚合与百分比展示的场景。也可作为代码评审清单,检查已有“按类别份额”图表是否存在未披露的部分覆盖。
限制
资料为具体缺陷的分析与检查说明,未提供安装命令、依赖、API、版本要求或性能数据,这些暂未提供。示例代码是取自某个仓库的片段,语境为 SQLite 与 Kotlin,其他数据库的 CAST 与整数除法行为可能不同,资料未展开说明。该技能面向图表诚实性审查,并不提供绘图库或端到端实现。