Условно говоря, у меня есть товар, цена которого может быть указана в рублях, долларах и биткоинах. Для классических валют было бы удобно использовать рекомендуемый тип 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). Кто-нибудь использовал его в реальных проектах?
Несколько лет назад, я потратил неделю чтобы гарантировано разобраться и принять правильное решение как хранить деньги в Postrgres.
3 вариант оказался единственно верным. Хранить деньги нужно в копейках, центах в виде int. Операции сложения вычитания так же необходимо проводить в копейках, центах. И только при выводе денег для конечного пользователя вы приводите его в читабельный вид $10.99
Тоже пока выбрал этот вариант. Отпишусь, если будут проблемы. Пока вижу сложность лишь в том, что при делении сумм доли копеек всегда будут отбрасываться. Например, 23 копейки, деленные на 3, вернут по 7 копеек: SELECT 23/3;
Получается, что 2 копейки стали доходом от деления, их нужно как-то учитывать... Можно снизить этот эффект, если увеличиться кратность расчетных единиц, но проблема с излишками все равно останется. Как вы это решали?
romaro, Идеального решения еще никто не нашел) Банки тоже страдают из-за этих потерь при округлении, ведь курсы то валюты к примеру 76,8765, а вам в приложении показывают 76,88.
Мы с бизнесом после некоторых переговоров пришли к следующей модели, да и теперь везде ее использую в работе:
1. При закупке товаров, мы округляем в меньшую сторону в ущерб себе, но зато не создаем мнимую экономию. Округляем в пользу поставщика.
2. При продаже товаров мы округляем в большую сторону уже в свою пользу, а не в пользу клиента ;)
В зависимости от вашей бизнес модели вы тоже можете применить что-то подобное, просто нужно переговорить с экономистами и аналитиками.
Главный бонус использования 3 варианта - не будет важна поддержка (т.е. ее отсутствие) чисел с переменной фиксированной точностью на бакэнде. Например в php любой драйвер преобразует числа в double со всеми вытекающими от сюда проблемами.
Недостаток - можно будет попасть на лимиты стандартных целых чисел (для 64-битных систем, например php), например если на tron ты попробуешь так реализовать работу с токеном usdd, то ты обнаружишь что его точность абсурдна - 18 знаков после запятой, и придется использовать соответствующие классы для работы с ним. Если не преобразовать в запросе тип поля в строку, тот же php pdo преобразует слишком большие целые числа в double
p.s. bitcoin lightning network добавили пару значимых знаков, советую об этом тоже подумать