Hai già scoperto che puoi testare l'espressione user_info->>'username'
per NULLA. Ma la tua funzione è ancora molto inefficiente . E ci sono ancora ambiguità .
Soluzione migliore in Postgres 9.3
È costoso aggiornare ripetutamente una riga per più colonne. Postgres scrive una nuova versione di riga per ogni aggiornamento. Usa un singolo UPDATE
se possibile:
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;
Chiama:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Questo è sostanzialmente più veloce per più colonne, poiché solo un singolo
UPDATE
(al massimo) viene eseguito. Se ilWHERE
la clausola non restituiscetrue
, non viene eseguito alcun aggiornamento e ottieni'{"success":false}'
come risultato. -
Se a volte i valori nella tabella sono già quelli in cui vengono modificati, è possibile un'altra ottimizzazione. Considera l'ultimo paragrafo di questa risposta correlata:
-
La variabile/parametro
user_id
manca nell'originale. -
C'è ancora un caso d'angolo ambiguità . Se l'elemento esiste ed è impostato su JSON
null
, ottieni anche un SQLNULL
come risultato. Considera:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
Non sono sicuro del motivo per cui utilizzi il tipo di dati
json
come tipo di ritorno, l'ho appena tenuto. Ma se la funzione non si aggiorna, non puoi essere sicuro del motivo per cui ricevifalse
. Potrebbe non esserci alcuna riga con l'id
specificato , la chiave nomina'firstname'
e'lastname'
potrebbe essere mancante o esserenull
...
Soluzione superiore in Postgres 9.4
C'è un pulito e una soluzione semplice in Postgres 9.4 con jsonb
con ?
operatore di "esistenza"
- che può anche utilizzare un indice per tabelle più grandi (non rilevante nella tua funzione):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
E il ?|
e ?&
varianti
per verificare la presenza di più chiavi contemporaneamente.
Quindi possiamo implementare:
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;
Queste chiamate funzionano come previsto ora:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);