Хранение денег в десятичной колонке - какая точность и масштаб? - PullRequest
158 голосов
/ 22 октября 2008

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

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

А какую точность и масштаб я должен использовать? Я думал о точности 24/8. Это излишне, недостаточно или хорошо?


Вот что я решил сделать:

  • Сохранять коэффициенты конвертации (если применимо) в самой таблице транзакций в виде числа с плавающей запятой
  • Хранить валюту в таблице счетов
  • Сумма сделки составит DECIMAL(19,4)
  • Все вычисления с использованием коэффициента конвертации будут обрабатываться моим приложением, поэтому я продолжаю контролировать вопросы округления

Я не думаю, что плавающая ставка для конверсии - это проблема, так как она в основном для справки, и я все равно приведу ее к десятичной дроби.

Спасибо всем за ваш ценный вклад.

Ответы [ 10 ]

158 голосов
/ 22 октября 2008

Если вы ищете универсальный вариант, я бы рекомендовал DECIMAL(19, 4) - популярный выбор (быстрый Google подтверждает это). Я думаю, что это происходит от старого типа данных VBA / Access / Jet Currency, являющегося первым десятичным типом с фиксированной точкой в ​​языке; Decimal только в стиле «версия 1.0» (т.е. не полностью реализовано) в VB6 / VBA6 / Jet 4.0.

Практическое правило для хранения десятичных значений с фиксированной запятой - хранить как минимум еще одно десятичное число, которое фактически требуется для округления. Одна из причин для сопоставления старого типа Currency на переднем конце с типом DECIMAL(19, 4) на заднем конце была в том, что Currency показывал округление банкиров по своей природе, тогда как DECIMAL(p, s) округлялось усечением.

Дополнительное десятичное место в хранилище для DECIMAL позволяет реализовать собственный алгоритм округления вместо того, чтобы принимать значение по умолчанию поставщика (а округление банкиров вызывает тревогу, если не сказать больше, для дизайнера, ожидающего все значения, заканчивающиеся на .5 округлить от нуля).

Да, DECIMAL(24, 8) звучит для меня излишне. Большинство валют указаны в четыре или пять десятичных знаков. Мне известны ситуации, когда требуется десятичная шкала 8 (или более) , но именно здесь «нормальная» денежная сумма (скажем, четыре десятичных знака) пропорциональна, подразумевая, что десятичная точность должна быть уменьшены соответственно (также рассмотрите тип с плавающей точкой в ​​таких обстоятельствах). И сегодня ни у кого нет столько денег, чтобы требовать десятичную точность 24:)

Однако, вместо того, чтобы подход «один размер подходит всем», некоторые исследования могут быть в порядке. Спросите своего дизайнера или эксперта по домену о правилах бухгалтерского учета, которые могут быть применимы: GAAP, ЕС и т. Д. Я смутно припоминаю некоторые внутригосударственные переводы в ЕС с явными правилами округления до пяти десятичных знаков, поэтому для хранения используется DECIMAL(p, 6). Бухгалтеры обычно предпочитают четыре знака после запятой.


PS Избегайте типа данных SQL Server MONEY, потому что он имеет серьезные проблемы с точностью при округлении, среди других соображений, таких как переносимость и т. Д. См. Блог Аарона Бертранда .


Microsoft и разработчики языка выбрали округление банкира, потому что разработчики оборудования выбрали его [цитата?]. Например, оно закреплено в стандартах Института инженеров по электротехнике и электронике (IEEE). И дизайнеры оборудования выбрали это, потому что математики предпочитают это. См. Википедия ; перефразируя: издание «Вероятность и теория ошибок» 1906 года назвало это «правилом компьютера» («компьютеры», то есть люди, которые выполняют вычисления).

92 голосов
/ 22 октября 2008

Недавно мы внедрили систему, которая должна обрабатывать значения в нескольких валютах и ​​конвертировать их между собой, и выяснила некоторые трудности.

НИКОГДА НЕ ИСПОЛЬЗУЙТЕ НОМЕРА С ПЛАВУЩИМИ ТОЧКАМИ ДЛЯ ДЕНЕГ

Арифметика с плавающей запятой вносит неточности, которые могут быть не замечены, пока они не напортачили. Все значения должны храниться в виде целых чисел или типов с фиксированной десятичной дробью, и если вы решите использовать тип с фиксированной десятичной дробью, убедитесь, что вы точно понимаете, что этот тип делает под капотом (т.е. использует ли он внутренне целое число или число с плавающей запятой) типа).

Когда вам нужно сделать расчеты или преобразования:

  1. Преобразование значений в число с плавающей запятой
  2. Рассчитать новое значение
  3. Округлить число и преобразовать его обратно в целое число

При преобразовании числа с плавающей запятой обратно в целое число на шаге 3 не просто приводите его - используйте математическую функцию, чтобы сначала округлить его. Обычно это будет round, хотя в особых случаях это может быть floor или ceil. Знайте разницу и тщательно выбирайте.

Сохраните тип числа вместе со значением

Это может быть не так важно для вас, если вы работаете только с одной валютой, но для нас это было важно при работе с несколькими валютами. Мы использовали трехсимвольный код для валюты, такой как USD, GBP, JPY, EUR и т. Д.

В зависимости от ситуации, также может быть полезно хранить:

  • Является ли число до или после налога (и какая ставка налога была)
  • Является ли число результатом преобразования (и из чего оно было преобразовано)

Знайте границы точности чисел, с которыми вы имеете дело

Для реальных значений вы хотите быть точным, как наименьшая единица валюты. Это означает, что у вас нет значений меньше цента, пенни, иены, фена и т. Д. Не сохраняйте значения с большей точностью, чем без причины.

Внутренне, вы можете иметь дело с меньшими значениями, в этом случае это другой тип значения валюты . Убедитесь, что ваш код знает, что есть, и не перепутает их. Избегайте использования значений с плавающей запятой даже здесь.


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

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

В базе данных значения хранятся в виде строки в следующем формате:

USD:2500

Это хранит значение $ 25,00. Мы смогли сделать это только потому, что код, работающий с валютами, не обязательно должен находиться внутри самого уровня базы данных, поэтому все значения можно сначала преобразовать в память. Другие ситуации, без сомнения, поддаются другим решениям.


И если раньше я не уточнил, не используйте float!

3 голосов
/ 25 мая 2014

При обработке денег в MySQL используйте DECIMAL (13,2), если вы знаете точность значений ваших денег, или DOUBLE, если вы просто хотите получить быстрое и достаточно хорошее приблизительное значение. Поэтому, если ваше приложение должно обрабатывать денежные суммы до триллиона долларов (или евро или фунтов), то это должно работать:

DECIMAL(13, 2)

Или, если вам необходимо соблюдать GAAP , используйте:

DECIMAL(13, 4)
2 голосов
/ 22 октября 2008

4 десятичных знака дадут вам точность хранения самых маленьких единиц валюты в мире. Вы можете взять его дальше, если вам нужна точность микроплатежей (наноплатежей ?!).

Я тоже предпочитаю DECIMAL типам денег, специфичным для СУБД, так вам удобнее хранить такую ​​логику в IMO приложения. Другой подход в том же духе - просто использовать [длинное] целое число с форматированием в ¤unit.subunit для удобства чтения (¤ = символ валюты) на уровне приложения.

1 голос
/ 22 октября 2008

Если вы собираетесь выполнять какие-либо арифметические операции в БД (умножение тарифов и т. Д.), Вы, вероятно, захотите гораздо большей точности, чем предлагают здесь люди, по тем же причинам, по которым вы никогда бы не захотел использовать в коде приложения что-либо меньшее, чем значение с плавающей запятой двойной точности.

1 голос
/ 22 октября 2008

Иногда вам нужно идти меньше чем на цент, и есть международные валюты, которые используют очень большие демононы. Например, вы можете взимать с ваших клиентов 0,088 цента за транзакцию. В моей базе данных Oracle столбцы определены как NUMBER (20,4)

1 голос
/ 22 октября 2008

Тип данных money на SQL Server состоит из четырех цифр после десятичной дроби.

Из электронной документации по SQL Server 2000:

Денежные данные представляют собой положительные или отрицательные суммы денег. В Microsoft® SQL Server ™ 2000 денежные данные хранятся с использованием типов данных money и smallmoney. Денежные данные могут храниться с точностью до четвертого знака после запятой. Используйте тип данных money для хранения значений в диапазоне от -922,337,203,685,477.5808 до +922,337,203,685,477.5807 (для хранения значения требуется 8 байтов). Используйте тип данных smallmoney для хранения значений в диапазоне от -214 748,3648 до 214 748,3647 (для хранения значения требуется 4 байта). Если требуется большее количество десятичных разрядов, используйте тип данных decimal.

0 голосов
/ 24 августа 2015

Поздний ответ здесь, но я использовал

DECIMAL(13,2)

, что, на мой взгляд, должно допускать до 99 999 999 999,99.

0 голосов
/ 22 октября 2008

Я думаю, что в значительной степени требования вашего или вашего клиента должны определять, какую точность и масштаб использовать. Например, для веб-сайта электронной коммерции, над которым я работаю, который занимается только деньгами в фунтах стерлингов, я должен был указывать десятичное значение (6, 2).

0 голосов
/ 22 октября 2008

Если бы вы использовали IBM Informix Dynamic Server, у вас был бы тип MONEY, который является второстепенным вариантом для типа DECIMAL или NUMERIC. Это всегда тип с фиксированной запятой (тогда как DECIMAL может быть типом с плавающей запятой). Вы можете указать масштаб от 1 до 32 и точность от 0 до 32 (по умолчанию используется масштаб 16 и точность 2). Таким образом, в зависимости от того, что вам нужно хранить, вы можете использовать DECIMAL (16,2) - все еще достаточно большой, чтобы удерживать дефицит федерального бюджета США, с точностью до цента - или вы можете использовать меньший диапазон или больше десятичных знаков.

...