Existe uma maneira simples no PL / pgSQL para verificar se uma consulta não retornou resultado?

16

Atualmente, estou experimentando um pouco com o PL / pgSQL e quero saber se existe uma maneira mais elegante de fazer algo assim:

select c.data into data from doc c where c.doc_id = id and c.group_cur > group_cur order by c.id desc limit 1;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        select c.data into data from doc c where c.doc_id = id and c.global_cur > global_cur order by c.id desc limit 1;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RETURN NULL;
icefex
fonte

Respostas:

21

Blocos de exceção são destinados a capturar erros, não verificar condições. Em outras palavras, se alguma condição puder ser tratada em tempo de compilação, ela não deve ser interceptada como erro, mas resolvida pela lógica comum do programa.

Na seção Detectando erros da documentação do PL / PgSQL você pode encontrar essa dica:

Dica: Um bloco contendo uma cláusula EXCEPTION é significativamente mais caro para entrar e sair do que um bloco sem uma. Portanto, não use EXCEPTION sem necessidade.

Em vez de usar exceções (incorretas) ou IF / THEN / ELSIF (melhor), você pode reescrever isso em uma consulta:

SELECT c.data into data
FROM  doc c
WHERE c.doc_id = id
  and (
    c.group_cur > group_cur
    or
    c.global_cur > global_cur
  )
ORDER BY
  -- this will make group always preferred over global
  case when c.group_cur > group_cur then 1 else 2 end ASC,
  -- and this is your normal ordering
  c.id DESC
limit 1;

Se você realmente deseja duas consultas, pode usar a variável FOUND especial para testar se a consulta anterior deu algum resultado:

select c.data into data
from doc c
where c.doc_id = id and c.group_cur > group_cur
order by c.id desc limit 1;
if not found then
    select c.data into data
    from doc c
    where c.doc_id = id and c.global_cur > global_cur
    order by c.id desc limit 1;
    if not found then return null; end if;
end if;

Links obrigatórios de RTFM a seguir :-)

Veja isto para descrição da FOUNDvariável, e isto para IF/ THENblocks.

filiprem
fonte
13

Você pode examinar uma variável especial FOUND de um tipo booleano. A partir da documentação:

FOUND começa falso dentro de cada chamada de função PL / pgSQL. É definido por cada um dos seguintes tipos de instruções:

Uma instrução SELECT INTO define FOUND true se uma linha for atribuída, false se nenhuma linha for retornada.

Uma instrução PERFORM define FOUND true se produzir (e descartar) uma ou mais linhas, false se nenhuma linha for produzida.

As instruções UPDATE, INSERT e DELETE configuram FOUND true se pelo menos uma linha for afetada, false se nenhuma linha for afetada.

Uma instrução FETCH define FOUND true se retornar uma linha, false se nenhuma linha for retornada.

Uma instrução MOVE define FOUND true se ela reposicionar o cursor com êxito, false caso contrário.

Uma instrução FOR ou FOREACH define FOUND true se iterar uma ou mais vezes, ou false. FOUND é definido desta maneira quando o loop sai; dentro da execução do loop, FOUND não é modificado pela instrução do loop, embora possa ser alterado pela execução de outras instruções no corpo do loop.

As instruções RETURN QUERY e RETURN QUERY EXECUTE configuram FOUND true se a consulta retornar pelo menos uma linha, false se nenhuma linha for retornada.

Outras instruções PL / pgSQL não alteram o estado de FOUND. Observe, em particular, que EXECUTE altera a saída de GET DIAGNOSTICS, mas não altera ENCONTRADO.

FOUND é uma variável local dentro de cada função PL / pgSQL; quaisquer alterações afetam apenas a função atual.

alexk
fonte
mas um select intoque não retorna dados ainda gera uma exceção, certo?
11123 Jack Douglas
3
geralmente não, ele gera exceções somente se cláusula RIGOROSA é especificado, como SELECT * INTO RIGOROSA meu recorde ...
Alexk
ah sim, meu mal - embora isso não signifique que o manipulador de exceções no exemplo do OP nunca será acionado? :-)
Jack Douglas
11
@JackDouglas: Geralmente, nenhum dado é motivo para uma exceção (exceto em casos especiais como o modificador STRICT acima). O OP tinha um equívoco lá.
Erwin Brandstetter