エラー状態を示すNULL値の存在を追跡しながら、列の合計を集計したいと思います。例:テーブル番号を取得します。
_# select * from numbers;
n | l
------+-----
1 | foo
2 | foo
NULL | bar
4 | bar
_
ラベルl
が与えられた場合、n
の値がない場合、そのラベルでNULL
の数値の合計を計算したいと思います。理想的には、行のないラベルの場合、合計は0になります。したがって、q('foo') = 3
、q('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
_
次に、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)
本当に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
カスタムアグリゲートを作成できます。例:
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)
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
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