web-dev-qa-db-ja.com

Postgresql LEFT JOIN json_agg()NULLを無視/削除

SELECT C.id, C.name, json_agg(E) AS emails FROM contacts C
LEFT JOIN emails E ON C.id = E.user_id
GROUP BY C.id;

Postgres 9.3は例えば出力を作成します

  id  |  name  |  emails
-----------------------------------------------------------
   1  |  Ryan  |  [{"id":3,"user_id":1,"email":"[email protected]"},{"id":4,"user_id":1,"email":"[email protected]"}]
   2  |  Nick  |  [null]

LEFT JOINを使用しているため、右テーブルの一致がない場合があります。そのため、右テーブルの列は空の(null)値に置き換えられます。その結果、[null] JSON集約の1つとして。

nullを無視/削除して、空のJSON配列[]右テーブルの列がnullの場合

乾杯!

39
user3081211

9.4では、合体および集約フィルター式を使用できます。

SELECT C.id, C.name, 
  COALESCE(json_agg(E) FILTER (WHERE E.user_id IS NOT NULL), '[]') AS emails 
FROM contacts C
LEFT JOIN emails E ON C.id = E.user_id
GROUP BY C.id, C.name
ORDER BY C.id;

フィルター式は、左結合条件が満たされていないため、集計がNULLの行を処理できないようにします。そのため、json [null]の代わりにデータベースnullになります。データベースがヌルになったら、通常どおり合体を使用できます。

http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SYNTAX-AGGREGATES

56
Mike Stankavich

このようなものでしょうか?

select
    c.id, c.name,
    case when count(e) = 0 then '[]' else json_agg(e) end as emails
from contacts as c
    left outer join emails as e on c.id = e.user_id
group by c.id

sql fiddle demo

参加する前にグループ化することもできます(このバージョンの方がいいと思います。もう少しわかりやすいです):

select
    c.id, c.name,
    coalesce(e.emails, '[]') as emails
from contacts as c
    left outer join (
        select e.user_id, json_agg(e) as emails from emails as e group by e.user_id
    ) as e on e.user_id = c.id

sql fiddle demo

13
Roman Pekar

これが実際にPostgreSQLのバグである場合、9.4で修正されることを望みます。とてもうるさい。

SELECT C.id, C.name, 
  COALESCE(NULLIF(json_agg(E)::TEXT, '[null]'), '[]')::JSON AS emails 
FROM contacts C
LEFT JOIN emails E ON C.id = E.user_id
GROUP BY C.id;

私は個人的にCOALESCEビットを実行せず、単にNULLを返します。あなたの電話。

3
Jeff

この回答 (申し訳ありませんが、ユーザー名にリンクできないようです)を使用しましたが、少し改善したと思います。

配列バージョンの場合、次のことができます

  1. 冗長な二重選択を取り除く
  2. array_to_json(array_agg())呼び出しの代わりに json_agg を使用します

これを取得します:

CREATE OR REPLACE FUNCTION public.json_clean_array(p_data JSON)
  RETURNS JSON
LANGUAGE SQL IMMUTABLE
AS $$
-- removes elements that are json null (not sql-null) or empty
SELECT json_agg(value)
  FROM json_array_elements(p_data)
 WHERE value::text <> 'null' AND value::text <> '""';
$$;

9.3のオブジェクトバージョンでは、次のことができます。

  1. 使用されていないWITH句を取り除きます
  2. 冗長な二重選択を取り除く

これを取得します:

CREATE OR REPLACE FUNCTION public.json_clean(p_data JSON)
  RETURNS JSON
  LANGUAGE SQL IMMUTABLE
AS $$
-- removes elements that are json null (not sql-null) or empty
  SELECT ('{' || string_agg(to_json(key) || ':' || value, ',') || '}') :: JSON
    FROM json_each(p_data)
   WHERE value::TEXT <> 'null' AND value::TEXT <> '""';
$$;

9.4では、新しく追加された json_object_agg を使用できるため、オブジェクトを構築するために文字列Assemblyを使用する必要はありません。

CREATE OR REPLACE FUNCTION public.json_clean(p_data JSON)
  RETURNS JSON
  LANGUAGE SQL IMMUTABLE
AS $$
-- removes elements that are json null (not sql-null) or empty
  SELECT json_object_agg(key, value)
    FROM json_each(p_data)
   WHERE value::TEXT <> 'null' AND value::TEXT <> '""';
$$;
3
Developer.ca

JSON配列をフィルタリングするための独自の関数を作成しました。

CREATE OR REPLACE FUNCTION public.json_clean_array(data JSON)
  RETURNS JSON
LANGUAGE SQL
AS $$
SELECT
  array_to_json(array_agg(value)) :: JSON
FROM (
       SELECT
         value
       FROM json_array_elements(data)
       WHERE cast(value AS TEXT) != 'null' AND cast(value AS TEXT) != ''
     ) t;
$$;

私はそれを

select 
    friend_id as friend, 
    json_clean_array(array_to_json(array_agg(comment))) as comments 
from some_entity_that_might_have_comments 
group by friend_id;

もちろん、postgresql 9.3でのみ機能します。オブジェクトフィールドにも同様のものがあります:

CREATE OR REPLACE FUNCTION public.json_clean(data JSON)
  RETURNS JSON
LANGUAGE SQL
AS $$
SELECT
  ('{' || string_agg(to_json(key) || ':' || value, ',') || '}') :: JSON
FROM (
       WITH to_clean AS (
           SELECT
             *
           FROM json_each(data)
       )
       SELECT
         *
       FROM json_each(data)
       WHERE cast(value AS TEXT) != 'null' AND cast(value AS TEXT) != ''
     ) t;
$$;

編集:ここで私のGistでいくつかのユーティリティを見ることができます(元々は私のものではありませんが、他のstackoverflowソリューションから取ったものです) https://Gist.github.com/le-doude/8b0e89d71a32efd2128 =

おそらく、Roman Pekarのソリューションよりもパフォーマンスは劣りますが、少しすっきりしています。

select
c.id, c.name,
array_to_json(array(select email from emails e where e.user_id=c.id))
from contacts c
1
maniek

この方法は機能しますが、より良い方法があります:(

SELECT C.id, C.name, 
  case when exists (select true from emails where user_id=C.id) then json_agg(E) else '[]' end
FROM contacts C
LEFT JOIN emails E ON C.id = E.user_id
GROUP BY C.id, C.name;

デモ: http://sqlfiddle.com/#!15/ddefb/16

0
Fabricator

少し異なりますが、他の人には役立つかもしれません:

配列内のすべてのオブジェクトが同じ構造である場合(たとえば、jsonb_build_objectを使用して作成するため)、array_removeで使用する「同じ構造のNULLオブジェクト」を定義できます。

...
array_remove(
    array_agg(jsonb_build_object('att1', column1, 'att2', column2)), 
    to_jsonb('{"att1":null, "att2":null}'::json)
)
...
0
tom