Как объединить вложенное значение из столбца jsonb? - PullRequest
1 голос
/ 17 марта 2020

У меня есть база данных PostgreSQL 11 с этими таблицами:

CREATE TABLE stats (
   id integer NOT NULL,
   uid integer NOT NULL,
   date date NOT NULL,
   data jsonb DEFAULT '[]'::json NOT NULL
);
INSERT INTO stats(id, uid, date, data) VALUES
   (1, 1, '2020-10-01', '{"somerandomhash":{"source":"thesource"}}');

CREATE TABLE links(
   id integer NOT NULL,
   uuid uuid NOT NULL,
   path text NOT NULL
);
INSERT INTO links(id, uuid, path) VALUES
   (1, 'acbd18db-4cc2-f85c-edef-654fccc4a4d8', 'thesource');

Моя цель - создать новую таблицу reports с data из таблицы stats, но с новой ключ из таблицы links. Это будет выглядеть так:

CREATE TABLE reports(
    id integer NOT NULL,
    uid integer NOT NULL,
    date date NOT NULL,
    data jsonb DEFAULT '[]'::json NOT NULL
);

INSERT INTO reports(id, uid, date, data) VALUES
   (1, 1, 2020-10-01, {"uuid":{"source":"thesource"});

Для этого я попытался присоединиться к таблице links слева, чтобы получить значение столбца uuid - без везения:

SELECT s.uid, s.date, s.data->jsonb_object_keys(data)->>'source' as path, s.data->jsonb_object_keys(data) as data, l.uuid
FROM stats s LEFT JOIN links l ON s.data->jsonb_object_keys(data)->>'source' = l.path

Я пытался использовать результат s.data-> jsonb_object_keys (data) - >> 'source' в левом соединении, но получил ошибку:

ERROR:  set-returning functions are not allowed in JOIN conditions

I попытался использовать LATERAL, но результат все еще не действителен.
Как заставить это работать?

Ответы [ 2 ]

1 голос
/ 18 марта 2020

jsonb_object_keys() - это функция возврата набора, которую нельзя использовать так, как вы делаете - как вам сообщают сообщения об ошибках. Более того, json_object_keys() возвращает ключ верхнего уровня , но, похоже, вас интересует только значение . Попробуйте вместо этого jsonb_each():

SELECT s.id
     , s.uid
     , s.date
     , jsonb_build_object(l.uuid::text, o.value) AS new_data
FROM   stats s
CROSS  JOIN LATERAL jsonb_each(s.data) o  -- defaults to column names (key, value)
LEFT   JOIN links l ON l.path = o.value->>'source';

дБ <> скрипка здесь

jsonb_each() возвращает top- клавиша уровня и значение . Продолжайте использовать только значение .

Вложенный объект JSON, кажется, имеет постоянное имя ключа 'source' . Итак, условие соединения: l.path = o.value->>'source'.

Наконец, создайте новое значение jsonb с jsonb_build_object().

Пока это работает, как показано, пара остаются вопросы:

  • Выше предполагается, что всегда есть один ключ верхнего уровня в stats.data. Если нет, вам придется определить, что делать ...

  • Выше предполагается, что в таблице links всегда есть только одно совпадение. Если нет, вам нужно определить, что делать ...

  • Самое главное: Если data так же регулярно, как вы это делаете рассмотрите простой столбец «uuid» (или в любом случае отбросьте его, так как значение в таблице links) и простой столбец «source», чтобы заменить столбец jsonb. Намного проще и эффективнее.

0 голосов
/ 17 марта 2020

Похоже, что вы хотите присоединиться с помощью клавиши "source" из столбца JSON.

Вместо

s.data->jsonb_object_keys(data)->>'source'

Попробуйте это

s.data ->> 'source'

Если мои предположения верны, весь запрос может go выглядеть так:

SELECT
  s.uid,
  s.date,
  s.data ->> 'source' AS path,
  s.data -> jsonb_object_keys(data) AS data,
  l.uuid
FROM stats s
LEFT JOIN links l ON s.data ->> 'source' = l.path
...