Обходите вложенный объект json и вставьте ключи и значения в две связанные таблицы - PullRequest
1 голос
/ 06 октября 2019

Я передаю следующую структуру json моей процедуре:

{questA: [[a1, a2], [a3, a4]], questB: [[b1, b2], [b2, b4]...]}

Я бы хотел пройтись по всем ключам 'quest' (questA, questB ...)и вставьте каждое имя ключа в одну таблицу, и его значения устанавливаются в другую таблицу в несколько строк, чтобы у каждого набора (a1, a2) была своя собственная строка плюс поле внешнего ключа для родительского квест-ключа.

quest
-------
id
key

questValues
-------------
id
val
val
quest_id
foreign key (quest_id) references quest(id)

IЯ пробовал что-то вроде:

FOR key, val IN SELECT * FROM jasonb_each_text(myJson) LOOP
...
END LOOP;

Но он зацикливается на всем, поэтому массивы val теперь просто текстовые. Я думал о связывании select с одной из буквенных функций json, но я не уверен в синтаксисе.

1 Ответ

0 голосов
/ 06 октября 2019

Вы действительно можете сделать это с помощью цепочки вывода различных функций JSON:

with input (parameter) as (
  values ('{"questA": [["a1", "a2"], ["a3", "a4"]], "questB": [["b1", "b2"], ["b2", "b4"]]}'::jsonb)
), elements as (
  select j.quest, k.answer
  from input i 
    cross join lateral jsonb_each(i.parameter) as j(quest,vals)
    cross join lateral jsonb_array_elements(j.vals) as k(answer)
), new_quests as (
  insert into quest ("key")
  select distinct quest
  from elements
  returning *
)  
insert into quest_values (val1, val2, quest_id)
select e.answer ->> 0 as val1,
       e.answer ->> 1 as val2,
       nq.id as quest_id
from new_quests nq
  join elements e on e.quest = nq.key;

Первый шаг («элементы») превращает значение JSON в строки, которые можно использовать в качестве источника операторов INSERT. ,Он возвращает это:

quest  | answer      
-------+-------------
questA | ["a1", "a2"]
questA | ["a3", "a4"]
questB | ["b1", "b2"]
questB | ["b2", "b4"]

Следующий шаг вставляет уникальные значения столбца quest в таблицу quest и возвращает сгенерированные идентификаторы.

И последний оператор объединяет сгенерированные идентификаторы со строками первого шага и извлекает два элемента массива как два значения. Он использует этот запрос в качестве источника для вставки в таблицу quest_values.

Внутри процедуры вам явно не нужна часть, генерирующая данные примера, поэтому она будет выглядеть примерно так:

with elements as (
  select j.quest, k.answer
  from jsonb_each(the_parameter) as j(quest,vals)
    cross join lateral jsonb_array_elements(j.vals) as k(answer)
), new_quests as (
  insert into quest ("key")
  select distinct quest
  from elements
  returning *
)  
insert into quest_values (val1, val2, quest_id)
select e.answer ->> 0 as val1,
       e.answer ->> 1 as val2,
       nq.id as quest_id
from new_quests nq
  join elements e on e.quest = nq.key;

Где the_parameter - параметр JSONB, переданный вашей процедуре.

Онлайн пример: https://rextester.com/NBJIK44025

...