Вече открихте, че можете да тествате израза user_info->>'username'
за NULL. Но вашата функция все още е много неефективна . И все още има неяснотита .
По-добро решение в Postgres 9.3
Скъпо е да се актуализира ред многократно за множество колони. Postgres пише нова версия на ред за всяка актуализация. Използвайте единичен UPDATE
ако изобщо е възможно:
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;
Обаждане:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Това е значително по-бързо за множество колони, тъй като само една
UPDATE
(най-много) се изпълнява. АкоWHERE
клаузата не дава оценка наtrue
, изобщо не се извършва актуализация и получавате'{"success":false}'
като резултат. -
Ако понякога стойностите в таблицата вече са това, на което се променят, е възможна друга оптимизация. Помислете за последния абзац на този свързан отговор:
-
Променливата/параметър
user_id
липсва във вашия оригинал. -
Все още има ъглов случайнеяснотата . Ако елементът съществува и е зададен на JSON
null
, вие също получавате SQLNULL
като резултат. Помислете за следното:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
Не съм сигурен защо използвате тип данни
json
като върнат тип, просто го запазих. Но ако функцията не се актуализира, не можете да сте сигурни защо получаватеfalse
. Възможно е да няма ред с даденияid
, имената на ключовете'firstname'
и'lastname'
може да липсва - или да еnull
...
Превъзходно решение в Postgres 9.4
Има чистия и просто решение в Postgres 9.4 с jsonb
с ?
оператор "съществуване"
- което дори може да използва индекс за по-големи таблици (не е подходящо за вашата функция):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
И ?|
и ?&
варианти
за проверка за множество ключове наведнъж.
Така че можем да приложим:
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;
Тези обаждания работят според очакванията сега:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);