L'operazione decimale di SQL Server non è accurata

Oct 27 2020

Quando eseguo questa semplice operazione in SQL server:

Select 800.0 /30.0 

Ottengo il valore 26.666666, dove anche se arrotonda per 6 cifre dovrebbe essere 26.666667. Come posso fare in modo che il calcolo sia accurato? Ho provato a cercarlo online e ho trovato una soluzione in cui eseguo il cast di ogni operando con un decimale ad alta precisione prima dell'operazione, ma questo non sarà conveniente per me perché ho molti calcoli lunghi e complessi. penso che ci debba essere una soluzione migliore.

Risposte

2 Larnu Oct 26 2020 at 23:46

Quando si utilizza una divisione, in SQL Server, qualsiasi cifra dopo la scala risultante viene troncata, non arrotondata. Per la tua espressione hai un decimal(4,1)e a decimal(3,1), che si traduce in a decimal(10,6):

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

Di conseguenza, 26.66666666666666~viene troncato a 26.666666.

È possibile aggirare questo problema aumentando la dimensione della precisione e della scala, quindi CONVERTtornare alla precisione e alla scala richieste. Ad esempio, aumentare la precisione e la scala di decimal(3,1)a decimal(5,2)e riconvertire in decimal(10,6):

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

Questo ritorna 26.666667.

CR241 Oct 26 2020 at 23:50

Questo potrebbe essere utile:

Usa ROUND (Transact-SQL)

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

Risultato:

RoundValue

26.666670
seanb Oct 27 2020 at 08:41

Credo che sia perché SQL Server prende i tuoi numeri come decimalvalori (che sono esatti, ad esempio, 6.6666 e 6.6667 significa esattamente quei valori, non 6 e due terzi) piuttosto che floatvalori (che possono funzionare con numeri approssimativi).

Se lo lanci / converti esplicitamente in floata all'inizio, dovresti ottenere che i tuoi calcoli funzionino senza intoppi.

Ecco alcuni esempi per dimostrare la differenza tra int, decimale floatcalcoli

  • Dividendo 20 per 3
  • Dividendo 20 per 3, quindi moltiplicando di nuovo per 3 (che matematicamente dovrebbe essere 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

con i seguenti risultati

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

Nel tuo caso, puoi usare

Select CAST(800.0 AS float) /30.0

che si traduce in 26.6666666666667

Nota se moltiplichi per 30, otterrai il risultato corretto, ad es.

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

risultati in 800. Le soluzioni che si occupano di decimali non lo avranno.

Nota anche che una volta che lo hai come float, dovrebbe rimanere un float fino a quando non viene riconvertito in un decimale o in un int in qualche modo (ad esempio, salvato in una tabella come int). Così...

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

risulterà comunque in 26.6666666666667

Si spera che questo ti aiuti nei tuoi calcoli lunghi e complessi.