选择json_agg内的列


21

我有一个查询,如:

SELECT a.id, a.name, json_agg(b.*) as "item"
  FROM a
  JOIN b ON b.item_id = a.id
 GROUP BY a.id, a.name;

如何选择JSON对象中b没有的列b.item_id

我已经阅读了有关ROW,但它返回的JSON对象如下:

{"f1": "Foo", "f2": "Bar"}

一旦提取JSON对象以匹配正确的列键,我将需要重新映射。我想避免这种情况,并保留原始列名称。

Answers:


50

不幸的是,SQL语法中没有规定说“除这一列外的所有列”。您可以通过列出行类型表达式中的其余列列表来实现目标:

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

这是更明确的形式的缩写:。 ROW(b.col1, b.col2, b.col3)

但是,列名称未保留在行类型表达式中。您可以通过这种方式在JSON对象中获得通用键名称。我看到3个保留原始列名的选项:

1.转换为注册类型

转换为众所周知的(注册)行类型。为每个现有表或视图或使用显式CREATE TYPE语句注册类型。您可以将临时表用于临时解决方案(在会话期间有效):

CREATE TEMP TABLE x (col1 int, col2 text, col3 date);  -- use adequate data types!

SELECT a.id, a.name
     , json_agg((b.col1, b.col2, b.col3)::x) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

2.使用子选择

使用subselect构造派生表整体引用该表。这也带有列名。它比较冗长,但是您不需要注册类型:

SELECT a.id, a.name
     , json_agg((SELECT x FROM (SELECT b.col1, b.col2, b.col3) AS x)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

3. json_build_object()在Postgres 9.4或更高版本中

SELECT a.id, a.name
     , json_agg(json_build_object('col1', b.col1, 'col2', b.col2, 'col3', b.col3)) AS item
FROM   a
JOIN   b ON b.item_id = a.id
GROUP  BY a.id, a.name;

有关:

jsonb各自的功能jsonb_agg()和相似jsonb_build_object()

对于Postgres的9.5或更高版本还看到a_horse的回答用新的更短的语法变种:Postgres的加减运算-jsonb“这只是一个键的所有键”
由于Postgres 10的 “除几个键外”是使用与text[]第二个操作数相同的运算符实现的-如mlt注释。


1
>或几个键请注意,json(b)-text []从10开始可用
。– mlt

解决方案3对我来说就像是魅​​力!
路易斯·费尔南多·达席尔瓦

17

从9.6开始,您可以简单地-从JSONB中删除密钥:

SELECT a.id, a.name, jsonb_agg(to_jsonb(b) - 'item_id') as "item"
FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

to_jsonb(b)将转换整个行,- 'item_id'然后删除名称item_id为键的键,然后将其汇总。


这项新功能似乎是OP所希望的。我在答案中添加了链接。
Erwin Brandstetter

当我尝试subselect变体时,出现了与该json_agg功能有关的错误:function json_agg(record) does not exist
fraxture

@fraxture:那么您就没有使用Postgres 9.6
a_horse_with_no_name,

确实,这就是问题所在。v9.2中有什么方法可以过滤列?
fraxture

8

您实际上可以使用子查询而无需分组依据

SELECT 
  a.id, a.name, 
  ( 
    SELECT json_agg(item)
    FROM (
      SELECT b.c1 AS x, b.c2 AS y 
      FROM b WHERE b.item_id = a.id
    ) item
  ) AS items
FROM a;

退货

{
  id: 1,
  name: "thing one",
  items:[
    { x: "child1", y: "child1 col2"},
    { x: "child2", y: "child2 col2"}
  ]
}

John Atten的这篇文章非常有趣,并且有更多详细信息


2

我发现最好创建JSON,然后对其进行汇总。例如

with base as (
select a, b, ('{"ecks":"' || to_json(x) || '","wai":"' || to_json(y) || '","zee":"' || to_json(z) || '"}"')::json c
) select (a, b, array_to_json(array_agg(c)) as c)

请注意,如果您不喜欢CTE(或者由于使用CTE而导致性能问题),则可以将其作为子查询来完成。

还要注意,如果您打算做很多事情,那么创建一个函数来包装键-值对可能是有益的,这样代码看起来会更干净。您将传递函数(例如)'ecks', 'x',然后返回"ecks": "x"


1

虽然除了选择一位之外,仍然没有办法对所有列进行任何选择,但是您可以使用json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))json数组来获取jsons数组,格式为{"col_1":"col_1 value", ...}

因此查询看起来像:

SELECT a.id, a.name, json_agg(to_json(b.col_1,b.col_2,b.col_3...)) as item
  FROM a
  JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;

并返回以下行:

id, name, item
8, the_name, [{"col_1":"value_1","col_2":"value_2","col_3":"value_3"...}, {"col_1":"value_1.2","col_2":"value_2.2","col_3":"value_3.2"...},...]
9, the_next_name, [{"col_1":"value_1.3","col_2":"value_2.3","col_3":"value_3.3"...},   {"col_1":"value_1.4","col_2":"value_2.4","col_3":"value_3.4"...},...]
...

(我现在使用的是Postgres 9.5.3,不确定何时添加此支持。)


1

您可以使用json_build_object这样的

SELECT 
  a.id, 
  a.name,
  json_agg(json_build_object('col1', b.col1, 'col2', b.col2) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.