LINUX.ORG.RU

Массово заменить ключ в jsonb

 


0

1

Использую PGSQL 16.

Есть основная таблица

CREATE TABLE product (
  doc_id INT,
  details JSONB
);


INSERT INTO product VALUES (1, '{"CPU":4, "RAM":16, "HDD":2}');
INSERT INTO product VALUES (2, '{"CPU":3, "RAM":8, "HDD":1}');


doc_id |             details             
----+---------------------------------
  1 | {"CPU": 4, "HDD": 2, "RAM": 16}
  2 | {"CPU": 3, "HDD": 1, "RAM": 8}

Также есть таблица соотв. для замены товара

CREATE TABLE replace (
  id INT,
  doc_id INT,
  old_key text,
  new_key text
);
INSERT INTO replace VALUES (1, 1, 'CPU', 'RAM');
INSERT INTO replace VALUES (2, 1, 'HDD', 'SSD');
INSERT INTO replace VALUES (3, 2, 'HDD', 'SSD');

id | doc_id | old_key | new_key 
----+--------+---------+---------
  1 |      1 | CPU     | RAM
  2 |      1 | HDD     | SSD
  3 |      2 | HDD     | SSD

Нужно в таблице product в JSONB заменить старый ключ на новый ключ, если новый ключ уже есть в JSONB, то значения старого и нового ключей нужно сложить, то есть после замены должны получить такие данные product:

id |             details             
----+---------------------------------
  1 | {"SSD": 2, "RAM": 20}
  2 | {"CPU": 3, "SSD": 1, "RAM": 8}

У меня нет идей как это можно сделать, помогите пож-ста написать такой запрос

  • Раскрыть k/v из json в отдельные строки через jsonb_each
  • Модифицировать их как угодно, в частности переименовать ключи через join с таблицей переименований и сложить через group by
  • Схлопнуть обратно в jsonb через group_by и jsonb_object_agg
anonymous
()
UPDATE product p
SET details = (
    SELECT jsonb_object_agg(
        COALESCE(r.new_key, o.key),
        SUM((o.value)::int)
    )
    FROM jsonb_each(p.details) o(key, value)
    LEFT JOIN replace r ON o.key = r.old_key AND r.doc_id = p.doc_id
    GROUP BY COALESCE(r.new_key, o.key)
)
WHERE EXISTS (SELECT 1 FROM replace WHERE doc_id = p.doc_id);

не проверял сам знаешь почему

x905 ★★★★★
()
WITH expanded AS (
    SELECT
        p.doc_id,
        COALESCE(r.new_key, j.key) AS final_key,
        SUM(j.value::int) AS final_value
    FROM product p
    CROSS JOIN LATERAL jsonb_each_text(p.details) AS j(key, value)
    LEFT JOIN replace r
        ON r.doc_id = p.doc_id
       AND r.old_key = j.key
    GROUP BY
        p.doc_id,
        COALESCE(r.new_key, j.key)
),
rebuilt AS (
    SELECT
        doc_id,
        jsonb_object_agg(final_key, final_value) AS new_details
    FROM expanded
    GROUP BY doc_id
)
UPDATE product p
SET details = r.new_details
FROM rebuilt r
WHERE r.doc_id = p.doc_id;

чат гопота 5.3 Instant

gagarin0
()