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 crée une sortie par exemple
id | name | emails
-----------------------------------------------------------
1 | Ryan | [{"id":3,"user_id":1,"email":"[email protected]"},{"id":4,"user_id":1,"email":"[email protected]"}]
2 | Nick | [null]
Comme j'utilise un LEFT JOIN, il y aura des cas où il n'y a pas de correspondance dans le tableau de droite, c'est pourquoi des valeurs vides (null) sont substituées aux colonnes du tableau de droite. En conséquence, je reçois [null]
en tant que l’un des agrégats JSON.
Comment puis-je ignorer/supprimer null
afin d'avoir un tableau JSON vide []
lorsque la colonne de la table de droite est null?
À votre santé!
En 9.4, vous pouvez utiliser coalesce et une expression de filtre agrégée.
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;
L'expression de filtre empêche l'assemblage de traiter les lignes nulles car la condition de jointure gauche n'est pas remplie. Vous obtenez donc une base de données null à la place de json [null]. Une fois que vous avez une base de données null, vous pouvez utiliser coalesce comme d’habitude.
http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SYNTAX-AGGREGATES
alors 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
vous êtes ici
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
S'il s'agit en fait d'un bogue PostgreSQL, j'espère qu'il a été corrigé dans la version 9.4. Très ennuyant.
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;
Personnellement, je ne fais pas le bit COALESCE, il suffit de renvoyer la valeur NULL. Ton appel.
J'ai utilisé cette réponse (désolé, je n'arrive pas à créer un lien vers votre nom d'utilisateur), mais je pense l'avoir un peu améliorée.
Pour la version tableau nous pouvons
array_to_json(array_agg())
et obtenez ceci:
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 <> '""';
$$;
Pour 9.3, pour la version objet, nous pouvons:
WITH
non utiliséeet obtenez ceci:
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 <> '""';
$$;
Pour la version 9.4, il n'est pas nécessaire d'utiliser la chaîne Assembly pour construire l'objet, car nous pouvons utiliser le json_object_agg récemment ajouté.
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 <> '""';
$$;
J'ai créé ma propre fonction pour filtrer les tableaux 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;
$$;
Je l'utilise comme
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;
bien sûr, ne fonctionne que dans postgresql 9.3. J'ai aussi un similaire pour les champs d'objet:
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;
$$;
EDIT: Vous pouvez voir quelques utils (quelques-uns ne sont pas les miens à l’origine mais ils ont été empruntés à d’autres solutions de stackoverflow) ici sur mon Gist: https://Gist.github.com/le-doude/8b0e89d71a32efd21283
Probablement moins performant que la solution de Roman Pekar, mais un peu plus net:
select
c.id, c.name,
array_to_json(array(select email from emails e where e.user_id=c.id))
from contacts c
Cela fonctionne, mais il doit y avoir un meilleur moyen :(
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;
Un peu différent mais pourrait être utile pour les autres:
Si tous les objets du tableau ont la même structure (par exemple parce que vous utilisez jsonb_build_object
pour les créer), vous pouvez définir un "objet NULL avec la même structure" à utiliser dans array_remove
:
...
array_remove(
array_agg(jsonb_build_object('att1', column1, 'att2', column2)),
to_jsonb('{"att1":null, "att2":null}'::json)
)
...