PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Vérifier si la clé existe dans un JSON avec PL/pgSQL ?

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 le WHERE 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 SQL NULL à 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 obtenez false . 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 être null ...


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);