Question

Le débogage de code SQL relatif aux finances a révélé un problème étrange lié à la précision numérique (24,8) des mathématiques.

En exécutant la requête suivante sur votre MSSQL, vous obtiendrez un résultat d'expression A + B * C égal à 0.123457

SÉLECTIONNER A,        B,        C        A + B * C DE ( 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

Nous avons donc perdu 2 symboles significatifs. En essayant de résoudre ce problème de différentes manières, j’ai obtenu que la conversion du résultat de la multiplication intermédiaire (qui est zéro!) En numérique (24,8) fonctionnerait correctement.

Et enfin a une solution. Mais j’ai toujours une question à poser: pourquoi MSSQL se comporte-t-il de la sorte et quels types de conversions se sont réellement produits dans mon échantillon?

Était-ce utile?

La solution

Tout comme l’ajout du type de type à virgule flottant est inexact, la multiplication des types décimaux peut être inexacte (ou provoquer une imprécision) si vous dépassez la précision. Voir Conversion de type de données et décimal et numérique .

Puisque vous avez multiplié NUMERIC (24,8) et NUMERIC (24,8) , et que SQL Server ne vérifiera que le type, pas le contenu, il essaiera probablement pour enregistrer les 16 chiffres non décimaux (24 - 8) potentiels s’ils ne peuvent pas enregistrer les 48 chiffres de précision (maximum est de 38). Si vous en combinez deux, vous obtenez 32 chiffres non décimaux, ce qui vous laisse seulement 6 chiffres décimaux (38 - 32).

Ainsi, la requête d'origine

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

réduit à

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

Encore une fois, entre NUMERIC (24,8) et NUMERIC (38,6) , SQL Server essaiera de sauvegarder les 32 chiffres potentiels de chiffres non décimaux. A + D réduit à

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

qui vous donne 0.123457 après arrondi.

Autres conseils

En suivant la logique indiquée par eed3si9n et ce que vous avez dit dans votre question, il semble que la meilleure approche lors d'opérations mathématiques consiste à les extraire dans une fonction et à spécifier la précision après chaque opération,

Dans ce cas, la fonction pourrait ressembler à:

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

Malgré ce qui est indiqué sur la précision, échelle et Longueur (Transact-SQL) . Je crois qu’il applique également une "échelle" minimale (nombre de décimales) de 6 au type NUMERIC résultant pour une multiplication identique à celle utilisée pour la division, etc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top