it-swarm.com.de

Postgresql LEFT JOIN json_agg () ignorieren/entfernen 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 erstellt beispielsweise eine Ausgabe

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

Da ich LEFT JOIN verwende, kann es Fälle geben, in denen keine Übereinstimmung der rechten Tabelle vorliegt. Daher werden leere (Null) Werte für die Spalten der rechten Tabelle verwendet. Als Ergebnis bekomme ich [null] als eines der JSON-Aggregate.

Wie kann ich null ignorieren/entfernen, sodass ich ein leeres JSON-Array [] habe, wenn die rechte Tabellenspalte null ist?

Prost!

31
user3081211

In 9.4 können Sie coalesce und einen Aggregatfilterausdruck verwenden.

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;

Der Filterausdruck verhindert, dass das Aggregat die Zeilen verarbeitet, die Null sind, da die linke Join-Bedingung nicht erfüllt ist. Daher wird am Ende eine Datenbanknull statt json [null] angezeigt. Wenn Sie eine Datenbank-Null haben, können Sie wie gewohnt Coalesce verwenden.

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

41
Mike Stankavich

so etwas kann sein?

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

du kannst auch vor dem Beitritt gruppieren (ich bevorzuge diese Version, es ist etwas klarer):

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

12
Roman Pekar

Wenn dies tatsächlich ein PostgreSQL-Fehler ist, hoffe ich, dass er in 9.4 behoben wurde. Sehr nervig.

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;

Ich persönlich mache das COALESCE-Bit nicht, gebe einfach NULL zurück. Ihr Anruf.

3
Jeff

Ich habe diese Antwort verwendet (sorry, ich kann mich anscheinend nicht mit Ihrem Benutzernamen verbinden), aber ich glaube, ich habe sie etwas verbessert.

Für die Array-Version können wir

  1. beseitigen Sie die redundante Doppelauswahl
  2. verwenden Sie json_agg anstelle der Aufrufe von array_to_json(array_agg())

und hol dir das:

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 <> '""';
$$;

Für 9.3 für die Objektversion können wir:

  1. entfernen Sie die nicht verwendete WITH-Klausel
  2. beseitigen Sie die redundante Doppelauswahl

und hol dir das:

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 <> '""';
$$;

Für 9.4 müssen wir nicht den String Assembly-Teil zum Erstellen des Objekts verwenden, da wir das neu hinzugefügte json_object_agg verwenden können.

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

Ich habe meine eigene Funktion zum Filtern von Json-Arrays erstellt:

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;
$$;

Ich benutze es als

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;

funktioniert natürlich nur in postgresql 9.3. Ich habe auch ein ähnliches für Objektfelder:

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;
$$;

BEARBEITEN: Einige Utensilien (einige sind nicht meine, aber sie wurden von anderen Stackoverflow-Lösungen übernommen) finden Sie hier bei Gist: https://Gist.github.com/le-doude/8b0e89d71a32efd21283

Wahrscheinlich weniger performant als die Lösung von Roman Pekar, aber etwas aufgeräumter:

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

Dieser Weg funktioniert, aber es muss einen besseren Weg geben :(

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;

demo: http://sqlfiddle.com/#!15/ddefb/16

0
Fabricator

Ein bisschen anders, könnte aber für andere hilfreich sein:

Wenn alle Objekte im Array dieselbe Struktur haben (z. B. weil Sie jsonb_build_object zum Erstellen verwenden), können Sie ein "NULL-Objekt mit derselben Struktur" definieren, das in array_remove verwendet werden soll:

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