Vous avez déjà trouvé que vous pouvez tester l'expression user_info->>'username'
pour NUL. Mais votre fonction est toujours très inefficace . Et il y a encore des ambiguïtés .
Meilleure solution dans Postgres 9.3
Il est coûteux de mettre à jour une ligne à plusieurs reprises pour plusieurs colonnes. Postgres écrit une nouvelle version de ligne pour chaque mise à jour. Utilisez un simple UPDATE
si possible :
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;
Appel :
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
C'est nettement plus rapide pour plusieurs colonnes, car un seul
UPDATE
(au plus) est exécuté. Si leWHERE
la clause n'est pas évaluée àtrue
, aucune mise à jour ne se produit et vous obtenez'{"success":false}'
en conséquence. -
Si parfois les valeurs du tableau correspondent déjà à ce qu'elles sont modifiées, une autre optimisation est possible. Considérez le dernier paragraphe de cette réponse connexe :
-
La variable / paramètre
user_id
manque dans votre original. -
Il y a toujours une ambiguïté de cas d'angle . Si l'élément existe et est défini sur JSON
null
, vous obtenez également un SQLNULL
à la suite. Considérez :SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
Vous ne savez pas pourquoi vous utilisez le type de données
json
comme type de retour, je l'ai juste gardé. Mais si la fonction ne se met pas à jour, vous ne savez pas pourquoi vous obtenezfalse
. Il se peut qu'il n'y ait pas de ligne avec l'id
donné , les noms de clé'firstname'
et'lastname'
peut être manquant - ou êtrenull
...
Solution supérieure dans Postgres 9.4
Il y a un propre et solution simple dans Postgres 9.4 avec jsonb
avec le ?
opérateur "existence"
- qui peut même utiliser un index pour les tables plus grandes (non pertinent dans votre fonction) :
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
Et le ?|
et ?&
variantes
pour vérifier plusieurs clés à la fois.
Nous pouvons donc implémenter :
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;
Ces appels fonctionnent désormais comme prévu :
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);