如何检测 PostgreSQL sum() 中的 NULL 行

问题描述 投票:0回答:5

我想聚合列的总和,同时跟踪表示错误情况的 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
,我知道结果无效。

postgresql null sum aggregate-functions
5个回答
14
投票

空集就够了吗?

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


8
投票

您可以创建自定义聚合,例如:

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)

3
投票

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

2
投票

对于这些情况,您可以使用

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


0
投票

我有以下内容...以防万一它对某人有用:

SELECT SUM(n) * CASE WHEN count(n) = count(*) THEN 1 ELSE NULL END FROM numbers

技巧是通过比较计数来检测是否有

NULL
行,然后强制使用
1
NULL
进行数学运算,当存在
NULL
行时,会产生
NULL

© www.soinside.com 2019 - 2024. All rights reserved.