5

Отладка некоторых связанных с финансами SQL-кода показала странную проблему с числовой точностью (24,8).SQL Server 2005 числовые потери точности

Запуск следующего запроса на вашем MSSQL вы получите результат выражения * C + B, чтобы быть 0,123457

ВЫБРАТЬ A, B, C, A + B * C FROM ( SELECT CAST (+0,12345678 А.С. Числовой (24,8)) в виде, CAST (0 AS NUMERIC (24,8)) как В, CAST (500 AS NUMERIC (24,8)) AS С ) T

Так мы потеряли 2 значимых символа. Пытаясь получить это исправление по-разному, я получил, что преобразование результата промежуточного умножения (которое равно нулю) в числовое (24,8) будет работать нормально.

И, наконец, есть решение. Но все же у меня возникает вопрос - почему MSSQL ведет себя таким образом и какие преобразования типов действительно происходили в моем примере?

ответ

7

Точно так же, как добавление типа с плавающей точкой является неточным, умножение десятичных типов может быть неточным (или вызвать неточность), если вы превысите точность. См. Data Type Conversion и decimal and numeric.

Поскольку вы умножали NUMERIC(24,8) и NUMERIC(24,8), а SQL Server будет проверять тип, а не контент, он, вероятно, попытается сохранить потенциальные 16 не десятичных цифр (24 - 8), когда он не сможет сохранить все 48 цифр (макс - 38). Объедините два из них, вы получите 32 десятичных разряда, что оставляет вас всего 6 десятичных цифр (38-32).

Таким образом, первоначальный запрос

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 

сводится к

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 

Опять же, между NUMERIC(24,8) и NUMERIC(38,6), SQL Server будет пытаться сохранить потенциальные 32 цифр, не являющихся десятичными знаками, так A + D сводится к

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

который gi ves вы 0.123457 после округления.

+0

Вы имели в виду NUMERIC (32,6)) ?? Если сумма должна быть 38 – Edmondo1984

+0

@ Edmondo1984 Пожалуйста, прочитайте ссылки и поймите, что означают оба числа. –

+0

Вы говорите, что при умножении двух чисел (24,8) сервер попытается сохранить 16 бит и создать (32,6), как он станет 38,6?Спасибо – Edmondo1984

0

Следуя логике указывал eed3si9n и то, что вы сказали в своем вопросе, кажется, что лучший подход при выполнении математику операций является извлечение их в функцию и, кроме того, чтобы указать точность после каждой операции,

Он это случае функция может выглядеть примерно так:

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 
+0

Этот подход не может решить проблему SQL Server, измельчающей десятичные части. Чтобы сохранить десятичные части, может потребоваться перевести на @a и @b в double. –

+0

Спасибо, я буду иметь это в виду, работая над некоторыми математическими вычислениями в SQL, до сих пор мне не нужно использовать его, но сейчас хорошо, что могут быть некоторые проблемы, которые следует учитывать – kristof

0

Несмотря на то, что он говорит на Precision, Scale, and Length (Transact-SQL). Я считаю, что он также применяет минимальную «шкалу» (число десятичных знаков) 6 к результирующему NUMERIC типу для умножения так же, как и для деления и т. Д.