选择 json_agg 中的列

Yan*_*hon 41 postgresql group-by row json

我有一个查询,如:

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;
Run Code Online (Sandbox Code Playgroud)

如何选择在列b,所以我没有b.item_id在JSON对象?

我读过关于ROW,但它返回一个 JSON 对象,如:

{"f1": "Foo", "f2": "Bar"}
Run Code Online (Sandbox Code Playgroud)

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

Erw*_*ter 75

不幸的是,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;
Run Code Online (Sandbox Code Playgroud)

这是更明确形式的缩写:.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;
Run Code Online (Sandbox Code Playgroud)

2. 使用子选择

使用子选择来构造派生表作为一个整体引用该表。这也带有列名。它更冗长,但您不需要注册类型:

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;
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

有关的:

jsonb各自的功能类似jsonb_agg()jsonb_build_object()

对于Postgres 9.5或更高版本,还可以看到a_horse 的答案,其中包含一个新的更短的语法变体:Postgres 添加了减号运算符-jsonb用于表示“除此一个键之外的所有键”
由于Postgres 10 “除了几个键”是用相同的运算符实现的,text[]作为第二个操作数 - 如 mlt 评论。


a_h*_*ame 24

从 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;
Run Code Online (Sandbox Code Playgroud)

to_jsonb(b)将转换整行,- 'item_id'然后将删除带有名称item_id的键,然后聚合结果。

  • 这个新功能似乎是 OP 所希望的。我添加了一个链接到我的答案。 (2认同)

小智 13

您实际上可以在没有 group by 的情况下使用子查询来完成

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;
Run Code Online (Sandbox Code Playgroud)

返回

{
  id: 1,
  name: "thing one",
  items:[
    { x: "child1", y: "child1 col2"},
    { x: "child2", y: "child2 col2"}
  ]
}
Run Code Online (Sandbox Code Playgroud)

John Atten 的这篇文章真的很有趣,而且有更多细节


小智 5

您可以使用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;
Run Code Online (Sandbox Code Playgroud)