Je hebt al ontdekt dat je de uitdrukking user_info->>'username'
. kunt testen voor NUL. Maar je functie is nog steeds zeer inefficiënt . En er zijn nog steeds dubbelzinnigheden .
Betere oplossing in Postgres 9.3
Het is duur om een rij herhaaldelijk bij te werken voor meerdere kolommen. Postgres schrijft voor elke update een nieuwe rijversie. Gebruik een enkele UPDATE
indien mogelijk:
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;
Bel:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Dit is aanzienlijk sneller voor meerdere kolommen, aangezien slechts een enkele
UPDATE
(hoogstens) wordt uitgevoerd. Als deWHERE
clausule evalueert niet naartrue
, er vindt helemaal geen update plaats en u krijgt'{"success":false}'
als resultaat. -
Als de waarden in de tabel soms al zijn waarnaar ze worden gewijzigd, is een andere optimalisatie mogelijk. Overweeg de laatste alinea van dit gerelateerde antwoord:
-
De variabele / parameter
user_id
ontbreekt in uw origineel. -
Er is nog steeds een hoekgeval ambiguïteit . Als het element bestaat en is ingesteld op JSON
null
, krijg je ook een SQLNULL
als resultaat. Overweeg:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
Ik weet niet zeker waarom u het gegevenstype
json
gebruikt als retourtype heb ik dat gewoon gehouden. Maar als de functie niet wordt bijgewerkt, weet u niet zeker waarom ufalse
krijgt . Er is mogelijk geen rij met de opgegevenid
, de sleutelnamen'firstname'
en'lastname'
kan ontbreken - ofnull
zijn ...
Superieure oplossing in Postgres 9.4
Er is een schone en eenvoudige oplossing in Postgres 9.4 met jsonb
met de ?
"bestaan"-operator
- die zelfs een index kan gebruiken voor grotere tabellen (niet relevant in uw functie):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
En de ?|
en ?&
varianten
om meerdere sleutels tegelijk te controleren.
Zodat we het volgende kunnen implementeren:
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;
Deze oproepen werken nu zoals verwacht:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);