Domanda

Il debug di alcuni codici SQL relativi alla finanza ha riscontrato uno strano problema con la precisione matematica numerica (24,8).

Eseguendo la seguente query sul tuo MSSQL, il risultato dell'espressione A + B * C sarà 0,123457

SELEZIONA A,        B,        C,        A + B * C A PARTIRE DAL ( SELEZIONA CAST (0.12345678 COME NUMERICO (24,8)) COME A,        CAST (0 COME NUMERICO (24,8)) AS B,        CAST (500 COME NUMERICO (24,8)) COME C ) T

Quindi abbiamo perso 2 simboli significativi. Cercando di risolvere questo problema in diversi modi ho ottenuto che la conversione del risultato della moltiplicazione intermedia (che è Zero!) In numerico (24,8) avrebbe funzionato bene.

E finalmente una soluzione. Ma ho ancora una domanda: perché MSSQL si comporta in questo modo e quali tipi di conversioni si sono effettivamente verificati nel mio campione?

È stato utile?

Soluzione

Proprio come l'aggiunta del tipo float è imprecisa, la moltiplicazione dei tipi decimali può essere inaccurata (o causare imprecisione) se si supera la precisione. Vedi Conversione del tipo di dati e decimale e numerico .

Poiché hai moltiplicato NUMERIC (24,8) e NUMERIC (24,8) e SQL Server controllerà solo il tipo e non il contenuto, probabilmente proverà per salvare le potenziali 16 cifre non decimali (24 - 8) quando non è possibile salvare tutte le 48 cifre di precisione (massimo 38). Combinane due, otterrai 32 cifre non decimali, che ti lasceranno con solo 6 cifre decimali (38 - 32).

Quindi la query originale

SELECT A, B, C, A + B * C
FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
  CAST(0 AS NUMERIC(24,8)) AS B,
  CAST(500 AS NUMERIC(24,8)) AS C ) T

si riduce a

SELECT A, B, C, A + D
FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A,
  CAST(0 AS NUMERIC(24,8)) AS B,
  CAST(500 AS NUMERIC(24,8)) AS C,
  CAST(0 AS NUMERIC(38,6)) AS D ) T

Ancora una volta, tra NUMERIC (24,8) e NUMERIC (38,6) , SQL Server tenterà di salvare le potenziali 32 cifre di non decimali, quindi A + D si riduce a

SELECT CAST(0.12345678 AS NUMERIC(38,6))

che ti dà 0.123457 dopo l'arrotondamento.

Altri suggerimenti

Seguendo la logica evidenziata da eed3si9n e cosa hai detto nella tua domanda sembra che l'approccio migliore quando si eseguono operazioni matematiche sia estrarli in una funzione e inoltre specificare la precisione dopo ogni operazione,

In questo caso la funzione potrebbe assomigliare a:

create function dbo.myMath(@a as numeric(24,8), @b as numeric(24,8), @c as numeric(24,8))
returns  numeric(24,8)
as
begin 
    declare @d as numeric(24,8)
    set @d = @b* @c
    return @a + @d
end

Nonostante ciò che dice su Precisione, scala e Lunghezza (Transact-SQL) . Credo che stia anche applicando una 'scala' minima (numero di cifre decimali) di 6 al tipo NUMERICO risultante per la moltiplicazione, così come per la divisione ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top