PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Verifique se a chave existe em um JSON com PL/pgSQL?


Você já descobriu que pode testar a expressão user_info->>'username' para NULO. Mas sua função ainda é muito ineficiente . E ainda há ambiguidades .

Melhor solução no Postgres 9.3


É caro atualizar uma linha repetidamente para várias colunas. O Postgres escreve uma nova versão de linha para cada atualização. Use um único UPDATE se tudo for possível:
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;

Ligar:
SELECT sp_update_user(123, '{"firstname": "jon", "lastname": "doe"}')

  • Isso é substancialmente mais rápido para várias colunas, pois apenas um único UPDATE (no máximo) é executado. Se o WHERE cláusula não é avaliada como true , nenhuma atualização acontece e você obtém '{"success":false}' como resultado.

  • Se algumas vezes os valores na tabela já forem os valores para os quais estão sendo alterados, outra otimização é possível. Considere o último parágrafo desta resposta relacionada:

  • A variável / parâmetro user_id está faltando no seu original.

  • Ainda há um caso de canto ambiguidade . Se o elemento existir e estiver definido como JSON null , você também obtém um SQL NULL como resultado. Considerar:
    SELECT ('{"b": null}'::json->>'b') IS NULL AS b_is_null
         , ('{"c": 2}'::json->>'b')    IS NULL AS b_missing;
    

  • Não tenho certeza por que você usa o tipo de dados json como tipo de retorno, eu apenas mantive isso. Mas se a função não for atualizada, você não pode ter certeza por que obtém false . Pode não haver nenhuma linha com o id fornecido , os nomes das chaves 'firstname' e 'lastname' pode estar faltando - ou ser null ...



Solução superior no Postgres 9.4


Há um limpo e solução simples no Postgres 9.4 com jsonb com o ? operador "existência" - que pode até usar um índice para tabelas maiores (não relevante em sua função):
SELECT ('{"b": null}'::jsonb ? 'b') AS b_is_null
     , ('{"c": 2}'::jsonb ? 'b')    AS b_missing;

E o ?| e ?& variantes para verificar várias chaves de uma só vez.
Então 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;

Essas chamadas funcionam como esperado agora:
SELECT sp_update_user(123, '{"firstname": null, "lastname": "doe1"}'::jsonb);
SELECT sp_update_user(123, '{"firstname": "doris"}'::jsonb);