A operação decimal do SQL Server não é precisa

Oct 27 2020

Quando executo esta operação simples no servidor SQL:

Select 800.0 /30.0 

Eu obtenho o valor 26,666666, onde mesmo se for arredondado para 6 dígitos, deve ser 26,666667. Como posso fazer com que o cálculo seja preciso? Tentei pesquisar sobre isso on-line e encontrei uma solução onde converter cada operando para um decimal de alta precisão antes da operação, mas isso não será conveniente para mim porque tenho muitos cálculos longos e complexos. acho que deve haver uma solução melhor.

Respostas

2 Larnu Oct 26 2020 at 23:46

Ao usar uma divisão, no SQL Server, quaisquer dígitos após a escala resultante são truncados, não arredondados. Para sua expressão, você tem a decimal(4,1)e a decimal(3,1), o que resulta em decimal(10,6):

Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Scale = max(6, s1 + p2 + 1)

Como resultado, 26.66666666666666~é truncado para 26.666666.

Você pode contornar isso aumentando o tamanho da precisão e da escala e, em seguida, de CONVERTvolta à precisão e escala necessárias. Por exemplo, aumente a precisão e a escala de decimal(3,1)para decimal(5,2)e converta de volta para decimal(10,6):

SELECT CONVERT(decimal(10,6),800.0 / CONVERT(decimal(5,3),30.0));

Isso retorna 26.666667.

CR241 Oct 26 2020 at 23:50

Isso pode ser útil:

Use ROUND (Transact-SQL)

SELECT ROUND(800.0 /30.0, 5) AS RoundValue;

Resultado:

RoundValue

26.666670
seanb Oct 27 2020 at 08:41

Acredito que seja porque o SQL Server considera seus números como decimalvalores (que são exatos, por exemplo, 6,6666 e 6,6667 significam exatamente esses valores, não 6 e dois terços) em vez de floatvalores (que podem funcionar com números aproximados).

Se você explicitamente lançar / converter floatem a no início, seus cálculos devem funcionar sem problemas.

Eis alguns exemplos para demonstrar a diferença entre int, decimale floatcálculos

  • Dividindo 20 por 3
  • Dividindo 20 por 3, depois multiplicando por 3 novamente (que matematicamente deve ser 20).
SELECT  (20/3)                             AS int_calc,
        (20/3) * 3                         AS int_calc_x3,
        (CAST(20 AS decimal(10,3)) /3)     AS dec_calc,
        (CAST(20 AS decimal(10,3)) /3) * 3 AS dec_calc_x3,
        (CAST(20 AS float) /3)             AS float_calc,
        (CAST(20 AS float) /3) * 3         AS float_calc_x3

com os seguintes resultados

int_calc   int_calc_x3   dec_calc   dec_calc_x3   float_calc         float_calc_x3
6         18             6.666666   19.999998     6.66666666666667   20

No seu caso, você pode usar

Select CAST(800.0 AS float) /30.0

o que resulta em 26,66666666666667

Observe que se você então multiplicar de volta por 30, obterá o resultado correto, por exemplo,

Select (CAST(800.0 AS float) /30.0) * 30

resulta em 800. Soluções que lidam com decimais não terão isso.

Observe também que, uma vez que você o tenha como um float, ele deve permanecer um float até ser convertido de volta para um decimal ou um int de alguma forma (por exemplo, salvo em uma tabela como um int). Então...

SELECT A.Num / 30
FROM (Select ((CAST(800.0 AS float) /30.0) * 30) AS Num) AS A

ainda resultará em 26,6666666666666667

Esperamos que isso o ajude em seus cálculos longos e complexos.