Nếu JSONB giống như một cái rương thần kỳ để chứa dữ liệu, thì || và jsonb_concat() là công cụ giúp tụi mình ghép các rương lại với nhau hoặc thay đổi bên trong nó. Ngoài đời, đôi khi bạn sẽ cần kết hợp nhiều đối tượng JSON lại, thêm dữ liệu từ cái này sang cái khác hoặc gộp các mảng thành một danh sách duy nhất.
Ví dụ, giả sử bạn có hai đối tượng JSONB:
{"name": "Alice", "age": 25}
và
{"city": "Wonderland", "hobbies": ["reading", "chess"]}
Bạn muốn nhận được:
{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}
Hoặc gộp hai mảng JSONB:
[1, 2, 3]
và
[4, 5, 6]
để kết quả là:
[1, 2, 3, 4, 5, 6]
Tất cả chuyện này đều làm được bằng || hoặc jsonb_concat(). Cùng tìm hiểu chi tiết nhé.
Toán tử || để kết hợp JSONB
Toán tử || cho phép bạn kết hợp hai đối tượng hoặc mảng JSONB trong PostgreSQL. Nó đơn giản, nhanh và dễ dùng. Đây là các quy tắc chính khi dùng nó:
- Nếu kết hợp hai đối tượng JSONB, đối tượng kết quả sẽ chứa tất cả các key và value của cả hai.
- Nếu có key trùng nhau, value bên phải sẽ thay thế value bên trái.
- Nếu kết hợp hai mảng JSONB, các phần tử của mảng bên trái và bên phải sẽ được ghép lại thành một mảng duy nhất.
Ví dụ 1: Kết hợp hai đối tượng JSONB
SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"city": "Wonderland", "hobbies": ["reading", "chess"]}'::jsonb AS merged_object;
Kết quả:
{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}
Ví dụ 2: Cập nhật value khi key trùng nhau
SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"age": 30, "city": "Wonderland"}'::jsonb AS updated_object;
Kết quả:
{"name": "Alice", "age": 30, "city": "Wonderland"}
Lưu ý là value của key "age" bên phải đã thay thế value bên trái.
Ví dụ 3: Kết hợp mảng
SELECT '[1, 2, 3]'::jsonb || '[4, 5, 6]'::jsonb AS merged_array;
Kết quả:
[1, 2, 3, 4, 5, 6]
Hàm jsonb_concat() để kết hợp JSONB
Hàm jsonb_concat() hoạt động giống như toán tử ||, nhưng linh hoạt hơn nếu bạn cần dùng nó trong function, trigger hoặc truy vấn động. Nó nhận hai tham số kiểu JSONB và trả về kết quả đã kết hợp.
Ví dụ: dùng jsonb_concat()
SELECT jsonb_concat('{"a": 1, "b": 2}'::jsonb, '{"b": 3, "c": 4}'::jsonb) AS combined;
Kết quả:
{"a": 1, "b": 3, "c": 4}
Kết hợp đối tượng và mảng: các lưu ý và đặc điểm
Khi kết hợp các đối tượng có key trùng nhau, hãy nhớ rằng value bên phải sẽ được ưu tiên.
Ví dụ:
SELECT '{"key1": "value1"}'::jsonb || '{"key1": "value2"}'::jsonb AS result;
Kết quả:
{"key1": "value2"}
Nếu bạn không muốn value bị ghi đè, nên lưu dữ liệu ở các key khác nhau hoặc dùng cách khác (ví dụ như mảng).
Còn mảng thì luôn được ghép bằng cách thêm phần tử của mảng bên phải vào mảng bên trái. Ví dụ:
SELECT '["a", "b"]'::jsonb || '["c", "d"]'::jsonb AS result;
Kết quả:
["a", "b", "c", "d"]
Nếu trong mảng có đối tượng, thứ tự đối tượng vẫn giữ nguyên:
SELECT '[{"id": 1}, {"id": 2}]'::jsonb || '[{"id": 3}]'::jsonb AS result;
Kết quả:
[{"id": 1}, {"id": 2}, {"id": 3}]
Ví dụ thực tế
Cập nhật profile người dùng. Giả sử bạn có bảng users, nơi profile được lưu dưới dạng JSONB:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
profile JSONB
);
INSERT INTO users (profile) VALUES ('{"name": "Alice", "age": 25}');
Bây giờ bạn muốn thêm thành phố nơi ở:
UPDATE users
SET profile = profile || '{"city": "Wonderland"}'
WHERE id = 1;
Kết quả truy vấn:
{"name": "Alice", "age": 25, "city": "Wonderland"}
Kết hợp dữ liệu đơn hàng. Giả sử bạn có bảng orders:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
details JSONB
);
INSERT INTO orders (details) VALUES ('{"items": [{"product": "laptop", "quantity": 1}]}');
Bây giờ bạn thêm một sản phẩm nữa vào đơn hàng:
UPDATE orders
SET details = jsonb_set(
details,
'{items}',
details->'items' || '[{"product": "mouse", "quantity": 2}]'::jsonb
)
WHERE id = 1;
Kết quả truy vấn:
{"items": [{"product": "laptop", "quantity": 1}, {"product": "mouse", "quantity": 2}]}
Khác biệt giữa || và jsonb_concat()
Về chức năng, toán tử || và hàm jsonb_concat() giống nhau. Dùng || khi bạn viết truy vấn đơn giản vì nó ngắn gọn hơn. Hàm jsonb_concat() tiện khi bạn cần gọi nó trong chương trình hoặc trigger.
Lỗi thường gặp và cách tránh
Lỗi: cố gắng kết hợp các kiểu không tương thích.
SELECT '{"key": "value"}'::jsonb || '["value"]'::jsonb;
Kết quả:
ERROR: cannot concatenate jsonb objects and arrays
Ở đây bên trái là đối tượng, bên phải là mảng — PostgreSQL không thể ghép hai kiểu này với nhau. Để thao tác thành công, cả hai toán hạng phải cùng kiểu: hoặc đều là đối tượng, hoặc đều là mảng.
Bỏ sót: không có index khi làm việc với JSONB
Nếu bạn thường xuyên lọc dữ liệu theo value trong trường JSONB mà không có index — truy vấn sẽ rất chậm. Đây không phải lỗi kiểu truyền thống, nhưng hậu quả về hiệu năng thì rất rõ. Đừng quên dùng GIN index nhé:
CREATE INDEX idx_profile_data ON employees USING gin(profile);
GO TO FULL VERSION