Как лучше хранить денежные суммы в Postgres?

Условно говоря, у меня есть товар, цена которого может быть указана в рублях, долларах и биткоинах. Для классических валют было бы удобно использовать рекомендуемый тип numeric/decimal с точностью 2 для копеек/центов. Но условный биткоин требует значительно большей точности (один сатоши = 0.00000001 btc). Таким образом, я стою перед выбором:
1) использовать для хранения денежных сумм numeric с точностью 8 (100,00000000 руб)
2) воспользоваться тем, что PG позволяет вообще не указывать точность для numeric (скорее всего не бесплатно)
3) хранить все денежные суммы в минимальных расчетных единицах, используя тип integer.

В случае с третьим вариантом, если товар, например, стоит 100 рублей, то в колонке с ценой будет указано значение 10000 (умножаем на 100 копеек). При этом в справочнике валют для рублей укажем кратность 100. А для биткоина уже 10 000 000.

Естественно, прежде чем выдавать данные на клиента, нужно будет задействовать функцию, которая преобразует значения в стандартный вид (делим на кратность).

Аналогично, если клиент отправит 0,00001 btc, то перед сохранением в базу нужно будет выполнить "конвертацию" в минимальные расчетные единицы (0,00001*10000000=1000).

Есть ли какие-то преимущества у третьего варианта перед первыми двумя (кроме того, что тип int работает значительно быстрее, чем numeric). Кто-нибудь использовал его в реальных проектах?
  • Вопрос задан
  • 1519 просмотров
Пригласить эксперта
Ответы на вопрос 4
@tukreb
Вот так не нужно
https://wiki.postgresql.org/wiki/Don%27t_Do_This#D...

И всё Postresql умеет в numeric https://www.postgresql.org/docs/current/datatype-n...
up to 131072 digits before the decimal point; up to 16383 digits after the decimal point
Ответ написан
Комментировать
@foterio
Несколько лет назад, я потратил неделю чтобы гарантировано разобраться и принять правильное решение как хранить деньги в Postrgres.
3 вариант оказался единственно верным. Хранить деньги нужно в копейках, центах в виде int. Операции сложения вычитания так же необходимо проводить в копейках, центах. И только при выводе денег для конечного пользователя вы приводите его в читабельный вид $10.99
Ответ написан
@rPman
Главный бонус использования 3 варианта - не будет важна поддержка (т.е. ее отсутствие) чисел с переменной фиксированной точностью на бакэнде. Например в php любой драйвер преобразует числа в double со всеми вытекающими от сюда проблемами.

Недостаток - можно будет попасть на лимиты стандартных целых чисел (для 64-битных систем, например php), например если на tron ты попробуешь так реализовать работу с токеном usdd, то ты обнаружишь что его точность абсурдна - 18 знаков после запятой, и придется использовать соответствующие классы для работы с ним. Если не преобразовать в запросе тип поля в строку, тот же php pdo преобразует слишком большие целые числа в double

p.s. bitcoin lightning network добавили пару значимых знаков, советую об этом тоже подумать
Ответ написан
AgentSmith
@AgentSmith
Это мой правильный ответ на твой вопрос
Но условный биткоин требует значительно большей точности (один сатоши = 0.00000001 btc).

Я у себя все транзакции с битками храню именно в сатоши в типе integer
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы