非NULL字段填充率指某列在指定分组下非NULL值占该组总行数的比例,用COUNT(col)/COUNT()计算,需乘1.0并用NULLIF防除零,且分母必须统一为COUNT()。

什么是“非NULL字段填充率”?
填充率不是SQL内置概念,而是业务侧常指:某列在指定分组(如分类)下,非NULL值占该组总行数的比例。它反映数据完整性,比如user_type在category = 'vip'下有100行,其中92行非NULL,填充率为0.92(或92%)。
用COUNT()和COUNT(column)直接算最可靠
COUNT(*)统计所有行(含NULL),COUNT(col)只统计col非NULL的行——两者相除就是填充率。这是唯一不依赖COALESCE或CASE、且能正确处理NULL分组键的写法。
示例(统计每个category下email字段的填充率):
SELECT category, COUNT(email) * 1.0 / NULLIF(COUNT(*), 0) AS email_fill_rate FROM users GROUP BY category;
-
NULLIF(COUNT(*), 0)防止除零错误,结果为NULL而非报错 - 乘
1.0强制转为浮点,避免整数除法截断(如9/10 = 0) - 若
category本身可能为NULL,需加WHERE category IS NOT NULL,否则GROUP BY会把NULL归为一组——这组是否计入业务填充率,得看需求
别用SUM(CASE WHEN ...)代替COUNT(col)
有人写SUM(CASE WHEN email IS NOT NULL THEN 1 ELSE 0 END)来算非NULL行数,逻辑没错,但没必要:
- 更长、更易写错(漏
ELSE 0会导致NULL参与SUM,结果变NULL) -
COUNT(email)是语义明确、优化器友好、且对空表/全NULL列天然鲁棒的写法 - 如果字段是字符串,误用
COUNT(NULLIF(email, ''))会混淆“空字符串”和NULL——填充率通常只关心NULL,不关心空白值
多个字段要分别统计?用多个COUNT()并列即可
不需要子查询或UNPIVOT。一个GROUP BY里可以同时算name、phone、address各自的填充率:
SELECT category, COUNT(name) * 1.0 / NULLIF(COUNT(*), 0) AS name_fill_rate, COUNT(phone) * 1.0 / NULLIF(COUNT(*), 0) AS phone_fill_rate, COUNT(address) * 1.0 / NULLIF(COUNT(*), 0) AS address_fill_rate FROM users GROUP BY category;
注意:所有COUNT()都基于同一行集,所以分母必须统一用COUNT(*),不能各自用COUNT(col)作分母——那算的是“该字段非NULL时,其他字段的条件填充率”,不是你要的。
真正容易被忽略的是:当某category组内所有email都是NULL时,COUNT(email)为0,结果是0.0,不是NULL——这符合填充率定义;但如果你的BI工具把0.0渲染成空格或隐藏,可能会误判为“无数据”。得确认下游怎么处理零值。










