もしJSONBがデータを入れる魔法の宝箱だとしたら、||とjsonb_concat()はその宝箱を合体させたり中身を変えたりするツールだよ。実際には、複数のJSONオブジェクトを合体させたり、片方のデータをもう片方に追加したり、配列を一つのリストにまとめたりしたい場面が出てくる。
例えば、こんな2つのJSONBオブジェクトがあるとする:
{"name": "Alice", "age": 25}
と
{"city": "Wonderland", "hobbies": ["reading", "chess"]}
こうしたい場合:
{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}
または、2つのJSONB配列を結合したいとき:
[1, 2, 3]
と
[4, 5, 6]
結果はこうなる:
[1, 2, 3, 4, 5, 6]
これ全部、||やjsonb_concat()でできるよ。じゃあ、詳しく見ていこう。
JSONB結合用||演算子
||演算子は、PostgreSQLで2つのJSONBオブジェクトや配列を結合できる。シンプルで速くて使いやすい。基本ルールはこんな感じ:
- 2つのJSONBオブジェクトを結合すると、両方のキーと値が入ったオブジェクトになる。
- キーがかぶった場合は、右側の値が左側の値を上書きする。
- JSONB配列同士なら、左と右の配列の要素が一つの配列にまとめられる。
例1:2つのJSONBオブジェクトの結合
SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"city": "Wonderland", "hobbies": ["reading", "chess"]}'::jsonb AS merged_object;
結果:
{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}
例2:キーがかぶったときの値の更新
SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"age": 30, "city": "Wonderland"}'::jsonb AS updated_object;
結果:
{"name": "Alice", "age": 30, "city": "Wonderland"}
ここで"age"キーの値は、右側のオブジェクトの値で上書きされてるのに注目。
例3:配列の結合
SELECT '[1, 2, 3]'::jsonb || '[4, 5, 6]'::jsonb AS merged_array;
結果:
[1, 2, 3, 4, 5, 6]
JSONB結合用jsonb_concat()関数
jsonb_concat()関数は||演算子とほぼ同じ動きをするけど、関数として使えるから、関数内やトリガー、動的クエリで使いたいときに便利。2つのJSONB型引数を受け取って、結合した結果を返すよ。
例:jsonb_concat()の使い方
SELECT jsonb_concat('{"a": 1, "b": 2}'::jsonb, '{"b": 3, "c": 4}'::jsonb) AS combined;
結果:
{"a": 1, "b": 3, "c": 4}
オブジェクトと配列の結合:ポイントと注意点
キーがかぶるオブジェクトを結合するときは、右側のオブジェクトの値が優先されるってことを覚えておこう。
例えば:
SELECT '{"key1": "value1"}'::jsonb || '{"key1": "value2"}'::jsonb AS result;
結果:
{"key1": "value2"}
値の上書きを避けたいなら、別のキーに保存するか、配列など他の方法を使おう。
あと、配列は常に右側の配列の要素が左側に追加される。例えば:
SELECT '["a", "b"]'::jsonb || '["c", "d"]'::jsonb AS result;
結果:
["a", "b", "c", "d"]
配列の中にオブジェクトがあっても、順番はそのまま:
SELECT '[{"id": 1}, {"id": 2}]'::jsonb || '[{"id": 3}]'::jsonb AS result;
結果:
[{"id": 1}, {"id": 2}, {"id": 3}]
実践例
ユーザープロフィールの更新。 例えば、usersテーブルがあって、プロフィールがJSONBで保存されてるとする:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
profile JSONB
);
INSERT INTO users (profile) VALUES ('{"name": "Alice", "age": 25}');
今度は居住都市を追加したい:
UPDATE users
SET profile = profile || '{"city": "Wonderland"}'
WHERE id = 1;
クエリ結果:
{"name": "Alice", "age": 25, "city": "Wonderland"}
注文データの結合。 例えば、ordersテーブルがあるとする:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
details JSONB
);
INSERT INTO orders (details) VALUES ('{"items": [{"product": "laptop", "quantity": 1}]}');
今度は注文にもう一つ商品を追加したい:
UPDATE orders
SET details = jsonb_set(
details,
'{items}',
details->'items' || '[{"product": "mouse", "quantity": 2}]'::jsonb
)
WHERE id = 1;
クエリ結果:
{"items": [{"product": "laptop", "quantity": 1}, {"product": "mouse", "quantity": 2}]}
||とjsonb_concat()の違い
機能的には||演算子もjsonb_concat()関数も同じ。シンプルなクエリなら||の方が書きやすい。関数として明示的に呼び出したい場合やトリガー内で使いたい場合はjsonb_concat()が便利。
よくあるミスとその防ぎ方
ミス例:型が違うものを結合しようとする。
SELECT '{"key": "value"}'::jsonb || '["value"]'::jsonb;
結果:
ERROR: jsonbオブジェクトと配列は結合できません
左がオブジェクト、右が配列だとPostgreSQLはそのまま結合できない。同じ型同士(オブジェクト同士、配列同士)じゃないとダメ。
見落としがち:JSONBでインデックスを作ってない
JSONBフィールドの値でよく検索するのにインデックスがないと、クエリがめっちゃ遅くなる。これは典型的なエラーじゃないけど、パフォーマンスに大きく影響する。GINインデックスを使うのを忘れずに:
CREATE INDEX idx_profile_data ON employees USING gin(profile);
GO TO FULL VERSION