如何从jsonb列加入嵌套值?

时间:2020-03-17 18:41:15

标签: sql postgresql left-join jsonb set-returning-functions

我有一个带有以下表的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表中的datastats表中的新键来创建新表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

我尝试使用LATERAL,但仍然无效。
如何进行这项工作?

2 个答案:

答案 0 :(得分:1)

jsonb_object_keys()是一个返回设置的函数,不能按照您的方式使用-就像错误消息告诉您的那样。而且,json_object_keys()返回顶级,但是您似乎只对 value 感兴趣。请尝试使用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';

db <>提琴here

jsonb_each()返回顶级密钥值。仅使用 value

嵌套的JSON对象似乎具有常量键名称​​'source'。因此,连接条件为l.path = o.value->>'source'

最后,使用jsonb_build_object()构建新的jsonb值。

尽管这种方法如所示,但仍有两个问题:

  • 以上假设stats.data中总是存在一个一个顶级密钥。如果没有,则必须定义要做什么...

  • 以上假设表links中始终存在一个完全匹配的项。如果没有,则必须定义要做什么...

  • 最重要的是: 如果data符合您的常规要求,请考虑使用普通的“ uuid”列(或将其放在表links中的值中去掉)和普通的“ source”列进行替换jsonb列。 很多更简单,更有效。

答案 1 :(得分:0)

您似乎想通过JSON列中的“源”键加入。

代替

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

尝试一下

s.data ->> 'source'

如果我的假设正确,则整个查询可以这样:

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