A minha nova realidade profissional trouxe-me o contacto diário com o bem conhecido Sistema de Gestão de Base de Dados (SGBD) da Microsofot: Microsoft SQL Server.

Não é pretensão deste post tecer qualquer consideração sobre a comprovada qualidade deste motor, mas sim expôr uma situação no mínimo curiosa que, embora possa parecer irrelevante, representou um gasto de tempo de produção considerável.

Para os interessados em simular esta situação e para o decorrer deste post, propiciem um ambiente com a seguinte tabela:

1
2
3
4
5
CREATE TABLE teste(
resultado SMALLINT,
valor1 FLOAT,
valor2 FLOAT
)

e com o registo:

1
INSERT INTO teste (resultado,valor1,valor2) VALUES (0,175.1,35.02)

O problema gira agora em torno das queries que se seguem:

1
2
UPDATE teste SET resultado = valor1/valor2
SELECT (valor1/valor2) AS ValorEsperado, resultado AS ValorArmazenado FROM test


Se tudo correr como esperado, o resultado da execução da instrução select será um registo com dois campos: ValorEsperado e ValorArmazenado – cujos valores serão respectivamente 4 e 5.

O que há de estranho nesta situação?!
O valor esperado da operação valor1/valor2 é efectivamente 5 (ValorEsperado) no entanto, aquando do update foi armazenado o valor 4 (ValorArmazenado).

Levei algum tempo a perceber a origem do problema que pode até parecer óbvia.
Os atributos valor1 e valor2 são do tipo float enquanto que o atributo resultado, onde será armazenado o valor resultante da divisão, é do tipo smallint.
O problema dá-se num suposto casting que é feito internamente para armazenar esse valor como smallint.
Até aqui pacífico. Embora estejamos a falar em valor absoluto de 5 unidades na verdade o que temos é 5.0 e tenho plena consciência da ousadia do casting de integer (smallint pode ver-se como um subset do tipo int) para float.
O curioso, e que me deixou a pensar foi mesmo o resultado da instrução update quando é feito um casting “explicíto”:

1
UPDATE teste SET resultado = CAST((valor1/valor2) AS SMALLINT)

Numa primeira análise pareceu-me lógico que o motor, sabendo da inconsistência de tipos, fizesse internamente um cast de float para smallint, mas parece que isso não acontece.

Esta brincadeira, consumiu-me aproximadamente 10min: 2min para análise do problema e 8min para considerações de mim para comigo sobre o mistério.
Para não o deitar comigo na almofada estou atento às vossas considerações.