私はこの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":[]}');
それは与えるでしょう:
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": []}
通常の1対多の場合、次のようになります。
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": []} | |
子供に基づいて参加する方法(LEFT JOIN
またはWHERE 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)...
これはより効率的です:
json
および json_array_elements()
を使用_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;
_
children
への参照では、_->
_ではなく_->>
_演算子を使用します。あなたのやり方では、最初にjson
/jsonb
をtext
にキャストし、次にjson
にキャストします。
セットを返す関数を呼び出すきれいな方法は_LEFT [OUTER] JOIN LATERAL
_です。これには、子のない行が含まれます。それらを除外するには、_[INNER] JOIN LATERAL
_または_CROSS JOIN
_に変更するか、カンマを使用した省略構文:
_, json_array_elements(p.data->'children') pc(child)
_
結果の列名の重複を回避します。
jsonb
および jsonb_array_elements()
を使用_EXPLAIN
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 jsonb_array_elements(p.data->'children') pc(child) ON TRUE
LEFT JOIN test c ON c.id = pc.child::text::int;
_
_-------------------------------------------------------------------------------------------
Hash Left Join (cost=37.69..4826.24 rows=123000 width=72)
Hash Cond: (((pc.child)::text)::integer = c.id)
-> Nested Loop Left Join (cost=0.01..2482.31 rows=123000 width=68)
-> Seq Scan on test p (cost=0.00..22.30 rows=1230 width=36)
-> Function Scan on jsonb_array_elements pc (cost=0.01..1.01 rows=100 width=32)
-> Hash (cost=22.30..22.30 rows=1230 width=36)
-> Seq Scan on test c (cost=0.00..22.30 rows=1230 width=36)
_
余談:基本的なデータ型を持つ正規化されたDB設計は、これに対してwayより効率的です。
気にしない、私は道を見つけた
SELECT *
FROM ( SELECT *, json_array_elements((data->>'children')::JSON) child FROM test) x1
LEFT JOIN test x2
ON x1.child::TEXT::INT = x2.id
;
id | data | child | id | data
----+--------------------------------------+-------+----+-----------------------------------
1 | {"parent": null, "children": [2, 3]} | 2 | 2 | {"parent": 1, "children": [4, 5]}
1 | {"parent": null, "children": [2, 3]} | 3 | 3 | {"parent": 1, "children": []}
2 | {"parent": 1, "children": [4, 5]} | 4 | 4 | {"parent": 2, "children": []}
2 | {"parent": 1, "children": [4, 5]} | 5 | 5 | {"parent": 2, "children": []}
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Hash Left Join (cost=37.67..4217.38 rows=123000 width=104)
Hash Cond: ((((json_array_elements(((test.data ->> 'children'::text))::json)))::text)::integer = x2.id)
-> Seq Scan on test (cost=0.00..643.45 rows=123000 width=36)
-> Hash (cost=22.30..22.30 rows=1230 width=36)
-> Seq Scan on test x2 (cost=0.00..22.30 rows=1230 width=36)
または
SELECT *
FROM test x1
LEFT JOIN ( SELECT *, json_array_elements((data->>'children')::JSON) child FROM test) x2
ON x1.id = x2.child::TEXT::INT
;
id | data | id | data | child
----+--------------------------------------+----+--------------------------------------+-------
2 | {"parent": 1, "children": [4, 5]} | 1 | {"parent": null, "children": [2, 3]} | 2
3 | {"parent": 1, "children": []} | 1 | {"parent": null, "children": [2, 3]} | 3
4 | {"parent": 2, "children": []} | 2 | {"parent": 1, "children": [4, 5]} | 4
5 | {"parent": 2, "children": []} | 2 | {"parent": 1, "children": [4, 5]} | 5
1 | {"parent": null, "children": [2, 3]} | | |
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Hash Right Join (cost=37.67..4217.38 rows=123000 width=104)
Hash Cond: ((((json_array_elements(((test.data ->> 'children'::text))::json)))::text)::integer = x1.id)
-> Seq Scan on test (cost=0.00..643.45 rows=123000 width=36)
-> Hash (cost=22.30..22.30 rows=1230 width=36)
-> Seq Scan on test x1 (cost=0.00..22.30 rows=1230 width=36)