COALESCE e o aninhador frenético

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.

comments powered by Disqus