Как получить конкретный объект из массива jsonb в PostgreSQL?

15

У меня есть поле с именем 'user', которое содержит массив json, который выглядит примерно так:

"user":

[{ "_id" : "1", "count" : "4" }, { "_id" : "3", "count": "4"}]

Теперь я хочу запрос как:

select count from tablename where id = "1"

Я не могу получить конкретное поле countиз массива объектов json в PostgreSQL 9.4.

Раби С Шах
источник

Ответы:

17

Было бы намного эффективнее хранить ваши значения в нормализованной схеме. Тем не менее, вы также можете заставить его работать с вашей текущей настройкой.

Предположения

Предполагая это определение таблицы:

CREATE TABLE tbl (tbl_id int, usr jsonb);

«пользователь» является зарезервированным словом и требует использования двойных кавычек в качестве имени столбца. Не делай этого. Я использую usrвместо этого.

запрос

Запрос не так тривиален, как (теперь удаленные) комментарии, которые он казал:

SELECT t.tbl_id, obj.val->>'count' AS count
FROM   tbl t
JOIN   LATERAL jsonb_array_elements(t.usr) obj(val) ON obj.val->>'_id' = '1'
WHERE  t.usr @> '[{"_id":"1"}]';

Есть 3 основных шага :

1. Определите подходящие строки дешево

WHERE t.usr @> '[{"_id":"1"}]'идентифицирует строки с соответствующим объектом в массиве JSON. Выражение может использовать общий индекс GIN для jsonbстолбца или индекс с более специализированным классом операторов jsonb_path_ops:

CREATE INDEX tbl_usr_gin_idx ON tbl USING gin (usr jsonb_path_ops);

Добавленное WHEREпредложение логически избыточно , но оно требует использования индекса. Выражение в предложении соединения применяет то же условие, но только после удаления массива в каждой строке, соответствующей требованиям. С поддержкой индекса Postgres обрабатывает только те строки, которые содержат квалифицирующий объект для начала. Не имеет большого значения для маленьких таблиц, имеет огромное значение для больших таблиц и только нескольких подходящих строк.

Связанные с:

2. Определите соответствующий объект (ы) в массиве

Unnest с jsonb_array_elements(). ( unnest()подходит только для типов массивов Postgres.) Поскольку нас интересует только фактическое сопоставление объектов, немедленно отфильтруйте условие соединения.

Связанные с:

3. Извлечь значение для вложенного ключа 'count'

После квалификации объекты были извлечены, просто: obj.val->>'count'.

Эрвин Брандштеттер
источник
2
Откуда obj(value)взялся? Это на LATERAL JOIN, jsonb_array_elementsили где-то еще?
Тайлер ДеВитт
Похоже, что форматирование могло испортиться. Я правильно читаю, JOIN LATERAL jsonb_array_elements(t.usr) obj(value) is short for JOIN LATERAL jsonb_array_elements(t.usr) AS obj(value)что obj(value)это псевдоним таблицы и столбца? В этом примере, если objпсевдоним таблицы, к чему это псевдоним? Набор вернулся из jsonb_array_elements?
Тайлер ДеВитт
1
да и да я удалил свой зашифрованный комментарий.
Эрвин Брандштеттер
Нужно ли использовать псевдоним столбца? В моем тестировании JOIN LATERAL jsonb_array_elements(t.usr) obj ON obj->>'_id' = '1'был тот же эффект (как только вы обновили оператор select для использования valueвместо val). Похоже, что jsonb_array_elements(t.usr)возвращает таблицу только с одним столбцом. Postgres умный и понимающий, что obj ->>это то же самое, что и obj.val ->>?
Тайлер ДеВитт
Только с одним столбцом Postgres использует данный псевдоним в качестве имени таблицы и столбца. Я просто являюсь явным, так как есть много возвращающих множество функций, которые возвращают более одного столбца.
Эрвин Брандштеттер,