Bạn đã thấy rằng bạn có thể kiểm tra biểu thức user_info->>'username'
cho NULL. Nhưng chức năng của bạn vẫn rất kém hiệu quả . Và vẫn còn sự mơ hồ .
Giải pháp tốt hơn trong Postgres 9.3
Sẽ rất tốn kém khi cập nhật liên tục một hàng cho nhiều cột. Postgres viết một phiên bản hàng mới cho mỗi bản cập nhật. Sử dụng một đơn UPDATE
nếu có thể:
CREATE OR REPLACE FUNCTION sp_update_user(_user_id int, _user_info json)
RETURNS json AS
$func$
BEGIN
UPDATE users u
SET firstname = COALESCE(_user_info->>'firstname', u.firstname)
, lastname = COALESCE(_user_info->>'lastname' , u.lastname)
WHERE id = sp_update_user._user_id
AND ((_user_info->>'firstname') IS NOT NULL OR
(_user_info->>'lastname') IS NOT NULL);
IF FOUND THEN
RETURN '{"success":true}'::json;
ELSE
RETURN '{"success":false}'::json;
END IF;
END
$func$ LANGUAGE plpgsql;
Gọi:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Điều này nhanh hơn đáng kể đối với nhiều cột, vì chỉ một
UPDATE
duy nhất (nhiều nhất) được thực thi. NếuWHERE
mệnh đề không đánh giá thànhtrue
, không có bản cập nhật nào xảy ra và bạn nhận được'{"success":false}'
kết quả là. -
Nếu đôi khi các giá trị trong bảng đã được thay đổi thành, thì có thể thực hiện một tối ưu hóa khác. Hãy xem xét đoạn cuối cùng của câu trả lời liên quan này:
-
Biến / tham số
user_id
bị thiếu trong bản gốc của bạn. -
Vẫn còn một trường hợp góc không rõ ràng . Nếu phần tử tồn tại và được đặt thành JSON
null
, bạn cũng nhận được một SQLNULL
kết quả là. Cân nhắc:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
Không chắc tại sao bạn sử dụng kiểu dữ liệu
json
là loại trả lại, tôi chỉ giữ điều đó. Nhưng nếu chức năng không cập nhật, bạn không thể chắc chắn tại sao bạn nhận đượcfalse
. Có thể không có hàng vớiid
đã cho , các tên khóa'firstname'
và'lastname'
có thể bị thiếu - hoặcnull
...
Giải pháp vượt trội trong Postgres 9.4
Có một sạch sẽ và giải pháp đơn giản trong Postgres 9.4 với jsonb
với ?
toán tử "sự tồn tại"
- thậm chí có thể sử dụng một chỉ mục cho các bảng lớn hơn (không liên quan trong chức năng của bạn):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
Và ?|
và ?&
các biến thể
để kiểm tra nhiều khóa cùng một lúc.
Vì vậy, chúng tôi có thể triển khai:
CREATE OR REPLACE FUNCTION sp_update_user(_user_id int, _user_info jsonb)
RETURNS jsonb AS
$func$
BEGIN
UPDATE users u
SET firstname = CASE WHEN _user_info ? 'firstname' THEN _user_info->>'firstname' ELSE u.firstname END
, lastname = CASE WHEN _user_info ? 'lastname' THEN _user_info->>'lastname' ELSE u.lastname END
WHERE id = sp_update_user._user_id
AND _user_info ?| '{firstname,lastname}';
IF FOUND THEN
RETURN '{"success":true}'::jsonb;
ELSE
RETURN '{"success":false}'::jsonb;
END IF;
END
$func$ LANGUAGE plpgsql;
Các cuộc gọi này hiện hoạt động như mong đợi:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);