CodeGym /课程 /SQL SELF /用 || 操作符和 jsonb_concat() 函数合并和修改 JSON 对象

用 || 操作符和 jsonb_concat() 函数合并和修改 JSON 对象

SQL SELF
第 34 级, 课程 0
可用

如果说 JSONB 是个神奇的宝箱,能装各种数据,那 || 和 jsonb_concat() 就是让你把宝箱合并或者改造内容的工具。在实际开发中,你可能会需要把多个 JSON 对象合成一个、把一个对象的数据加到另一个里,或者把数组拼成一个大列表。

比如,假设你有两个 JSONB 对象:

{"name": "Alice", "age": 25}

还有

{"city": "Wonderland", "hobbies": ["reading", "chess"]}

你想得到:

{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}

或者合并两个 JSONB 数组:

[1, 2, 3]

和

[4, 5, 6]

让结果变成:

[1, 2, 3, 4, 5, 6]

这些都可以用 || 或 jsonb_concat() 搞定。下面咱们详细聊聊怎么用。

用 || 操作符合并 JSONB

|| 操作符可以在 PostgreSQL 里合并两个 JSONB 对象或者数组。它用起来简单、快、直观。主要规则如下:

  • 如果合并的是两个 JSONB 对象,结果对象会包含两个对象的所有 key 和 value。
  • 如果 key 重复了,右边的 value 会覆盖左边的 value。
  • 如果合并的是 JSONB 数组,左边和右边数组的元素会拼成一个新数组。

例子 1:合并两个 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:key 重复时更新 value

SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"age": 30, "city": "Wonderland"}'::jsonb AS updated_object;

结果:

{"name": "Alice", "age": 30, "city": "Wonderland"}

注意,key "age" 的值被右边对象的值覆盖了。

例子 3:合并数组

SELECT '[1, 2, 3]'::jsonb || '[4, 5, 6]'::jsonb AS merged_array;

结果:

[1, 2, 3, 4, 5, 6]

jsonb_concat() 函数合并 JSONB

jsonb_concat() 跟 || 操作符差不多,但如果你要在函数、触发器或者动态 SQL 里用它会更灵活。它接收两个 JSONB 参数,返回合并后的结果。

例子:用 jsonb_concat()

SELECT jsonb_concat('{"a": 1, "b": 2}'::jsonb, '{"b": 3, "c": 4}'::jsonb) AS combined;

结果:

{"a": 1, "b": 3, "c": 4}

合并对象和数组:细节和注意点

合并有重复 key 的对象时要记住,右边对象的值优先。

比如:

SELECT '{"key1": "value1"}'::jsonb || '{"key1": "value2"}'::jsonb AS result;

结果:

{"key1": "value2"}

如果你不想让值被覆盖,建议把数据放不同的 key,或者用数组等别的方式存。

而数组总是直接把右边数组的元素加到左边数组后面。比如:

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}]

实用例子

更新用户 profile。 假设有个 users 表,profile 字段用 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() 功能上是一样的。写简单 SQL 时用 || 更简洁。如果你要在程序或触发器里用,jsonb_concat() 会更方便。

常见错误和避免方法

错误:试图合并不兼容的类型。

SELECT '{"key": "value"}'::jsonb || '["value"]'::jsonb;

结果:

ERROR:  cannot concatenate jsonb objects and arrays

这里左边是对象,右边是数组 —— PostgreSQL 没法直接合并。要让操作成功,两边类型必须一样:要么都是对象,要么都是数组。

疏忽:用 JSONB 没加索引

如果你经常按 JSONB 字段里的值查数据,但没建索引,查询会很慢。这不算 bug,但性能会很差。记得用 GIN 索引:

CREATE INDEX idx_profile_data ON employees USING gin(profile);
2
任务
SQL SELF, 第 34 级, 课程 0
已锁定
更新 JSONB 对象并替换键
更新 JSONB 对象并替换键
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION