sql >> Database >  >> RDS >> PostgreSQL

Controleer of de sleutel bestaat in een JSON met PL/pgSQL?

Je hebt al ontdekt dat je de uitdrukking user_info->>'username' . kunt testen voor NUL. Maar je functie is nog steeds zeer inefficiënt . En er zijn nog steeds dubbelzinnigheden .

Betere oplossing in Postgres 9.3

Het is duur om een ​​rij herhaaldelijk bij te werken voor meerdere kolommen. Postgres schrijft voor elke update een nieuwe rijversie. Gebruik een enkele UPDATE indien mogelijk:

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;

Bel:

SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
  • Dit is aanzienlijk sneller voor meerdere kolommen, aangezien slechts een enkele UPDATE (hoogstens) wordt uitgevoerd. Als de WHERE clausule evalueert niet naar true , er vindt helemaal geen update plaats en u krijgt '{"success":false}' als resultaat.

  • Als de waarden in de tabel soms al zijn waarnaar ze worden gewijzigd, is een andere optimalisatie mogelijk. Overweeg de laatste alinea van dit gerelateerde antwoord:

  • De variabele / parameter user_id ontbreekt in uw origineel.

  • Er is nog steeds een hoekgeval ambiguïteit . Als het element bestaat en is ingesteld op JSON null , krijg je ook een SQL NULL als resultaat. Overweeg:

    SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null
         , ('{"c": 2}'::json->>'b')    IS NULL AS b_missing;
    
  • Ik weet niet zeker waarom u het gegevenstype json gebruikt als retourtype heb ik dat gewoon gehouden. Maar als de functie niet wordt bijgewerkt, weet u niet zeker waarom u false krijgt . Er is mogelijk geen rij met de opgegeven id , de sleutelnamen 'firstname' en 'lastname' kan ontbreken - of null zijn ...


Superieure oplossing in Postgres 9.4

Er is een schone en eenvoudige oplossing in Postgres 9.4 met jsonb met de ? "bestaan"-operator - die zelfs een index kan gebruiken voor grotere tabellen (niet relevant in uw functie):

SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
     , ('{"c": 2}'::jsonb ? 'b')    AS b_missing;

En de ?| en ?& varianten om meerdere sleutels tegelijk te controleren.
Zodat we het volgende kunnen implementeren:

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;

Deze oproepen werken nu zoals verwacht:

SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);


  1. Barman gebruiken voor PostgreSQL Disaster Recovery

  2. voer de opgeslagen procedure uit in het script van google apps

  3. Verkrijg de naam van de DB-eigenaar in PostgreSql

  4. Hoe PostgreSQL wal-bestandsgegevens lezen? Is er een opdracht om PostgreSQL binair naar een leesbaar tekstformaat te converteren?