COUNT(布尔字段)返回总行数,因COUNT只统计非NULL值而不区分TRUE/FALSE;正确做法是用SUM(CASE WHEN...THEN 1 ELSE 0 END)跨库兼容地分别计数真假值。

SQL中布尔字段的COUNT()为什么总是返回总行数?
因为大多数数据库(PostgreSQL、SQLite)把布尔值当作普通标量处理,COUNT(col) 会忽略 NULL 但不区分 TRUE/FALSE —— 只要非空就计1。想分别统计真假值,得用条件聚合。
常见错误写法:COUNT(is_active) 或 COUNT(*) WHERE is_active = TRUE(语法非法)。
- PostgreSQL:直接支持
is_active = TRUE,但必须套在SUM()或COUNT()的FILTER子句里 - MySQL:布尔实际是
TINYINT(1),可用SUM(is_active)算真值个数(因 TRUE=1, FALSE=0) - SQL Server:用
CASE WHEN is_active = 1 THEN 1 ELSE 0 END配合SUM()
用SUM()或CASE实现真假值分别计数
跨数据库兼容性最好的方式是用 SUM(CASE WHEN ... THEN 1 ELSE 0 END),它把布尔逻辑转为数值累加。
SELECT SUM(CASE WHEN is_published THEN 1 ELSE 0 END) AS published_count, SUM(CASE WHEN NOT is_published THEN 1 ELSE 0 END) AS unpublished_count, COUNT(*) AS total_count FROM articles;
注意:PostgreSQL 支持更简洁的 COUNT(*) FILTER (WHERE is_published),但 MySQL 和 SQL Server 不认这个语法。
- 别用
COUNT(CASE WHEN ...):当条件不满足时CASE返回NULL,而COUNT(NULL)不计数,结果会偏小 - 如果字段允许
NULL,且你想排除NULL行再算占比,需在WHERE里显式过滤,或在分母用COUNT(is_published)
计算布尔字段的百分比(避免除零和精度丢失)
直接写 published_count * 100.0 / total_count 看似简单,但有两处硬伤:除零报错、整数除法截断(尤其在 PostgreSQL/SQL Server 中)。
安全写法是强制转浮点并处理分母为0:
SELECT
ROUND(
100.0 * SUM(CASE WHEN is_featured THEN 1 ELSE 0 END) /
NULLIF(COUNT(*), 0),
2
) AS featured_pct
FROM products;-
NULLIF(COUNT(*), 0)把分母为0转成NULL,整个表达式结果也为NULL,不报错 -
100.0中的.0强制浮点运算,避免整除(如 MySQL 中5/10得0) - 如果业务要求“无数据时显示 0 而非 NULL”,可外层套
COALESCE(..., 0)
GROUP BY 布尔字段时的排序与空值陷阱
对布尔字段 GROUP BY is_deleted 是合法的,但结果顺序不可靠:PostgreSQL 可能按 FALSE、TRUE、NULL 排,MySQL 可能按内部整数值排。
若需固定顺序(比如先显示启用项),显式用 ORDER BY is_enabled DESC(因通常 TRUE > FALSE),但要注意 NULL 默认排最前或最后,取决于数据库和 NULLS FIRST/LAST 设置。
- PostgreSQL 支持
ORDER BY is_active DESC NULLS LAST - MySQL 8.0+ 支持
NULLS LAST,旧版需用ORDER BY is_active IS NULL, is_active DESC - 如果分组后还要算每组占比,分母必须是该分组内的总数,不是全表总数 —— 别漏掉
PARTITION BY或子查询隔离上下文
布尔字段的统计本身不难,难点在于不同数据库对布尔类型的底层实现差异太大,同一句 SQL 在 PostgreSQL 里跑得欢,在 MySQL 里可能语法报错或语义偏移。动手前先确认目标库的布尔类型映射规则,比死记函数名更重要。

















