sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Verificar si la clave existe en un JSON con PL/pgSQL?

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 el DONDE la cláusula no se evalúa como true , 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 SQL NULL 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é obtiene false . Puede que no haya ninguna fila con el id dado , los nombres clave 'firstname' y 'apellido' podría faltar o ser null ...


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);