Du har allerede fundet ud af, at du kan teste udtrykket user_info->>'brugernavn'
for NULL. Men din funktion er stadig meget ineffektiv . Og der er stadig uklarheder .
Bedre løsning i Postgres 9.3
Det er dyrt at opdatere en række gentagne gange for flere kolonner. Postgres skriver en ny rækkeversion for hver opdatering. Brug en enkelt OPDATERING
hvis det overhovedet er muligt:
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 til:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Dette er væsentligt hurtigere for flere kolonner, da kun en enkelt
OPDATERING
(højst) udføres. HvisWHERE
klausul evalueres ikke tiltrue
, der sker ingen opdatering overhovedet, og du får'{"success":false}'
som resultat. -
Hvis værdierne i tabellen nogle gange allerede er, hvad de ændres til, er en anden optimering mulig. Overvej det sidste afsnit af dette relaterede svar:
-
Variablen / parameteren
user_id
mangler i din original. -
Der er stadig en tvetydighed i hjørnet . Hvis elementet findes og er sat til JSON
null
, får du også en SQLNULL
som resultat. Overvej:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
Ikke sikker på, hvorfor du bruger datatypen
json
som returtype beholdt jeg det bare. Men hvis funktionen ikke opdateres, kan du ikke være sikker på, hvorfor du fårfalse
. Der er muligvis ingen række med det angivneid
, nøglenavnene'fornavn'
og'efternavn'
kunne mangle - eller værenull
...
Super løsning i Postgres 9.4
Der er en ren og enkel løsning i Postgres 9.4 med jsonb
med <-koden>? "eksistens" operatør
- som endda kan bruge et indeks til større tabeller (ikke relevant i din funktion):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
Og ?|
og ?&
varianter
at tjekke for flere nøgler på én gang.
Så vi kan implementere:
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;
Disse opkald fungerer som forventet nu:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);