使用 JSONB 加入 PostgreSQL

Kok*_*zzu 24 postgresql join json postgresql-9.4

我有这个 SQL:

CREATE TABLE test(id SERIAL PRIMARY KEY, data JSONB);

INSERT INTO test(data) VALUES
   ('{"parent":null,"children":[2,3]}'),
   ('{"parent":1,   "children":[4,5]}'),
   ('{"parent":1,   "children":[]}'),
   ('{"parent":2,   "children":[]}'),
   ('{"parent":2,   "children":[]}');
Run Code Online (Sandbox Code Playgroud)

那会给:

 id |                 data                 
----+--------------------------------------
  1 | {"parent": null, "children": [2, 3]}
  2 | {"parent": 1, "children": [4, 5]}
  3 | {"parent": 1, "children": []}
  4 | {"parent": 2, "children": []}
  5 | {"parent": 2, "children": []}
Run Code Online (Sandbox Code Playgroud)

当进行正常的一对多时,它会显示如下内容:

SELECT * 
FROM test x1
  LEFT JOIN test x2
    ON x1.id = (x2.data->>'parent')::INT;
 id |                 data                 | id |               data                
----+--------------------------------------+----+-----------------------------------
  1 | {"parent": null, "children": [2, 3]} |  2 | {"parent": 1, "children": [4, 5]}
  1 | {"parent": null, "children": [2, 3]} |  3 | {"parent": 1, "children": []}
  2 | {"parent": 1, "children": [4, 5]}    |  4 | {"parent": 2, "children": []}
  2 | {"parent": 1, "children": [4, 5]}    |  5 | {"parent": 2, "children": []}
  5 | {"parent": 2, "children": []}        |    | 
  4 | {"parent": 2, "children": []}        |    | 
  3 | {"parent": 1, "children": []}        |    | 
Run Code Online (Sandbox Code Playgroud)

如何根据孩子加入(使用LEFT JOINWHERE IN)?我试过了:

SELECT data->>'children' FROM test;
 ?column? 
----------
 [2, 3]
 [4, 5]
 []
 []
 []

SELECT json_array_elements((data->>'children')::TEXT) FROM t...
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

SELECT json_array_elements((data->>'children')::JSONB) FROM ...
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

SELECT json_to_record((data->>'children')::JSON) FROM test;
ERROR:  function returning record called in context that cannot accept type record
HINT:  Try calling the function in the FROM clause using a column definition list.

SELECT * FROM json_to_record((test.data->>'children')::JSON);
ERROR:  missing FROM-clause entry for table "test"
LINE 1: SELECT * FROM json_to_record((test.data->>'children')::JSON)...
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 26

这会更有效率:

使用jsonbjsonb_array_elements_text()在第 9.4+ 页

EXPLAIN 
SELECT p.id AS p_id, p.data
     , c.id AS c_id, c.data
FROM   test p
LEFT   JOIN LATERAL jsonb_array_elements_text(p.data->'children') pc(child) ON TRUE
LEFT   JOIN test c ON c.id = pc.child::int;
Run Code Online (Sandbox Code Playgroud)

db<>在这里摆弄

关于jsonb_array_elements_text()

使用->运算符而不是->>在对 的引用中children。你拥有它的方式,你首先将json/投射jsonbtext,然后再回到json.

调用集合返回函数的干净方法是LEFT [OUTER] JOIN LATERAL. 这包括没有孩子的行。要排除这些,请更改为[INNER] JOIN LATERALor CROSS JOIN- 或带逗号的速记语法:

, json_array_elements(p.data->'children') pc(child)
Run Code Online (Sandbox Code Playgroud)

避免结果中出现重复的列名。

jsonjson_array_elements()在第 9.3 页

SELECT p.id AS p_id, p.data AS p_data
     , c.id AS c_id, c.data AS c_data
FROM   test p
LEFT   JOIN LATERAL json_array_elements(p.data->'children') pc(child) ON TRUE
LEFT   JOIN test c ON c.id = pc.child::text::int;
Run Code Online (Sandbox Code Playgroud)

旧的sqlfiddle

旁白:标准化的数据库设计,基本数据类型将是方式这更有效。