Postgres根据2列汇总结果

时间:2017-07-01 14:07:35

标签: sql postgresql select group-by

在postgres 9.6中,我有一个包含这3列的表格。

id | val1 | val2
------------------
1  | x    | 1
1  | x    | 2
1  | x    | 3
1  | y    | 4
2  | y    | 1
2  | y    | 2

我可以使用聚合函数将其转换为此吗?

id | 
------------------------------
1  | { x: [1, 2, 3], y: [4] }
2  | { y: [1, 2] }

或者如果我无法找到我想要的东西,下面的东西也可以。或者任何允许我在我的应用程序代码中将结果转换为上述内容的东西。

id | 
------------------------------
1  | [ {x: 1}, {x: 2}, {x: 3}, {y: 4} ]
2  | [ {y: 1}, {y: 2} ]

我知道我可以执行select id, array_agg(val2) from mytable group by val2之类的操作,但只能按val2分组并返回类似

的内容
id | 
------------------
1  | [1, 2, 3, 4]
2  | [1, 2]

4 个答案:

答案 0 :(得分:5)

以下是使用jsonb_agg and jsonb_object_agg functions

的一种方法
with the_table(id , val1 , val2) as(
    select 1 ,'x',1 union all
    select 1  ,'x', 2 union all
    select 1  ,'x', 3 union all
    select 1  ,'y', 4 union all
    select 2 ,'y', 1 union all
    select 2 ,'y', 2
)


select id, jsonb_object_agg(val1, arr) from (
    select id, val1, jsonb_agg(val2) as arr
    from the_table
    group by id, val1
)t
group by id

答案 1 :(得分:5)

你可以用:

SELECT
    id, json_object_agg(val1, aaa)
FROM
    (
    SELECT
        id, val1, json_agg(val2) AS aaa
    FROM
        t
    GROUP BY
        id, val1
    ) AS q 
GROUP BY
    id;
id | json_object_agg               
-: | :-----------------------------
 1 | { "x" : [1, 2, 3], "y" : [4] }
 2 | { "y" : [1, 2] }              

您可以在 dbfiddle here

找到生成此结果所需的定义

您将使用这两个功能:

  • json_agg
  • json_object_agg

可以在PostgreSQL doc

中的9.20. Aggregate Functions找到该文档

答案 2 :(得分:1)

另一个有用的代码片段受到SO问题的启发:

drop aggregate if exists jsonb_objarr_agg(text, anyelement);
drop function if exists jsonb_objarr_agg_func(jsonb, text, anyelement);
create function jsonb_objarr_agg_func(jsonb, text, anyelement) returns jsonb immutable language sql as $$
  select
    jsonb_set(
      $1,
      array[$2],
      coalesce(
        case
          when jsonb_typeof($1->$2) <> 'array' then to_jsonb(array[$1->$2])
          else $1->$2
        end, 
        '[]'::jsonb) || to_jsonb($3), true);
$$;

select jsonb_objarr_agg_func('{"a":1}', 'a', 2);

create aggregate jsonb_objarr_agg(text, anyelement) (
  sfunc = jsonb_objarr_agg_func,
  stype = jsonb,
  initcond = '{}');

with t(i, v1, v2) as (values
    (1, 'x', 1),
    (1, 'x', 2),
    (1, 'x', 3),
    (1, 'y', 4),
    (2, 'y', 1),
    (2, 'y', 2))
select
  i,
  jsonb_objarr_agg(v1, v2)
from t
group by i;
╔═══╤════════════════════════════╗
║ i │      jsonb_objarr_agg      ║
╠═══╪════════════════════════════╣
║ 1 │ {"x": [1, 2, 3], "y": [4]} ║
║ 2 │ {"y": [1, 2]}              ║
╚═══╧════════════════════════════╝

此聚合允许使用第一个参数作为键jsonb,并将第二个参数聚合到数组中。

如果您需要json类型,则可以在此代码中轻松将jsonb替换为json

live demo

答案 3 :(得分:0)

我认为以下是您想要的:

select id, json_agg(json_build_object(val1, val2s ))
from (select id, val1, array_agg(val2) as val2s
      from t
      group by id, val1
     ) t
group by id;