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. OmWHERE
klausul 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_id
saknas i originalet. -
Det finns fortfarande ett hörnfall tvetydighet . Om elementet finns och är satt till JSON
null
, får du också en SQLNULL
som 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
json
som 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);