Postgresql: от массива json к одному json с индексом по идентификатору

В функции SQL у меня есть массив jsonb в формате:

[{id: 1, name: John}, {id: 2, name: Jane}]

Я хотел бы преобразовать эту структуру в один jsonb, используя идентификаторы в качестве ключей:

{
  1: {id: 1, name: John},
  2: {id: 2, name: Jane}
}

JSON не поддерживает числовые ключи, поэтому на самом деле это будут "1" и "2". Вы хотите select только это или update это на месте?

Zegarek 05.04.2024 09:58
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
1
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете разбить массив с помощью jsonb_array_elements() , а затем повторно объединить его в объект с помощью jsonb_object_agg() . Демо на db<>fiddle:

select jsonb_object_agg(_element->>'id', _element)
from my_table
cross join lateral jsonb_array_elements(my_table.jdata) as _an(_element)
group by my_table.ctid
jsonb_object_agg {"1": {"id": 1, "имя": "Джон"}, "2": {"id": 2, "имя": "Джейн"}} {"1": {"id": 1, "name": "Джон"}, "2": {"id": 2, "name": "Тед"}, "11": {"id": 11, "имя": "Джейн"}, "111": {"id": 111, "имя": "Боб"}}

Вы не можете использовать номер JSON в качестве ключа, поэтому я использовал приведение аксессора -> к text. Не зная вашего первичного ключа, я группирую по ctid системного столбца, потому что он всегда присутствует и уникален, но вам, вероятно, нужен реальный ПК. cross join lateral можно заменить одной запятой ,, но явный синтаксис join обычно предпочтительнее старого неявного стиля.

Если вы хотите, чтобы новым ключом был не фактический id, а индекс в этом массиве, вы можете использовать с порядковым номером, чтобы получить их:

select jsonb_object_agg(n::text, e)
from my_table 
   , jsonb_array_elements(jdata) with ordinality as elements(e,n)
group by my_table.ctid

То же самое, но вместо update вместо select:

update my_table set jdata=
(select jsonb_object_agg(e->>'id', e)
 from jsonb_array_elements(jdata) e )
returning jsonb_pretty(jdata);
jsonb_pretty {
   "1": {
        "id": 1,
        "имя": "Джон"
    },
    "2": {
        "id": 2,
        "имя": "Джейн"
    }
} {
   "1": {
        "id": 1,
        "имя": "Джон"
    },
    "2": {
        "id": 2,
        "имя": "Тед"
    },
"11": {
        "id": 11,
        "name": "Джейн"
    },
    "111": {
        "id": 111,
        "имя": "Боб"
    }
}

Обратите внимание, что массив jsonb всегда сохраняет свой первоначальный порядок как значимый, но полученный объект jsonb будет свободно переупорядочен PostgreSQL на основе ключей.

cross join предпочтительнее соединения запятых.
Charlieface 05.04.2024 11:27

@Charlieface Я знаю, что я в меньшинстве по этой теме, отсюда и замечание в ответе. Мне нравится их краткость при работе с одной таблицей, питающей множество SRF, поэтому я не против время от времени давать случайным свидетелям воспоминания об их травме SQL в 80-х-00-х годах. :)

Zegarek 05.04.2024 11:34

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

Zegarek 05.04.2024 11:44

@Charlieface: Помимо личных предпочтений и сильных мнений, запятая и CROSS JOIN — это всего лишь два разных, одинаково допустимых варианта синтаксиса, даже с слегка разными ролями. См.: dba.stackexchange.com/a/167217/3684

Erwin Brandstetter 06.04.2024 05:49

Другие вопросы по теме