如何将多个列分组为单个数组或类似数组?

时间:2019-02-11 19:26:20

标签: sql json postgresql aggregate-functions

我希望查询返回这样的结构,其中tags是一个数组数组或类似的数组:

id | name | tags
1    a      [[1, "name1", "color1"], [2, "name2", color2"]]
2    b      [[1, "name1", "color1"), (3, "name3", color3"]]

我希望这个查询能够正常工作,但是却给我一个错误:

SELECT  i.id, i.name, array_agg(t.tag_ids, t.tag_names, t.tag_colors) as tags 
FROM    ITEMS
LEFT OUTER JOIN (
  SELECT      trm.target_record_id
            , array_agg(tag_id) as tag_ids
            , array_agg(t.tag_name) as tag_names
            , array_agg(t.tag_color) as tag_colors
  FROM        tags_record_maps trm
  INNER JOIN  tags t on t.id = trm.tag_id
  GROUP BY    trm.target_record_id
) t on t.target_record_id = i.id;

错误:

PG::UndefinedFunction: ERROR:  function array_agg(integer[], character varying[], character varying[]) does not exist
LINE 1: ..., action_c2, action_c3, action_name, action_desc, array_agg(...
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

此查询有效,并产生相似的结果(但不完全是我想要的):

SELECT  i.id, i.name, t.tag_ids, t.tag_names, t.tag_colors as tags  as tags 
FROM    ITEMS
LEFT OUTER JOIN (
  SELECT      trm.target_record_id, array_agg(tag_id) as tag_ids, array_agg(t.tag_name) as tag_names, array_agg(t.tag_color) as tag_colors
  FROM        tags_record_maps trm
  INNER JOIN  tags t on t.id = trm.tag_id
  GROUP BY    trm.target_record_id
) t on t.target_record_id = i.id;

结果:

id | name | tag_ids | tag_names         | tag_colors          
1    a      [1, 2]    ["name1, "name2"]   ["color1", "color2"]
1    a      [1, 3]    ["name1, "name3"]   ["color1", "color3"]

编辑:

此查询几乎产生了我要查找的内容,除了它将json键命名为f1f2f3。如果可以将它们命名为idnamecolor,那将是完美的:

  SELECT        trm.target_record_id, json_agg( (t.id, t.tag_name, t.tag_color) )
  FROM          tags_record_maps trm
  INNER JOIN    tags t on t.site_id = trm.site_id and t.id = trm.tag_id
  GROUP BY      trm.target_record_id
  having count(*) > 1;

结果:

[{"f1":1,"f2":"name1","f3":"color1"},{"f1":2,"f2":"name2","f3":"color2"}]

3 个答案:

答案 0 :(得分:2)

(t.id, t.tag_name, t.tag_color)ROW(t.id, t.tag_name, t.tag_color)的简短语法-ROW constructor不保留嵌套的属性名称。 The manual:

  

默认情况下,由ROW表达式创建的值是匿名记录类型。如有必要,可以将其强制转换为命名的复合类型-   表的行类型或使用以下命令创建的复合类型   CREATE TYPE AS

我强调大胆。要在结果中也获得正确的键名,请按照报价建议广播到注册的复合类型,使用嵌套子选择,或在其中使用json_build_object() Postgres 9.4或更高版本(有效地避免了ROW构造函数的先验性):

SELECT trm.target_record_id
     , json_agg(json_build_object('id', t.id
                                , 'tag_name', t.tag_name
                                , 'tag_color', t.tag_color)) AS tags
FROM   tags_record_maps trm
JOIN   tags             t USING (site_id)
WHERE  t.id = trm.tag_id
GROUP  BY trm.target_record_id
HAVING count(*) > 1;

我使用原始的列名,但是您可以自由选择键名。就您而言:

       json_agg(json_build_object('id', t.id
                                , 'name', t.tag_name
                                , 'color', t.tag_color)) AS tags

详细说明:

答案 1 :(得分:0)

为什么不尝试使用Json_Agg()

SELECT 
     json_agg(tag_ids, tag_names, tag_colors)
FROM items

等等...

答案 2 :(得分:0)

array_agg()一个参数放入数组中。您可以尝试将这些值连接在一起:

array_agg(t.tag_ids || ':' || t.tag_names || ':' || t.tag_colors)

或者使用行构造器:

array_agg( (t.tag_ids, t.tag_names, t.tag_colors) )