Um belo dia eu me deparei com um SQL no mínimo estranho, por assim dizer. Não que só devam existir SQLs perfeitos, ou que nunca tenha visto SQLs estranhos, mas o autor do SQL propagave esta técnica como sendo adequada e que cobria todas as possibilidades do problema e portante era “a melhor”. Com não concordei no fato de considerar ela “a melhor” sugeri outras formas de obter o mesmo resultado.
O problema
Imagine que você tem um cadastro de sua agenda telefônica modelado em
um banco em que apenas o nome é obrigatório e o sobrenome aceita
NULL
. Como vimos no post anterior você usa NULL
para expressar
valores deconhecidos ou que não se aplicam a situação em
questão. Neste caso o sobrenome pode ser desconhecido mesmo.
A questão era mostrar uma listagem com o sobrenome seguido do nome das pessoas cadastradas nesta agenda e eis que a primeira solução surge neste formato.
SELECT ...,
CASE WHEN sobrenome IS NULL
THEN nome
WHEN sobrenome IS NOT NULL
THEN sobrenome || ',' || nome
END
FROM ...
Funciona? Sim! É o mais adequado? Bem, se não existisse uma construção
própria para isto certamente seria uma alternativa bastante
utilizada. Entretanto existe uma função específica para isto, e ela se
chama COALESCE, cujo papel é retornar o primeiro valor não nulo
(NULL
) dos parametros passados. Sendo assim, com ela a construção
acima ficaria assim:
SELECT ...,
COALESCE(sobrenome || ',', '') || nome
FROM ...
Caso você esteja um pouco perdido ainda, sem problemas, o operador
||
é um operador de concatenação, sendo que 'a' || 'b'
retorna 'ab'
enquanto 'a' || NULL
retorna NULL
. Ou seja, desconheço o resultado real de
uma concatenação de algo conhecido com algo desconhecido, e o
COALESCE
vem justamente para isto. Sendo assim a construção acima
fica mais ou menos descrita assim: “seja otimista e concatene sobrenome
com a virgula e se o resultado for NULL
retorne um texto vazio, e então concatene com nome
.
Então você explica isto para a pessoa e orienta ler a documentação. A pessoa não lẽ, apenas “olha” e resultado seguinte é:
SELECT ...,
CASE WHEN COALESCE(endereco, '') <> ''
THEN CASE WHEN COALESCE(
COALESCE(endereco, '') ||
' ' ||
COALESCE(bairro, '')
) <> ' '
THEN endereco ||' '|| bairro
ELSE COALESCE(cidade, '')
END
ELSE COALESCE(cidade,'SEM CIDADE')
END
FROM ...
LEFT JOIN ...
LEFT JOIN ...
Não estou criticando o aninhamento de COALESCE
junto com
CASE
. Particularmente já escrevi coisa pior, mas só que quando eu
lia novamente fazia uma auto reflexão e percebia que não tava legal,
dai eu voltava para a documentação e percebia onde eu podia
melhorar. Uma resposta padrão é, “mas não tenho tempo então a primeira
coisa que sai é o que vai”. Bom, parar para ler a documentação e
controlar esse tempo num limiar finito era a técnica que eu
empregava. Alias, ainda emprego, pois é eficiente em mais de 90% dos
casos.
Possível solução?
Vocês tem todo o direito de não concordar comigo, mas eu vejo a solução abaixo mais legível, menos verbosa e mais declarativa do que a anterior.
SELECT ...,
COALESCE(endereco ||' '|| bairro, cidade, 'SEM CIDADE'),
FROM ...
LEFT JOIN ...
LEFT JOIN ...
Opa! Três parâmetros? Exatamente! Pois conforme a documentação:
The COALESCE function returns the first of its arguments that is not null.
Ou seja, você pode fazer isto:
SELECT ...,
COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, NULL, NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, NULL, 'SEM VALOR'),
COALESCE(NULL, 'SEM VALOR'),
...
FROM ...
Isso é possivel porque COALESCE
é uma função com um parametro
VARIADIC
, ou seja, foi criada de modo a permitir uma quantidade
variável de parametros de entrada, mas isso é outra história.
Estas e outras “curiosidadades” estou compilando numa apresentação que espero ter a chance de apresentá-la em alguns eventos este ano.
PS: se você leu até aqui, verá que existem vários erros de português no meu texto.
ATUALIZADO: Esta e outras curiosidades foram compiladas em uma palestra apresentada no PGBR2015.