Извлечь ключ, значение из объектов json в Postgres

У меня есть таблица Postgres с таким содержимым:

id  | data

1   | {"a":"4", "b":"5"}
2   | {"a":"6", "b":"7"}
3   | {"a":"8", "b":"9"}

Первый столбец является целым числом, а второй - столбцом json.

Я хочу иметь возможность расширять ключи и значения из json, чтобы результат выглядел так:

id  | key  | value

1   | a    | 4
1   | b    | 5
2   | a    | 6
2   | b    | 7
3   | a    | 8
3   | b    | 9

Можно ли этого добиться в Postgres SQL?


Что я пробовал

Учитывая, что исходную таблицу можно смоделировать как таковую:

select *
from 
(
values
(1, '{"a":"4", "b":"5"}'::json),
(2, '{"a":"6", "b":"7"}'::json),
(3, '{"a":"8", "b":"9"}'::json)
) as q (id, data)

Я могу получить только ключи, используя:

select id, json_object_keys(data::json)
from 
(
values
(1, '{"a":"4", "b":"5"}'::json),
(2, '{"a":"6", "b":"7"}'::json),
(3, '{"a":"8", "b":"9"}'::json)
) as q (id, data)

И я могу получить их в виде наборов записей вот так:

select id, json_each(data::json)
from 
(
values
(1, '{"a":"4", "b":"5"}'::json),
(2, '{"a":"6", "b":"7"}'::json),
(3, '{"a":"8", "b":"9"}'::json)
) as q (id, data)

Но я не могу понять, как добиться результата с помощью идентификатора, ключа и значения.

Любые идеи?

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


person Tom G    schedule 18.08.2016    source источник
comment
Используйте функции json_object_keys или json_each как таблицу, а не как столбец: select id, j.key, j.value from my_table, json_each(data) j   -  person Abelisto    schedule 18.08.2016
comment
Спасибо, Абелисто. Конечно, я бы получил полное декартово произведение, а не таблицу, которую я ищу, используя предложенный вами запрос?   -  person Tom G    schedule 18.08.2016


Ответы (3)


SELECT q.id, d.key, d.value
FROM q
JOIN json_each_text(q.data) d ON true
ORDER BY 1, 2;

Функция json_each_text() - это функция, возвращающая набор, поэтому вы должны использовать ее как источник строки. Здесь вывод функции соединен сбоку к таблице q, что означает, что для каждой строки в таблице каждая пара (key, value) из столбца data присоединяется только к этой строке, поэтому связь между исходной строкой и строками, сформированными из объекта json, сохраняется.

Таблица q также может быть очень сложным подзапросом (или предложением VALUES, как в вашем вопросе). В функции соответствующий столбец используется из результата оценки этого подзапроса, поэтому вы используете только ссылку на псевдоним подзапроса и (псевдоним) столбца в подзапросе.

person Patrick    schedule 18.08.2016
comment
Спасибо, Патрик. Я все еще немного смущен тем, как я вписываю это в свой исходный запрос. Могу ли я определить q с помощью оператора WITH? - person Tom G; 18.08.2016
comment
Можно ли этого добиться без оператора WITH? - person Tom G; 18.08.2016
comment
q - это таблица, о которой вы говорили, вы использовали ее как прокси для исходной таблицы. - person Patrick; 18.08.2016
comment
Ах хорошо. Итак, в моем реальном примере на самом деле нет таблицы, а есть внутренний запрос с именем q. Я думаю, в этом случае оператор WITH - единственный способ? [p.s. Я принял ваш ответ] - person Tom G; 18.08.2016
comment
Вы можете использовать любой из них, но между оператором WITH и подзапросом есть небольшая разница: первый обрабатывается, как указано, а затем используется в основном запросе, в то время как подзапрос будет сглажен, а затем объединен с основным запросом. перед обработкой. Это может иметь важные последствия для производительности; подробное объяснение см. в этом отличном сообщении в блоге Крейга Рингера. - person Patrick; 18.08.2016
comment
Да, именно поэтому я хотел сделать это без оператора WITH. Чтобы сделать это с подзапросом, мне нужно повторить подзапрос дважды (то есть в обоих местах, где в вашем запросе выше есть 'q')? Или есть способ лучше? - person Tom G; 18.08.2016
comment
Спасибо за хороший ответ, Патрик, это оказалось для меня очень полезным. Очень признателен. - person Rob212; 16.08.2017
comment
Спасибо! Я потратил часы, пытаясь сделать именно это, и мне никогда бы в голову не пришло истинное соединение ON. - person Dmitri; 05.09.2017
comment
Если я правильно понял из документации, postgresql. org / docs / 9.6 / static / есть способ описать боковое соединение таблицы с функцией возврата набора, просто используя SELECT * FROM q, LATERAL jsonb_each(q.data), не запутывая JOIN ... ON TRUE. Он дает ожидаемый результат объединения каждой строки из q с результатом функции возврата набора для этой конкретной строки. - person Stanislav Savulchik; 02.02.2018

Это тоже решит эту проблему:

select you_table.id , js.key, js.value
from you_table, json_each(you_table.data) as js
person Roman M    schedule 03.06.2020

Другой способ, который, как мне кажется, очень прост в работе, когда у вас есть несколько jsons, к которым нужно присоединиться, - это сделать что-то вроде:

SELECT data -> 'key'   AS key, 
       data -> 'value' AS value 
FROM   (SELECT Hstore(Json_each_text(data)) AS data 
        FROM   "your_table") t;
person Gregorio Freidin    schedule 26.05.2020