SUM(SIGN(x)) 返回正负个数之差而非各自数量,无法拆分;需用 CASE WHEN SIGN(x)=1 THEN 1 ELSE 0 END 等分别求和统计。

为什么不能直接用 SUM(SIGN(x)) 统计正负个数?
SIGN(x) 返回 -1(负)、0(零)、1(正),所以 SUM(SIGN(x)) 得到的是「正数个数减去负数个数」,不是各自独立的计数。比如有 3 个正数、2 个负数,结果是 1,完全无法拆分出 3 和 2。
用 CASE WHEN SIGN(x) = 1 THEN 1 ELSE 0 END 分别求和
真正要统计个数,得把 SIGN 的输出转为布尔计数:对每种符号单独构造 0/1 列,再 SUM。
-
SUM(CASE WHEN SIGN(x) = 1 THEN 1 ELSE 0 END)→ 正数个数 -
SUM(CASE WHEN SIGN(x) = -1 THEN 1 ELSE 0 END)→ 负数个数 -
SUM(CASE WHEN SIGN(x) = 0 THEN 1 ELSE 0 END)→ 零值个数(可选)
示例:
SELECT SUM(CASE WHEN SIGN(amount) = 1 THEN 1 ELSE 0 END) AS positive_cnt, SUM(CASE WHEN SIGN(amount) = -1 THEN 1 ELSE 0 END) AS negative_cnt FROM transactions;
SIGN 在不同数据库中的空值和 NULL 行为
SIGN(NULL) 在所有主流 SQL 引擎(PostgreSQL、MySQL、SQL Server、SQLite)中都返回 NULL,而 CASE WHEN SIGN(x) = 1 对 NULL 不匹配,自动走 ELSE 0 分支——这点很关键,意味着你不需要额外处理 NULL,它天然被排除在计数外。
- 如果想把
NULL单独计数,得显式加WHEN x IS NULL THEN 1 - MySQL 中
SIGN(NAN)返回0,但标准数值列一般不会出现;PostgreSQL 对NAN报错,需提前过滤 - 整数和浮点数都支持
SIGN,但某些旧版 SQLite 可能不支持浮点输入,建议先CAST(x AS REAL)
替代方案:用 COUNT + FILTER(PostgreSQL)或 WHERE 子查询
如果只查单个符号,COUNT 更直观;但一次性查多个符号时,上面的 CASE + SUM 仍是通用性最强、性能最稳的方式。
- PostgreSQL 可用
COUNT(*) FILTER (WHERE amount > 0),语义清晰,但 MySQL / SQL Server 不支持 - 避免写成
(SELECT COUNT(*) FROM t WHERE x > 0)多次子查询——全表扫描重复执行,性能差 - 注意:用
amount > 0比SIGN(amount) = 1少一次函数调用,少量提升,且更易用索引(如果字段有索引)
实际中,只要字段上有索引,直接写 amount > 0 或 amount 通常比套一层 <code>SIGN 更稳妥——函数会阻止索引使用,这点容易被忽略。

















