Хранение денег в десятичном столбце - какова точность и масштаб?

StackOverflow https://stackoverflow.com/questions/224462

Вопрос

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

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

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


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

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

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

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

Это было полезно?

Решение

Если вы ищете универсальный вариант, я бы посоветовал 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 позволяет реализовать собственный алгоритм округления вместо использования значения по умолчанию, установленного поставщиком (а округление банкиров вызывает, по меньшей мере, тревогу для проектировщика, ожидающего, что все значения, оканчивающиеся на 0,5, будут округляться от нуля).

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

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


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


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

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

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

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

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

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

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

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

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

Возможно, это не так важно для вас, если вы работаете только с одной валютой, но для нас это было важно при работе с несколькими валютами.Мы использовали 3-символьный код для обозначения валюты, такой как 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!

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

DECIMAL(13, 2)

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

DECIMAL(13, 4)

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

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

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

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

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

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

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

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

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

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

DECIMAL(13,2)

и я прав, полагая, что должно быть до 99 999 999 999,99.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top