Você já pensou que o SQL pode estar errado em matemática? Parece loucura. Mas se você usou o tipo de dados SQL FLOAT, pode ter se deparado com os problemas que estou prestes a mostrar.
Considere isto. 0,1 + 0,2 deve ser 0,3, certo? Mas verifique isso usando o tipo de dados SQL FLOAT.
DECLARE @f1 FLOAT = 0.1
DECLARE @f2 FLOAT = 0.2
SELECT CASE WHEN @f1 + @f2 = .3 THEN 1 ELSE 0 END
O resultado correto é 1. Mas verifique a Figura 1.
Eu tenho sua atenção agora? Eu certamente espero que sim. É bastante assustador depender de um sistema que não nos dá a matemática correta. Mas este artigo irá ajudá-lo a evitar isso.
Há algum trabalho a fazer. Precisamos começar do que é um tipo de dados FLOAT.
O que é o tipo de dados SQL FLOAT?
O tipo de dados SQL FLOAT é um tipo de dados numérico aproximado usado para números de ponto flutuante. Eles podem armazenar números muito grandes ou muito pequenos. Eles também são usados para cálculos que exigem tempos de processamento rápidos.
Tudo isso tem o custo da perda de precisão. Além disso, você não pode dizer onde o ponto decimal será colocado após o cálculo - ele flutua . Enquanto isso, números exatos como DECIMAL terão uma posição de ponto decimal fixo.
Como você declara um tipo de dados SQL FLOAT
A sintaxe é FLOAT[(n)], onde n é o número de bits usados para armazenar a mantissa de um número de ponto flutuante em notação científica. Isso também determina a precisão e o tamanho do armazenamento. Os valores possíveis para n estão entre 1 e 53. Observe que n é opcional.
Aqui está um exemplo:
DECLARE @floatValue1 FLOAT; -- Float variable without the number of bits
DECLARE @floatValue2 FLOAT(3) -- Float variable with 3 bits
Se você não especificar n , o padrão é 53. Esse também é o valor máximo. Além disso, FLOAT(53) é um número de ponto flutuante de precisão dupla ou binário64. Além de usar FLOAT(53), você também pode declará-lo como DOUBLE PRECISION.
As 3 declarações a seguir são funcionalmente equivalentes:
DECLARE @double1 FLOAT(53);
DECLARE @double2 FLOAT;
DECLARE @double3 DOUBLE PRECISION;
A tabela mostra o número de bits e o tamanho de armazenamento correspondente.
Valor de n | Tamanho do armazenamento |
1 a 24 | 4 bytes |
25 a 53 | 8 bytes |
O SQL FLOAT e o REAL são a mesma coisa?
REAL também é FLOAT(24). Também é referido como precisão simples ou binário32.
Por que saber isso é importante
Saber que este é um número aproximado impedirá que você o use para cálculos que exigem precisão. Você também está preocupado com armazenamento e memória? Use REAL ou FLOAT(24) se não precisar de valores muito grandes ou muito pequenos.
Quais são as diferenças entre FLOAT e DECIMAL?
FLOAT é um número aproximado. DECIMAL é um número exato. Aqui está um resumo das diferenças em uma tabela:
FLUTUAR | DECIMAL | |
Ponto Decimal | Pode ser colocado em qualquer lugar do dígito | Posição fixa |
Limite máximo | 38 dígitos ou 99.999.999.999.999.999.999.999.999.999.999.999.999 | FLOAT(53) tem um alcance máximo de 1,79E+308 ou 179 seguido por 306 zeros |
Armazenamento | Máximo de 8 bytes | Máximo de 17 bytes |
Resultado computacional | Aproximado | Exato |
Verificações de comparação | Não use =ou <>. Evite ao arredondar | =ou <> operadores. Bom para arredondar |
Você já viu na Figura 1 como calcular um número FLOAT pode ter resultados estranhos. Se você alterar o tipo de dados para DECIMAL assim:
DECLARE @d1 DECIMAL(2,1) = 0.1
DECLARE @d2 DECIMAL(2,1) = 0.2
SELECT CASE WHEN @d1 + @d2 = 0.3 THEN 1 ELSE 0 END
O resultado estará correto.
Usar um operador de desigualdade também é um problema. Confira o circuito abaixo.
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue <> 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
O que você acha? Veja a Figura 2 abaixo.
Estrondo! Loop infinito! A condição de desigualdade será sempre verdadeira. Portanto, a escolha lógica é alterar o tipo para DECIMAL.
DECLARE @decimalValue DECIMAL(2,1) = 0.0
WHILE @decimalValue <> 5.0
BEGIN
PRINT @decimalValue;
SET @decimalValue += 0.1;
END
O código acima certamente irá parar quando @decimalValue é igual a 5,0. Veja você mesmo na Figura 3 abaixo.
Legal! Mas se você ainda insistir em FLOAT, isso funcionará bem sem o loop infinito.
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue < 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
Enquanto isso, o arredondamento também está desativado. Considere o seguinte:
DECLARE @value FLOAT(2) = 1.15
SELECT ROUND(@value, 1) -- This will result to 1.1
Em vez de 1.20, o código resulta em 1.1. Mas se você usar DECIMAL, o resultado será correto.
DECLARE @value DECIMAL(3,2) = 1.15
SELECT ROUND(@value, 1) -- This will result in 1.2 or 1.20
Quando FLOAT está correto e DECIMAL não
Os números exatos NÃO são tão exatos o tempo todo? Para reproduzir esse problema, usaremos um cálculo e, em seguida, o reverteremos. Primeiro, vamos preparar os dados.
CREATE TABLE ExactNumerics1
(
fixed1 DECIMAL(8,4),
fixed2 DECIMAL(8,4),
fixed3 DECIMAL(8,4),
calcValue1 AS fixed3 / fixed1 * fixed2
)
GO
INSERT INTO ExactNumerics1
(fixed1,fixed2,fixed3)
VALUES
(54,0.03,1*54/0.03)
A tabela acima usará valores fixos para as 2 primeiras colunas. A terceira coluna terá o cálculo. Por fim, a quarta, que é uma coluna computada, fará o cálculo inverso. O resultado correto na coluna calculada deve ser 1.
Agora, para compará-lo ao FLOAT, vamos criar uma tabela e dados semelhantes.
CREATE TABLE ApproxNumerics1
(
float1 FLOAT(2),
float2 FLOAT(2),
float3 FLOAT(2),
calcValue1 AS float3 / float1 * float2
)
INSERT INTO ApproxNumerics1
(float1, float2, float3)
VALUES
(54,0.03,1*54/0.03)
Vamos consultar.
SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1
Os resultados? Confira a Figura 4.
O que aconteceu aqui? FLOAT acertou, mas DECIMAL não. Algo deu errado.
CONVERSÃO IMPLÍCITA FAZ ISSO NOVAMENTE
A conversão implícita acontece porque o SQL perdoa. Quando diferentes tipos de dados são usados em um cálculo, o SQL Server tenta convertê-lo usando a conversão implícita nas nossas costas.
Uma conversão realmente aconteceu? Além disso, cada coluna no ExactNumerics1 tabela é um DECIMAL.
Vamos verificar a estrutura da tabela do ExactNumerics1 tabela no SQL Server Management Studio:
Observe a área de caixa vermelha na Figura 3. A coluna computada é um DECIMAL(30,17), não um DECIMAL(8,4). De acordo com a documentação oficial, 2 colunas DECIMAL com precisão e escala diferentes são 2 tipos de dados diferentes . Veja você mesmo aqui. Por causa da diferença, é necessária uma conversão. Assim, a conversão implícita entra em jogo.
E se eles são diferentes e uma conversão implícita aconteceu?
Novamente, com base na documentação oficial, uma perda de precisão ou escala pode ocorrer durante a conversão implícita . Assim, é necessário um CAST explícito. Observe o tipo de dados DECIMAL na tabela de conversão nessa referência.
Alguma perda aconteceu aqui. Se a coluna calculada também for DECIMAL(8,4), a conversão implícita não ocorre.
Para evitar a conversão implícita, siga a documentação oficial. A estrutura da tabela deveria ter ficado assim:
CREATE TABLE ExactNumerics2
(
fixed1 DECIMAL(8,4),
fixed2 DECIMAL(8,4),
fixed3 DECIMAL(8,4),
calcValue1 AS CAST(fixed3 / fixed1 * fixed2 AS DECIMAL(8,4)) -- the explicit CAST
)
O CAST explícito na coluna calculada garante que os tipos de dados sejam consistentes. Se também seguirmos essa estrutura e inserirmos os mesmos dados, o resultado estará correto. Confira a nova saída na Figura 6 abaixo.
Eventualmente, os números exatos não serão exatos se ocorrer uma conversão implícita entre 2 ou mais valores DECIMAL.
Por que saber isso é importante
Dá uma ideia do que você precisa para suas tabelas e variáveis. Além disso, a conversão implícita pode fazer com que até mesmo números exatos fiquem malucos. Portanto, defina explicitamente a precisão e a escala e seja consistente com ela em seus cálculos.
Devo usar SQL FLOAT para dados financeiros?
Ao calcular porcentagens em cada fatia de um gráfico de pizza, a soma deve ser 100%. Os totais nos relatórios resumidos e detalhados também devem ser consistentes. Se a precisão dos resultados for crucial, um tipo de dados aproximado como FLOAT não fará o trabalho. A escolha lógica para isso é DECIMAL.
Mas fica uma pergunta.
Quando você deve usar FLOAT?
Use FLOAT para dados que exigem valores astronômicos, como distâncias entre galáxias. Enquanto isso, o tipo de dado DECIMAL sofrerá um estouro aritmético com esse tipo de dado. Valores minúsculos como o diâmetro de um núcleo atômico também se encaixam usando FLOAT. Dados científicos e outros valores que não exigem precisão também podem se beneficiar do FLOAT.
Por que saber isso é importante
Não dizemos que FLOAT é ruim e DECIMAL é bom ou vice-versa. Conhecer os casos de uso corretos para cada um fornecerá a você e a seus usuários os resultados pretendidos. E, novamente, você quer seus usuários felizes, certo?
Conclusão
No final do dia, todos nós queremos fazer nossos trabalhos e ser bons neles. A matemática sempre fará parte do nosso trabalho. E conhecer os tipos de dados numéricos corretos também nos ajudará a lidar com isso. Não é difícil se você sabe o que está fazendo.
Espero que este artigo tenha ajudado a evitar matemática estranha no SQL Server.
Você tem mais alguma coisa a acrescentar? Então, deixe-nos saber na seção de comentários. Compartilhe isso também em suas plataformas de mídia social favoritas.