如何从 jsonb 列加入嵌套值?

How to join on a nested value from a jsonb column?

我有一个带有这些 table 的 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');

我的目标是使用 stats table 中的 data 创建一个新的 table reports,但使用 [=] 中的新密钥17=] table。它看起来像这样:

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"});

为此,我尝试左连接 table 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

我尝试在left join中使用s.data->jsonb_object_keys(data)->>'source'的结果,但是得到了错误:

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

我尝试使用 LATERAL 但仍然无效。
如何实现?

您似乎想通过 JSON 列中的 "source" 键加入。

而不是

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

jsonb_object_keys() is a set-returning function which cannot be used the way you do - as the error messages tells you. What's more, json_object_keys() returns top-level key(s), but it seems you are only interested in the value. Try 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<>fiddle here

jsonb_each() returns 顶级键 值。仅使用 value.

继续

嵌套的 JSON 对象似乎具有常量键名称 'source'。所以连接条件是l.path = o.value->>'source'.

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

虽然这可以正常工作,但仍有几个问题:

  • 上面假设在stats.data中总是恰好有一个顶级键。如果没有,您必须定义要做什么...

  • 上面假设在tablelinks中总是只有一个匹配项。如果没有,您必须定义要做什么...

  • 最重要的是: 如果 data 和你认为的一样规则,考虑一个普通的 "uuid" 列(或者删除它,因为值在 table links 中)和一个普通的列 "source" 替换 jsonb 列。 更简单、更高效。