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 derWHERE
-Klausel wird nicht alstrue
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 SQLNULL
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 Siefalse
erhalten . Möglicherweise gibt es keine Zeile mit der angegebenenid
, die Schlüsselnamen'firstname'
und'Nachname'
könnte fehlen - odernull
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);