PL / pgSQL суммирует два json или две строки поле за полем

#sql #postgresql #plpgsql

#sql #postgresql #plpgsql

Вопрос:

Я часами пытаюсь найти ответ на свою проблему, но у меня не получилось:

Я хочу суммировать все значения некоторых строк (которые имеют одинаковые значения даты и ‘name’) в другую, которая будет ‘aggregation one’.

Для этого я выбираю нужные строки, выполняю циклы для всех строк по дате / имени и выполняю один заключительный цикл для поля rows, чтобы суммировать поля одно за другим:

 DO $code$

DECLARE 
date_trunc_by_hour record; 
row_for_hour record;
field_of_row_for_hour record;
aggreged metrics.bot%ROWTYPE;
first boolean := true;
BEGIN

--boucle par heure, loop by hour
FOR date_trunc_by_hour IN
SELECT date_trunc('hour',date) as date_trunc
FROM metrics.bot 
where aggrege = false 
group by date_trunc
LOOP
 RAISE INFO 'Recherche pour la date : %', date_trunc_by_hour.date_trunc;
--boucle par row dans l'heure, loop on each row for the hour selected
first := true ;
FOR row_for_hour IN
  SELECT *
    FROM metrics.bot 
    where date_trunc('hour',date) = date_trunc_by_hour.date_trunc       

    LOOP
    IF first = true 
        THEN
        aggreged := row_for_hour; -- if it's the first row, i take the values of this row to add ones of the others rows after
        RAISE INFO '%', aggreged;
        first = false;
        ELSE -- if its not the first, i want to add values field by field to the first one
            FOR field_of_row_for_hour IN select * from json_each(row_to_json(row_for_hour))
            LOOP

        -- Here i would like for each field of each row in the loop, to add incoming row value to aggreged value, value can be a json                                                                

            END LOOP;

    END IF;
    END LOOP;

END LOOP;

END $code$;
  

Например, первые две строки, имеющие одинаковую пару бот / дата, должны давать третью:

  ===== ===================== ========= ========= ===== ============================ ========== 
  BOT          DATE           PROCESS   INSERT    MAJ             ERRORS             aggreged  
 ===== ===================== ========= ========= ===== ============================ ========== 
    1   2019-02-12 17:00:00   scan            2     5   {"société":1}                false     
    1   2019-02-12 17:00:00   scan            4     7   {"société":1,"enchere":1}    false     
    1   2019-02-12 17:00:00   scan            6    12   {"enchere":1, "société":2}   true      
 ===== ===================== ========= ========= ===== ============================ ========== 
  

Которые я вставлю в свою таблицу перед удалением row1 и row2.

На моей памяти я однажды решил эту проблему с помощью приведения json, но я не помню как.

Спасибо за вашу помощь

Ответ №1:

Вам нужен пользовательский агрегат, который суммирует целочисленные атрибуты:

 create or replace function jsonb_sum_attributes(jsonb, jsonb)
returns jsonb language sql as $$
    select jsonb_object_agg(key, sum)
    from (  
        select key, sum(value::int)
        from (
            select *
            from jsonb_each_text($1)
            union all
            select *
            from jsonb_each_text($2)
            ) s
        group by key
        ) s
$$;

create aggregate jsonb_sum_attributes_agg(jsonb) 
(  
    sfunc = 'jsonb_sum_attributes',
    stype = jsonb,
    initcond = '{}'
);
  

Запрос:

 select bot, date, process, insert, maj, errors, aggreged
from bot
union 
select bot, date, process, sum(insert), sum(maj), jsonb_sum_attributes_agg(errors), true
from bot
group by bot, date, process
order by bot, date, process, aggreged

 bot |        date         | process | insert | maj |            errors            | aggreged 
----- --------------------- --------- -------- ----- ------------------------------ ----------
   1 | 2019-02-12 17:00:00 | scan    |      2 |   5 | {"société": 1}               | f
   1 | 2019-02-12 17:00:00 | scan    |      4 |   7 | {"enchere": 1, "société": 1} | f
   1 | 2019-02-12 17:00:00 | scan    |      6 |  12 | {"enchere": 1, "société": 2} | t
(3 rows)    
  

Рабочий пример в rextester.