我想聚合列的总和,同时跟踪表示错误情况的 NULL 值的存在。例如,取表号:
# select * from numbers;
n | l
------+-----
1 | foo
2 | foo
NULL | bar
4 | bar
给定一个标签
l
,我想计算带有该标签的数字 n
的总和,前提是没有 NULL
值。理想情况下,对于没有任何行的标签,总和将为 0。所以我正在寻找一些查询q
,这样
q('foo') = 3
、q('baz') = 0
和 q('bar')
以某种方式发出错误信号,例如通过返回 NULL
。
我从
sum()
聚合函数开始,但这会将 NULL
行转换为 0。一种解决方案是返回 NULL
的变体,前提是存在任何 NULL
值。
sum()
给出
# select sum(n) from numbers where l = 'bar';
sum
-----
4
但我宁愿
sumnull()
与
# select sumnull(n) from numbers where l = 'bar';
sumnull
---------
NULL
到目前为止我发现的最好的解决方案是也计算非 NULL 行并与总计数进行比较:
# select sum(n), count(*), count(n) as notnull from numbers;
sum | count | notnull
-----+-------+---------
7 | 4 | 3
那么如果
count
不等于notnull
,我知道结果无效。
空集就够了吗?
create table numbers (n int);
insert into numbers values (1),(2),(null),(4);
select sum(n)
from numbers
having bool_and(n is not null);
sum
-----
(0 rows)
如果你确实需要一个空值,那就有点复杂了:
with sum_null as (
select sum(n) as sum_n
from numbers
having bool_and(n is not null)
)
select case
when not exists (select 1 from sum_null) then null
else (select sum_n from sum_null) end
;
sum_n
-------
(1 row)
将
having
行替换为:
having not bool_or(n is null)
可读性较差,但可能更快,因为它可以在第一个找到
null
时停止搜索。
https://www.postgresql.org/docs/current/static/functions-aggregate.html#FUNCTIONS-AGGREGATE-TABLE
您可以创建自定义聚合,例如:
create or replace function int_sum_null(int, int)
returns int language sql as $$
select $1 + $2
$$;
create aggregate sumnull(integer) (
sfunc = int_sum_null,
stype = int
);
select sum(n), sumnull(n)
from numbers;
sum | sumnull
-----+---------
7 | <null>
(1 row)
更新#1
没有自定义聚合的解决方案:
select case
when bool_or(n is null) then null
else sum(n) end
from numbers;
select coalesce((
select sum(n)
from numbers
having not bool_or(n is null)), null);
这些变体基于 Clodoaldo Neto 的想法。如果您喜欢他们,请也为他的答案点赞。
更新#2
修改自定义聚合
sumnull
并添加初始条件:
drop aggregate sumnull(integer);
create aggregate sumnull(integer) (
sfunc = int_sum_null,
stype = int,
initcond = 0
);
要获取您在更新的问题中描述的结果:
create table numbers (n int, l text);
insert into numbers values
(1, 'foo'), (2, 'foo'), (null, 'bar'), (4, 'bar');
select
sumnull(n) filter (where l = 'foo') foo,
sumnull(n) filter (where l = 'bar') bar,
sumnull(n) filter (where l = 'baz') baz
from numbers;
foo | bar | baz
-----+-----+-----
3 | | 0
(1 row)
1)使用合并
SELECT SUM(COALESCE(n,'NaN'::numeric)) FROM numbers;
如果任何行为 NULL,则结果将为 'NaN'
2)如果任何行具有 NULL 值,您将得到 NULL 作为结果,否则结果为数字
SELECT
CASE WHEN (SELECT COUNT(*) FROM numbers WHERE n IS NULL) > 0 THEN NULL
ELSE (SELECT SUM(COALESCE(n, 0)) FROM numbers)
END
对于这些情况,您可以使用
NaN
将 coalesce
替换为 NULL
;使其返回
NaN
create table t(
val int
);
insert into t (val) values (1), (2), (NULL);
select sum(coalesce(val, double precision 'NaN')) from t;
结果:
NaN
。
insert into t (val) values (1), (2), (3);
select sum(coalesce(val, double precision 'NaN')) from t;
结果:
6
。
看到这个SQLFiddle
我有以下内容...以防万一它对某人有用:
SELECT SUM(n) * CASE WHEN count(n) = count(*) THEN 1 ELSE NULL END FROM numbers
技巧是通过比较计数来检测是否有
NULL
行,然后强制使用 1
或 NULL
进行数学运算,当存在 NULL
行时,会产生 NULL
。