sql >> Databasteknik >  >> RDS >> PostgreSQL

Kontrollera om nyckel finns i en JSON med PL/pgSQL?

Du har redan upptäckt att du kan testa uttrycket user_info->>'användarnamn' för NULL. Men din funktion är fortfarande mycket ineffektiv . Och det finns fortfarande tvetydigheter .

Bättre lösning i Postgres 9.3

Det är dyrt att uppdatera en rad upprepade gånger för flera kolumner. Postgres skriver en ny radversion för varje uppdatering. Använd en singel UPPDATERA om möjligt:

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:

SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
  • Detta är betydligt snabbare för flera kolumner, eftersom bara en enda UPPDATERING (högst) exekveras. Om WHERE klausul utvärderas inte till true , ingen uppdatering sker alls och du får '{"success":false}' som ett resultat.

  • Om ibland värdena i tabellen redan är vad de ändras till, är en annan optimering möjlig. Tänk på det sista stycket i detta relaterade svar:

  • Variabeln / parametern user_id saknas i originalet.

  • Det finns fortfarande ett hörnfall tvetydighet . Om elementet finns och är satt till JSON null , får du också en SQL NULL som resultat. Tänk på:

    SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null
         , ('{"c": 2}'::json->>'b')    IS NULL AS b_missing;
    
  • Osäker på varför du använder datatypen json som returtyp behöll jag bara det. Men om funktionen inte uppdateras kan du inte vara säker på varför du får false . Det kanske inte finns någon rad med det angivna id , nyckelnamnen 'firstname' och 'efternamn' kan saknas - eller vara null ...


Överlägsen lösning i Postgres 9.4

Det finns en ren och enkel lösning i Postgres 9.4 med jsonb med <-koden>? "existens"-operator - som till och med kan använda ett index för större tabeller (inte relevant i din funktion):

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

Och ?| och ?& varianter för att söka efter flera nycklar samtidigt.
Så vi kan implementera:

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;

Dessa samtal fungerar som förväntat nu:

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


  1. Vad är en genererad kolumn?

  2. Hur man kommer åt och uppdaterar Sqlite db-fil lagrad lokalt [tillgångsmapp] i projektfil med cordova

  3. Worklight 6.2 migreringsverktygsfel

  4. Söker effektivt efter en enorm tidsserietabell för en rad var 15:e minut