PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Überprüfen Sie, ob der Schlüssel in einem JSON mit PL/pgSQL vorhanden ist?

Sie haben bereits festgestellt, dass Sie den Ausdruck user_info->>'username' testen können für NULL. Aber Ihre Funktion ist immer noch sehr ineffizient . Und es gibt immer noch Unklarheiten .

Bessere Lösung in Postgres 9.3

Es ist teuer, eine Zeile wiederholt für mehrere Spalten zu aktualisieren. Postgres schreibt für jedes Update eine neue Zeilenversion. Verwenden Sie eine einzelne AKTUALISIEREN wenn möglich:

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;

Aufruf:

SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
  • Bei mehreren Spalten geht das wesentlich schneller, da nur ein einziges UPDATE (höchstens) ausgeführt wird. Wenn der WHERE -Klausel wird nicht als true ausgewertet , erfolgt überhaupt kein Update und Sie erhalten '{"success":false}' als Ergebnis.

  • Wenn die Werte in der Tabelle manchmal schon so sind, wie sie geändert werden, ist eine weitere Optimierung möglich. Betrachten Sie den letzten Absatz dieser verwandten Antwort:

  • Die Variable / Parameter user_id fehlt in Ihrem Original.

  • Es gibt immer noch einen Eckfall Mehrdeutigkeit . Wenn das Element existiert und auf JSON null gesetzt ist erhalten Sie auch ein SQL NULL als Ergebnis. Bedenken Sie:

    SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null
         , ('{"c": 2}'::json->>'b')    IS NULL AS b_missing;
    
  • Nicht sicher, warum Sie den Datentyp json verwenden als Rückgabetyp habe ich das einfach beibehalten. Aber wenn die Funktion nicht aktualisiert wird, können Sie nicht sicher sein, warum Sie false erhalten . Möglicherweise gibt es keine Zeile mit der angegebenen id , die Schlüsselnamen 'firstname' und 'Nachname' könnte fehlen - oder null sein ...


Überlegene Lösung in Postgres 9.4

Es gibt eine saubere und einfache Lösung in Postgres 9.4 mit jsonb mit dem Code <>? "Existenz"-Operator - die sogar einen Index für größere Tabellen verwenden kann (in Ihrer Funktion nicht relevant):

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

Und der ?| und ?& Varianten um nach mehreren Schlüsseln gleichzeitig zu suchen.
So können wir Folgendes implementieren:

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;

Diese Aufrufe funktionieren jetzt wie erwartet:

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