PostgreSQL: какой тип данных следует использовать для валюты?

Похоже на Money Тип не рекомендуется, как описано здесь

Мое приложение должно хранить валюту, какой тип данных я буду использовать? Числовой, денежный или поплавковый?

8 ответов

Решение

Числовое с принудительной точностью 2 единицы. Никогда не используйте float или float, как тип данных, для представления валюты, потому что если вы это сделаете, люди будут недовольны, когда итоговая цифра финансового отчета неверна на + или - на несколько долларов.

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

Ваш источник никоим образом не является официальным. Это относится к 2011 году, и я даже не узнаю авторов. Если бы тип денег был "обескуражен", PostgreSQL сказал бы об этом в руководстве, а это не так.

Для более официального источника прочитайте эту ветку в pgsql-general (только с этой недели!) С заявлениями основных разработчиков, включая D'Arcy JM Cain (первоначальный автор типа money) и Tom Lane:

В принципе, money имеет свое (ограниченное) использование. Преимущество над numeric это производительность.

decimal это просто псевдоним для numeric в Postgres.

Связанный ответ (и комментарии!) Об улучшениях в последних выпусках:

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

Ваш выбор:

  1. integer: хранить сумму в центах. Это то, что используют транзакции EFTPOS.
  2. decimal(12,2): сохранить сумму с двумя знаками после запятой. Это то, что использует большинство программного обеспечения главной книги.
  3. float: ужасная идея - неадекватная точность. Это то, что используют наивные разработчики.

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

Обратите внимание, что если вы объединяете несколько транзакций, которые были результатом расчета (например, с использованием обменного курса), в одно значение, имеющее деловое значение, точность должна быть выше, чтобы обеспечить точное значение макроса; рассмотреть возможность использования чего-то вроде decimal(18, 8) Таким образом, сумма является точной, и отдельные значения могут быть округлены до центовой точности для отображения.

Из большого опыта во многих приложениях я рекомендую 64-bit integer хранение значений в микродолларах (или аналогичной основной валюте).

  • Микро = 1 миллионная, поэтому 1 микродоллар составляет 1 / 1 000 000. Это обеспечивает очень тонкую гранулярность, чтобы обрабатывать доли цента без проблем, если у вас нет особых потребностей.

  • 64-разрядные целые числа невелики, их легко хранить, они просты в обращении и совместимы со всем.

  • Легко поддерживать точность во всех расчетах и ​​(в качестве наилучшей практики) применять округление при окончательном выводе (например, при создании счета).

  • Хорошо работает при очень небольших ценах за единицу (например, показы в рекламных кампаниях или плата за API).

Я сохраняю все свои денежные поля как:

numeric(15,6)

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

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

Использовать BigInt для хранения валюты в виде положительного целого числа, представляющего денежное выражение в наименьшей денежной единице (например, 100 центов для хранения 1,00 доллара или 100 для хранения 100 ¥ (японская иена, валюта с нулевым десятичным числом). Это то, что делает Stripe - один самые важные компании, предоставляющие финансовые услуги для глобальной электронной коммерции.

Это не прямой ответ, а пример того, почемуfloatне лучший тип данных для валюты.

Из-за внутреннего представления плавающей запятой она более подвержена ошибкам округления.

В нашей собственной десятичной системе вы получите ошибки округления всякий раз, когда будете делить на что-либо, кроме 2 или 5, которые являются множителями 10. В двоичной системе это только 2, а не 5, поэтому даже «чистые» десятичные дроби, такие как0.2(1/5) находятся в группе риска.

Вы можете увидеть это, если попробуете следующее:

      select
    0.1::float + 0.2::float as floats,          --  0.30000000000000004
    0.1::numeric + 0.2::numeric as numerics     --- 0.3
;

Это то, что сбивает аудиторов с толку.

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

Чтобы определить необходимую точность, необходимо учитывать следующее:

  • Типы валют, которые вы поддерживаете (они могут иметь разное количество знаков после запятой). Криптовалюты имеют до 18 знаков после запятой (ETH). Количество десятичных знаков может меняться со временем из-за инфляции.
  • Хранение цен на небольшие единицы товаров (вероятно, в результате конвертации из другой валюты) или наличие накопителей (накапливание 10% комиссии от транзакций в 1 цент до тех пор, пока сумма не достигнет 1 цента) может потребовать использования большего количества десятичных знаков, чем определено для валюты.
  • Хранение целочисленного количества минимальных единиц может привести к необходимости масштабирования значений в будущем, если вам потребуется изменить точность. Если вы используете десятичные дроби, это намного проще.

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

Подробности и предостережения в статье .

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