web-dev-qa-db-ja.com

postgresを使用してstring_aggのようにarray_aggのnull値を除外する方法は?

_array_agg_を使用して名前を収集する場合、コンマで区切られた名前を取得しますが、null値がある場合、そのnullも集計の名前として使用されます。例えば ​​:

_SELECT g.id,
       array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users,
       array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users
FROM groups g
GROUP BY g.id;
_

_,Larry,Phil_の代わりに_Larry,Phil_を返します(私の9.1.2では、_NULL,Larry,Phil_と表示されます)。 this fiddleのように

代わりに、string_agg()を使用すると、 here のような名前のみ(空のコンマまたはヌルなし)が表示されます。

問題は、サーバーに_Postgres 8.4_がインストールされており、string_agg()がそこで動作しないことです。 array_aggをstring_agg()と同様に動作させる方法はありますか?

70
Daud

SQL Fiddle

select
    id,
    (select array_agg(a) from unnest(canonical_users) a where a is not null) canonical_users,
    (select array_agg(a) from unnest(non_canonical_users) a where a is not null) non_canonical_users
from (
    SELECT g.id,
           array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END) canonical_users,
           array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END) non_canonical_users
    FROM groups g
    GROUP BY g.id
) s

または、nullを排除するarray_to_stringを使用して、より単純で安価になる場合があります。

SELECT
    g.id,
    array_to_string(
        array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END)
        , ','
    ) canonical_users,
    array_to_string(
        array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END)
        , ','
    ) non_canonical_users
FROM groups g
GROUP BY g.id

SQL Fiddle

21
Clodoaldo Neto

Postgresql-9.3では、これを行うことができます。

SELECT g.id,
   array_remove(array_agg(CASE WHEN g.canonical = 'Y' THEN g.users ELSE NULL END), NULL) canonical_users,
   array_remove(array_agg(CASE WHEN g.canonical = 'N' THEN g.users ELSE NULL END), NULL) non_canonical_users
FROM groups g 
GROUP BY g.id;

更新:postgresql-9.4で;

SELECT g.id,
   array_agg(g.users) FILTER (WHERE g.canonical = 'Y') canonical_users,
   array_agg(g.users) FILTER (WHERE g.canonical = 'N') non_canonical_users
FROM groups g 
GROUP BY g.id;
194
Dale O'Brien

配列集合体からNULLを削除するという一般的な問題を解決する際、問題を攻撃する主な方法は2つあります。array_agg(unnest(array_agg(x))を実行するか、カスタム集合体を作成するかです。

最初の形式は 上記

SELECT 
    array_agg(u) 
FROM (
    SELECT 
        unnest(
            array_agg(v)
        ) as u 
    FROM 
        x
    ) un
WHERE 
    u IS NOT NULL;

二番目:

/*
With reference to
http://ejrh.wordpress.com/2011/09/27/denormalisation-aggregate-function-for-postgresql/
*/
CREATE OR REPLACE FUNCTION fn_array_agg_notnull (
    a anyarray
    , b anyelement
) RETURNS ANYARRAY
AS $$
BEGIN

    IF b IS NOT NULL THEN
        a := array_append(a, b);
    END IF;

    RETURN a;

END;
$$ IMMUTABLE LANGUAGE 'plpgsql';

CREATE AGGREGATE array_agg_notnull(ANYELEMENT) (
    SFUNC = fn_array_agg_notnull,
    STYPE = ANYARRAY,
    INITCOND = '{}'
);

2番目の呼び出しは(当然)最初の呼び出しよりも少し見た目が良いです:

xからarray_agg_notnull(v)を選択します。

11
rorycl

このスレッドはかなり古いものですが、これを追加していますが、小さな配列で非常にうまく機能するこの巧妙なトリックに出くわしました。追加のライブラリや関数なしでPostgres 8.4以降で実行されます。

_string_to_array(array_to_string(array_agg(my_column)))::int[]
_

array_to_string()メソッドは実際にnullを取り除きます。

7
ced-b

コメントで提案されているように、配列内のヌルを置換する関数を作成できますが、コメントでリンクされているスレッドでも指摘されているように、この種の集計関数を作成する必要がある場合、集計関数の効率を損ないます、分割してから再度集約します。

配列にnullを保持することは、Array_Aggの単なる(おそらく望ましくない)機能だと思います。これを回避するには、サブクエリを使用できます。

SELECT  COALESCE(y.ID, n.ID) ID,
        y.Users,
        n.Users
FROM    (   SELECT  g.ID, ARRAY_AGG(g.Users) AS Users
            FROM    Groups g
            WHERE   g.Canonical = 'Y'
            GROUP BY g.ID
        ) y
        FULL JOIN 
        (   SELECT  g.ID, ARRAY_AGG(g.Users) AS Users
            FROM    Groups g
            WHERE   g.Canonical = 'N'
            GROUP BY g.ID
        ) n
            ON n.ID = y.ID

SQL FIDDLE

3
GarethD