web-dev-qa-db-ja.com

PostgreSQLでNULL行を検出する方法sum()

エラー状態を示すNULL値の存在を追跡しながら、列の合計を集計したいと思います。例:テーブル番号を取得します。

_# select * from numbers;
  n   |  l  
------+-----
    1 | foo
    2 | foo
 NULL | bar
    4 | bar
_

ラベルlが与えられた場合、nの値がない場合、そのラベルでNULLの数値の合計を計算したいと思います。理想的には、行のないラベルの場合、合計は0になります。したがって、q('foo') = 3q('baz') = 0、およびq('bar')どういうわけかエラーを通知します、例えばNULLを返すことによって。

sum()aggregate function から始めましたが、それはNULL行を0に変換します。1つの解決策は、そこに提供された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
_

次に、countnotnullと等しくない場合、結果が無効であることがわかります。

10
robx

空のセットで十分ですか?

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)

本当にnull値が必要な場合は、もう少し複雑です。

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

10
Clodoaldo Neto

カスタムアグリゲートを作成できます。例:

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)
7
klin

NaNを使用してcoalesceNULLに置き換えることにより、これらの場合に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

2
Felk

1)使用 [〜#〜] coalesce [〜#〜]

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
1
Maxim