Потеря числовой точности в SQL Server 2005

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

Выполнив следующий запрос на своем MSSQL, вы получите результат выражения A + B * C, равный 0,123457

ВЫБЕРИТЕ, B, C, А + В * С ИЗ ( ВЫБЕРИТЕ CAST (0,12345678 КАК ЧИСЛО (24,8)) КАК A, CAST (0 КАК ЧИСЛО (24,8)) КАК B, CAST (500 КАК ЧИСЛО (24,8)) КАК C ) Т

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
9 002
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

Поскольку вы умножили 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))

что дает вам 0.123457 после округления.

ты имел ввиду ЧИСЛОВЫЙ (32,6)) ?? Если сумма должна быть 38

Edmondo1984 03.12.2012 23:47

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

Eugene Yokota 04.12.2012 00:06

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

Edmondo1984 04.12.2012 09:53

@ Edmondo1984 В NUMERIC(p, s)p означает точное число цифр общее. Итак, в NUMERIC(24, 8) есть 16 цифр не десятичных знаков. Никаких битов. Опять же, прочтите ссылку на документы msdn.

Eugene Yokota 05.12.2012 01:11

Следуя логике, указанной 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

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

Eugene Yokota 24.09.2008 16:48

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

kristof 25.09.2008 15:09

Несмотря на то, что написано на Точность, масштаб и длина (Transact-SQL). Я считаю, что он также применяет минимальный масштаб (количество знаков после запятой), равный 6, к результирующему типу NUMERIC для умножения так же, как и для деления и т. д.

Другие вопросы по теме