Postgresql: как получить значения из массивов JSONB?

У нас есть документация официальный, которая мне непонятна без примеров.

У меня есть JSON, который хранит эту структуру, она немного больше, но такая же структура, больше JSON внутри массива:

WITH
  responses AS (
      SELECT *
      FROM (VALUES ((1,
        '[
          {
            "id": "13",
            "alias": "r1",
            "title": "quest",
            "answer": "5",
            "question": "qq",
            "answer_id": 10048
          },
          {
            "id": "24",
            "alias": "q6",
            "title": "quest",
            "answer": "yes",
            "question": "quest",
            "answer_id": 10094
          }
        ]' :: JSON),
        (2,
        '[
          {
            "id": "13",
            "alias": "r1",
            "title": "quest",
            "answer": "-1",
            "question": "qq",
            "answer_id": 10048
          },
          {
            "id": "24",
            "alias": "q6",
            "title": "quest",
            "answer": "no",
            "question": "quest",
            "answer_id": 10094
          }
        ]' :: JSON))



           )    TEST(id,val)

  )
  SELECT * from responses

Я мог преобразовать в плоскую структуру:

id| question| answer
-----------
1, 'r1', 5
1, 'q6', yes
2, 'r1', -1
2, 'q6', no

Но мне нужно таким образом получить результат

id| r1| q6
----------
1,  5, yes

2, -1, no

Как я могу получить результат, подобный предыдущему?

См. stackoverflow.com/a/23061730/50552, чтобы узнать, как превратить строки в столбцы.

Andomar 16.03.2018 16:06
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
1
333
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Есть некоторые проблемы с вашим запросом WITH.

Во-первых, нет необходимости выполнять «select * from (values ​​(..))» - VALUES - это сам запрос, который можно использовать без SELECT:

test=# values (1, 'zz'), (2, 'bbb');
 column1 | column2
---------+---------
       1 | zz
       2 | bbb
(2 rows)

Далее возникает проблема со скобками. Сравните предыдущий запрос с этим:

test=# values ((1, 'zz'), (2, 'bbb'));
 column1 | column2
---------+---------
 (1,zz)  | (2,bbb)
(1 row)

Если вы поместите дополнительные круглые скобки, как это, у вас будет только одна строка из двух столбцов, а значения в этих столбцах относятся к «анонимному» типу записи (его магия типа Postgres, очень мощная, но это другая история, здесь не нужна).

Итак, давайте исправим первую часть вашего CTE:

with responses(id, val) AS (
 values
  (1, '
    [
      {
        "id": "13",
        "alias": "r1",
        "title": "quest",
        "answer": "5",
        "question": "qq",
        "answer_id": 10048
      },
      {
        "id": "24",
        "alias": "q6",
        "title": "quest",
        "answer": "yes",
        "question": "quest",
        "answer_id": 10094
      }
    ]'::json
  ), (2, '
    [
      {
        "id": "13",
        "alias": "r1",
        "title": "quest",
        "answer": "-1",
        "question": "qq",
        "answer_id": 10048
      },
      {
        "id": "24",
        "alias": "q6",
        "title": "quest",
        "answer": "no",
        "question": "quest",
        "answer_id": 10094
      }
    ]'::json
  )
)
select * from responses;

Теперь мы можем использовать функцию json_array_elements (..) для извлечения элементов JSON из массивов JSON:

with responses(id, val) AS (
 values
 ...
)
select id, json_array_elements(val)
from responses;

Давайте воспользуемся им для построения «второй ступени» нашего CTE:

...
), extra(id, elem) as (
  select id, json_array_elements(val)
  from responses
)
...

И, наконец, мы получим такой результат:

...
select
  id,
  elem->>'id' as json_id,
  elem->>'alias' as alias,
  elem->>'question' as question,
  elem->>'answer' as answer
from extra;

Весь запрос:

with responses(id, val) AS (
 values
  (1, '
    [
      {
        "id": "13",
        "alias": "r1",
        "title": "quest",
        "answer": "5",
        "question": "qq",
        "answer_id": 10048
      },
      {
        "id": "24",
        "alias": "q6",
        "title": "quest",
        "answer": "yes",
        "question": "quest",
        "answer_id": 10094
      }
    ]'::json
  ), (2, '
    [
      {
        "id": "13",
        "alias": "r1",
        "title": "quest",
        "answer": "-1",
        "question": "qq",
        "answer_id": 10048
      },
      {
        "id": "24",
        "alias": "q6",
        "title": "quest",
        "answer": "no",
        "question": "quest",
        "answer_id": 10094
      }
    ]'::json
  )
), extra(id, elem) as (
  select id, json_array_elements(val)
  from responses
)
select
  id as row_id,
  elem->>'id' as json_id,
  elem->>'alias' as alias,
  elem->>'question' as question,
  elem->>'answer' as answer
from extra;

Результат:

 row_id | id | alias | question | answer
--------+----+-------+----------+--------
      1 | 13 | r1    | qq       | 5
      1 | 24 | q6    | quest    | yes
      2 | 13 | r1    | qq       | -1
      2 | 24 | q6    | quest    | no
(4 rows)

Это немного отличается от того, что вы хотели. То, что вы хотели, не может быть достигнуто с помощью чистого SQL, поскольку вы хотите иметь динамические имена столбцов в выходных данных - эти «r1» и «q6» должны динамически извлекаться из данных JSON. Однако с помощью plpgsql или с расширением tablefunc можно создать сводную таблицу, дайте мне знать, если вам это нужно.

вы не можете этого достичь без поворота? Получить имя столбца не проблема, можно жестко запрограммировать?

Rocketq 20.03.2018 14:54

Конечно, если вы можете жестко запрограммировать имена столбцов, вы легко сможете сделать это с помощью обычного sql.

Nick 20.03.2018 15:54

Но как? без поворота

Rocketq 20.03.2018 16:06

С участием. Но без расширений tapoueh.org/blog/2013/07/simple-case-for-pivoting-in-sql

Nick 20.03.2018 16:07

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