Ya descubrió que puede probar la expresión user_info->>'username'
para NULO. Pero su función sigue siendo muy ineficiente . Y todavía hay ambigüedades .
Mejor solución en Postgres 9.3
Es costoso actualizar una fila repetidamente para varias columnas. Postgres escribe una nueva versión de fila para cada actualización. Utilice un simple ACTUALIZAR
si es posible:
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;
Llamar:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')
-
Esto es sustancialmente más rápido para múltiples columnas, ya que solo un único
ACTUALIZAR
(como máximo) se ejecuta. Si elDONDE
la cláusula no se evalúa comotrue
, no ocurre ninguna actualización y obtienes'{"success":false}'
como resultado. -
Si a veces los valores en la tabla ya son los que se están cambiando, es posible otra optimización. Considere el último párrafo de esta respuesta relacionada:
-
La variable/parámetro
user_id
falta en su original. -
Todavía hay un caso de esquina ambigüedad . Si el elemento existe y está establecido en JSON
null
, también obtienes un SQLNULL
como resultado. Considere:SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null , ('{"c": 2}'::json->>'b') IS NULL AS b_missing;
-
No estoy seguro de por qué usa el tipo de datos
json
como tipo de retorno, solo mantuve eso. Pero si la función no se actualiza, no puede estar seguro de por qué obtienefalse
. Puede que no haya ninguna fila con elid
dado , los nombres clave'firstname'
y'apellido'
podría faltar o sernull
...
Solución superior en Postgres 9.4
Hay un limpio y solución simple en Postgres 9.4 con jsonb
con el código <>? operador de "existencia"
- que incluso puede usar un índice para tablas más grandes (no relevante en su función):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
, ('{"c": 2}'::jsonb ? 'b') AS b_missing;
Y el ?|
y ?&
variantes
para comprobar si hay varias claves a la vez.
Así que podemos implementar:
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;
Estas llamadas funcionan como se esperaba ahora:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);