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":"hello@world.com"},{"id":4,"user_id":1,"email":"again@awesome.com"}]
2 | Nick | [null]
当我使用LEFT JOIN时,会出现没有右表匹配的情况,因此将空(null)值替换为右表列。因此,我将[null]
作为JSON聚合之一。
如何忽略/删除null
,这样当右表列为空时我有一个空的JSON数组[]
?
干杯!
答案 0 :(得分:47)
在9.4中,您可以使用coalesce和聚合过滤器表达式。
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,就可以像往常一样使用coalesce。
http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SYNTAX-AGGREGATES
答案 1 :(得分:12)
这样的事情,可能是吗?
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 强>
答案 2 :(得分:3)
如果这实际上是一个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 :(得分:3)
我使用this answer(抱歉,我似乎无法链接到您的用户名),但我相信我对其进行了一些改进。
对于数组版本,我们可以
array_to_json(array_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,对于对象版本,我们可以:
WITH
子句得到这个:
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
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 <> '""';
$$;
答案 4 :(得分:1)
可能性能不如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
答案 5 :(得分:1)
我创建了自己的函数来过滤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;
$$;
编辑:您可以在我的要点上看到一些工具(其中一些不是我的,但它们是从其他stackoverflow解决方案中获取的):
https://gist.github.com/le-doude/8b0e89d71a32efd21283
答案 6 :(得分:0)
这种方式有效,但必须有更好的方法:(
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;
答案 7 :(得分:0)
有些不同,但可能对其他人有帮助
如果数组中的所有对象都具有相同的结构(例如,因为使用jsonb_build_object
来创建它们),则可以定义要在array_remove
中使用的“具有相同结构的NULL对象”:>
...
array_remove(
array_agg(jsonb_build_object('att1', column1, 'att2', column2)),
to_jsonb('{"att1":null, "att2":null}'::json)
)
...