HiveBrain v1.2.0
Get Started
← Back to all entries
snippetsqlMinor

How to sum nested values from a JSON array based on conditions

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
conditionshowarrayjsonnestedbasedsumvaluesfrom

Problem

I have a table with 3 columns in Postgres 14.2: name, name_adds, aditional.

And these rules:

-
name and name_adds can be filled with the same value or not. Example:
name (john) and name_adds (doe) or both value is (john)

-
column additional has one of two formats:

format 1:

{"default":[{"value_1": 100, "value_2": 0.1},{"value_1": 200, "value_2": 0.2}], "non_default":[{"value_1": 200, "value_2": 0.1}, {"value_1": 400, "value_2": 0.1}]}

format 2:

[{"value_1": 10,"value_2": 11},{"value_1": 1,"value_2": 19}

-
If (name = name_adds) sum default -> value_1

-
If (name != name_adds) sum non_default -> value_1

How can I sum format 1 or 2 from point 2. based on the rules in point 3. and 4.?

Example table:

name    name_adds   additional
---------------------------------------------------------------
john    john      {"default":[{"value_1": 100, "value_2": 0.1}, 
                  {"value_1": 200, "value_2": 0.2}],"non_default": 
                  [{"value_1": 200, "value_2": 0.1}, {"value_1": 400, 
                   "value_2": 0.1}]}

john     doe     {"default":[{"value_1": 100, "value_2": 0.1}, 
                  {"value_1": 200, "value_2": 0.2}],"non_default": 
                  [{"value_1": 200, "value_2": 0.1}, {"value_1": 400, 
                   "value_2": 0.1}]}

downy    downy    [{"value_1": 10, "value_2": 11},{"value_1": 1,"value_2": 19}]

downy    dan      [{"value_1": 10, "value_2": 11},{"value_1": 1,"value_2": 19}]


Expected result:

name    name_adds   sum_result
---------------------------------------------------------------
john    john        300

john     doe        600

downy    downy      11

downy    dan        11


I tried a query in this online demo, but didn't manage to get the expected result.

Solution

SELECT a.name, a.name_adds, sub.*
FROM   test_json a
CROSS  JOIN LATERAL (
   SELECT sum((obj ->> 'value_1')::int) AS sum_result  -- or numeric?
   FROM   json_array_elements(COALESCE(CASE WHEN name = name_adds
                                            THEN additional::json -> 'default'
                                            ELSE additional::json -> 'non_default' END
                                     , additional::json)) obj
   ) sub;


db<>fiddle here

This relies on additional::json -> 'default' (or 'non_default', respectively) being NULL in case of "format 2", in which case we take additional::json directly for "format 1" using COALESCE.

Then unnest with json_array_elements(), and sum extracted value_1.

The column additional should be type json (or jsonb) to begin with.

Chances are, you would be much better off with a normalized relational design replacing that highly regular JSON column completely.

Code Snippets

SELECT a.name, a.name_adds, sub.*
FROM   test_json a
CROSS  JOIN LATERAL (
   SELECT sum((obj ->> 'value_1')::int) AS sum_result  -- or numeric?
   FROM   json_array_elements(COALESCE(CASE WHEN name = name_adds
                                            THEN additional::json -> 'default'
                                            ELSE additional::json -> 'non_default' END
                                     , additional::json)) obj
   ) sub;

Context

StackExchange Database Administrators Q#313252, answer score: 4

Revisions (0)

No revisions yet.