Вече открихте, че можете да тествате израза 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);