Home > OS >  Postgresql -> JSON Array to rows
Postgresql -> JSON Array to rows

Time:10-16

One of the columns in my table, has JSONArray data. I have used jsonb_agg() in a jsonb column to a view. Now the data from view looks like below

[
  {
    "Android": 2,
    "Windows": 1,
    "Macintosh": 1
  },
  {
    "iOS": 1,
    "Android": 2,
    "Windows": 2,
    "Macintosh": 2
  },
  {},
  {
    "Android": 1,
    "Windows": 1
  },
  {
    "Android": 1
  },
  {
    "iOS": 1,
    "Android": 2
  },
  {
    "iOS": 2,
    "Android": 1
  },
  {
    "iOS": 2
  },
  {
    "Android": 1
  },
  {
    "iOS": 2,
    "Windows": 1
  },
  {
    "Android": 5
  },
  {},
  {},
  {
    "iOS": 1,
    "Android": 1
  },
  {},
  {},
  {
    "Windows": 3
  }
]

However, I need to product the below result

{
  "Android": 16,
  "Windows": 8,
  "Macintosh": 3,
  "iOS": 9
}

Is there a way within PostgreSQL to achieve this?

CodePudding user response:

Yes there is a way. Here it is.

select to_jsonb(t.*) from
(
 select 
    sum((j->>'Android')::numeric) "Android", 
    sum((j->>'Windows')::numeric) "Windows", 
    sum((j->>'Macintosh')::numeric) "Macintosh", 
    sum((j->>'iOS')::numeric) "iOS" 
 from jsonb_array_elements('[{"Android": 2, "Windows": 1, "Macintosh": 1}, {"iOS": 1, "Android": 2, "Windows": 2, "Macintosh": 2}, {}, {"Android": 1, "Windows": 1}, {"Android": 1}, {"iOS": 1, "Android": 2}, {"iOS": 2, "Android": 1}, {"iOS": 2}, {"Android": 1}, {"iOS": 2, "Windows": 1}, {"Android": 5}, {}, {}, {"iOS": 1, "Android": 1}, {}, {}, "Windows": 3}]'::jsonb) as j
) as t;

As a parameterized query:

select to_jsonb(t.*) from
(
 select 
    sum((j->>'Android')::numeric) "Android", 
    sum((j->>'Windows')::numeric) "Windows", 
    sum((j->>'Macintosh')::numeric) "Macintosh", 
    sum((j->>'iOS')::numeric) "iOS" 
 from jsonb_array_elements(?::jsonb) as j
) as t;

CodePudding user response:

You can combine many json/jsonb functions to get the result

SELECT
    jsonb_agg(json_build_object(key, sum))
FROM (
    SELECT
        key,
        sum(value)
    FROM (
        SELECT
            (arr).key,
            (arr).value::int
        FROM (
            SELECT
                jsonb_each(j) arr
            FROM (
                SELECT
                    jsonb_array_elements('[{"Android": 2, "Windows": 1, "Macintosh": 1}, {"iOS": 1, "Android": 2, "Windows": 2, "Macintosh": 2}, {}, {"Android": 1, "Windows": 1}, {"Android": 1}, {"iOS": 1, "Android": 2}, {"iOS": 2, "Android": 1}, {"iOS": 2}, {"Android": 1}, {"iOS": 2, "Windows": 1}, {"Android": 5}, {}, {}, {"iOS": 1, "Android": 1}, {}, {}, {"Windows": 3}]
'::jsonb) AS j) AS jpart) AS x) sub
    GROUP BY
        1) AS sub2

output: [{"Windows": 8}, {"Android": 16}, {"iOS": 9}, {"Macintosh": 3}]

CodePudding user response:

You can use dynamic key, value like that:

with data as (
  select
    je.key,
    sum(je.value::int)
  from 
    json_array_elements('[{"Android": 2, "Windows": 1, "Macintosh": 1}, {"iOS": 1, "Android": 2, "Windows": 2, "Macintosh": 2}, {}, {"Android": 1, "Windows": 1}, {"Android": 1}, {"iOS": 1, "Android": 2}, {"iOS": 2, "Android": 1}, {"iOS": 2}, {"Android": 1}, {"iOS": 2, "Windows": 1}, {"Android": 5}, {}, {}, {"iOS": 1, "Android": 1}, {}, {}, {"Windows": 3}]') d
    cross join json_each_text(d) as je
  group by 1
)
select json_object_agg(d.key, d.sum)
from data d

Or if you have table and column you can use this sample format like that:

with data as (
  select
    je.key,
    sum(je.value::int)
  from 
    your_table t join
    json_array_elements(t.your_json_coulmn) d on true
    cross join json_each_text(d) as je
  group by 1
)
select json_object_agg(d.key, d.sum)
from data d
  • Related