Du har redan upptäckt att du kan testa uttrycket user_info->>'användarnamn' för NULL. Men din funktion är fortfarande mycket ineffektiv . Och det finns fortfarande tvetydigheter .
Bättre lösning i Postgres 9.3
Det är dyrt att uppdatera en rad upprepade gånger för flera kolumner. Postgres skriver en ny radversion för varje uppdatering. Använd en singel UPPDATERA om möjligt:
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;
Ring:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Detta är betydligt snabbare för flera kolumner, eftersom bara en enda
UPPDATERING(högst) exekveras. OmWHEREklausul utvärderas inte tilltrue, ingen uppdatering sker alls och du får'{"success":false}'som ett resultat. -
Om ibland värdena i tabellen redan är vad de ändras till, är en annan optimering möjlig. Tänk på det sista stycket i detta relaterade svar:
-
Variabeln / parametern
user_idsaknas i originalet. -
Det finns fortfarande ett hörnfall tvetydighet . Om elementet finns och är satt till JSON
null, får du också en SQLNULLsom resultat. Tänk på:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing; -
Osäker på varför du använder datatypen
jsonsom returtyp behöll jag bara det. Men om funktionen inte uppdateras kan du inte vara säker på varför du fårfalse. Det kanske inte finns någon rad med det angivnaid, nyckelnamnen'firstname'och'efternamn'kan saknas - eller varanull...
Överlägsen lösning i Postgres 9.4
Det finns en ren och enkel lösning i Postgres 9.4 med jsonb med <-koden>? "existens"-operator
- som till och med kan använda ett index för större tabeller (inte relevant i din funktion):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
Och ?| och ?& varianter
för att söka efter flera nycklar samtidigt.
Så vi kan implementera:
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; Dessa samtal fungerar som förväntat nu:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);