на decimal?
Как это правильно сделать? Что-то не гуглится. Будто это в принципе не принято делать
> Гайз, есть ли подводные камни при альтере поля с float на decimal? Хмм... могут быть разве что проблемы с точностью (особенно если выбранного NUMERIC(x, y) не хватает для всех исходных значений). Но с float-ами так всегда. > Как это правильно сделать? Что-то не гуглится. Будто это в принципе не принято делать Я бы не ожидал, что для ALTER COLUMN col /* поле типа x */ TYPE y про каждую возможную пару (x,y) кто-то будет писать в интернете. ;)
Спасибо. В принципе с флоат всегда такая фигня с точностью. Зачем-то у нас он оказывается для денег используется. Хочу поменять Ещё задумываюсь может на integer что бы центы хранить? Тогда при смене типа ещё на 100 умножить как-то надо. Есть ли способ без промежуточный таблицы для миграции?
> Ещё задумываюсь может на integer что бы центы хранить? При расчётах всё равно часто получаются / используются доли центов / копеек, так что обычно не стоит. > Есть ли способ без промежуточный таблицы для миграции? Просто ALTER COLUMN col TYPE y, если время есть (когда к этой таблице не будут обращаться). Если нет — всё сложнее, но возможно... тут совсем недавно про это спрашивали, кажется.
Если переходить на integer что бы хранить центы, это придется же не просто альтерить. Там надо на 100 умножить
Это, как раз, несложно: ALTER COLUMN x TYPE bigint USING (100 * x). А вот приложения переписывать (на работу в центах) кто-то будет. ;) > Кстати доли центов не хотим использовать. Ну и ну... А Вы знаете, что в типичных случаях (при достаточных оборотах) Вы так "просеиваете" от единиц до сотен тысяч долларов в год (они просто "исчезают" из расчётов себестоимости, налогов, доходов)? И если есть какие-то контролирующие организации, последствия могут быть ещё более печальными...
не все валюты имеют размен на 100 частей. https://ru.wikipedia.org/wiki/Существующие_разменные_денежные_единицы#Современные_недесятичные_системы_денежного_счисления_и_другие_исключения
> В валюте долей центов не существует как выше сказали. Это же совершенно неважно! Простой пример: нужно заплатить, к примеру, 18% налог на 100 штук товара ценой 1.25$. "Наивный" расчёт в центах даёт: SELECT 100 * (125 * 18 / 100) = 22$ (а правильная сумма — 22.50$). И такие штуки вполне могут накапливаться (см. выше про "типичные" суммы), и кроме прямых потерь (на refund и т.п.), могут быть и косвенные — в виде штрафов, судебных издержек и стоимости работ по исправлению этого кошмара , если эти расчёты как-то законодательно регулируются. > Поэтому хочу сначала продвинуть идею хотя бы на decimal перейти. В большинстве случаев (если нужны правильные расчёты) для денег используется только этот тип данных.
Да, в вашем примере неправильный рассчет, если считать от юнит прайс, потом умножать на количество. Мы от line total считаем что бы хоть как-то убавить такие потери Спасибо за советы. Очень ценны. Понимаю проблемы, думаем как решать так, что бы усилия того стоили
Ну так вот поэтому decimal (он же numeric) и используют в таких случаях — чтоб не думать, как считать, и получать точные результаты. Да и перейти на него в вашем случае проще, как я понял. > Мне очень не нравится поставленная задача перейти на центы А с центами придётся ещё думать, как считать — зачем это нужно? Кстати, см. https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_money (потому что описываемые там проблемы — в основном от подхода "давайте считать в центах").
Спасибо за статью. Пошла читать Кроме явных проблем там переписывать все, что касается денег в online marketplace приложении. Как минимум по времени и новым багам того не стоит
Кстати на счет налогов я немного приврала. Мы сами не считаем. Считает сторонний сервис Avalara. Не рентабельно все типы американских налогов в разных штатах у себя реализовать. Хоть тут ответственность не на разработчиках 🙈
А правильная сумма будет 22, 22.50 или 23, в зависимости от местных традицый, на самом деле. Если речь идёт о Российском НДС, например -- то правильным будет 22 либо 23, на выбор. А 22.50 -- неправильным. При этом прямой рассчёт в NUMERIC -- даст 23, если что: (100::int*(1.25::NUMERIC(10,2)*(18::int/100::float))::NUMERIC(10,2))::NUMERIC(10,2); В общем, я во-первых не вижу заментой разницы между NUMERIC и int. Во-вторых -- какое значение является по-настоящему правильным -- это сложный вопрос, но почти во всём можно его правильно посчитать. Хоть во float (хотя боли будет большэ, хужэ того, начинающий разработчик с этим, скорее всего, не справится).
> А правильная сумма будет 22, 22.50 или 23, в зависимости от местных традицый, на самом деле. Возможно, но при работе с numeric это решает программист, а при работе с центами это решается "как повезёт" за него. > Если речь идёт о Российском НДС, например -- то правильным будет 22 либо 23, на выбор. А 22.50 -- неправильным. Я давно не работал с Российским НДС, но раньше это было точно не так. И последствия "в виде штрафов, судебных издержек и стоимости работ по исправлению этого кошмара" именно по этому поводу я видел своими глазами. Кто-то сходу знает, какие там сейчас требования, может прокомментировать? > При этом прямой рассчёт в NUMERIC -- даст 23, если что: Показанный пример не называется "прямой рассчёт в NUMERIC" — это какая-то каша. > В общем, я во-первых не вижу заментой разницы между NUMERIC и int. А она есть, и была продемонстрирована мной выше. Честное слово, у меня уже складывается ощущение (по результатам предыдущих обсуждений), что если Вам показать результат вроде SELECT 23 = 22.50; → f, Вы и с ним будете способны спорить, чтобы "доказать" свою точку зрения, извините. ;( > Хоть во float (хотя боли будет большэ, хужэ того, начинающий разработчик с этим, скорее всего, не справится). Да-да, конечно. См. вот это https://t.me/pgsql/340458 и удачи в расчётах. Мне помнится, у нас так один (начинающий, правда) разработчик бился головой об клавиатуру две недели, пытаясь найти, куда же пропадают копейки в его отчётах. Зато с тех пор навсегда отучился хранить деньги во float. ;)
>при работе с центами это решается "как повезёт" за него. Нет. Я могу получить любое из этих чисел при работе что с numeric, что с цэнтами, что с float. Более того, работа с numeric и цэнтами отличается очень несильно. >Я давно не работал с Российским НДС, но раньше это было точно не так. На самом деле это очень давно не менялось -- и я заметно упростил. 22 и 23 -- это на 100 различных продаж (в т.ч., например, при составлении ежэмесячной СФ на продажы физ.лицам). Если это одна строчка счёта-фактуры -- то будет 22,50.
> Нет. Я могу получить любое из этих чисел при работе что с numeric, что с цэнтами, что с float. Не можете. Особенно в случае с float... да я просто процитирую документацию (сколько можно уже!): Inexact means that some values cannot be converted exactly to the internal format and are stored as approximations, so that storing and retrieving a value might show slight discrepancies. Managing these errors and how they propagate through calculations is the subject of an entire branch of mathematics and computer science and will not be discussed here, except for the following points: . If you require exact storage and calculations (such as for monetary amounts), use the numeric type instead. <skip> On all currently supported platforms, the real type has a range of around 1E-37 to 1E+37 with a precision of at least 6 decimal digits. (Выделение моё.) Как возможно считать деньги с precision в шесть десятичных цифр, лично мне непонятно. А (см. сообщение ранее) на принципиальную недетерминированность таких расчётов в SQL Вам есть что сказать?
Во-первых, я говорил про float, который здесь double precision, а не real. С real -- действительно всё тяжэлее было бы.
И да, у меня нет проблем с тем, что оно inexact -- если вовремя выкидывать ошыбки при помощи округления. (Например, в perl по сути все числа -- double precision floating point, и ничего. Округлял до двух знаков после запятой тем или иным способом на каждом шаге -- и всё работало).
Да-да, конечно. При чём тут императивный perl (в котором можно точно задать последовательность вычислений)?! Мы SQL тут обсуждаем или что-то "левое"?
Я и в SQL могу задать последовательность вычислений. Если надо. Или сделать вычисления, которые не зависят от их последовательности (округление до копеек именно это и делало, если что).
Это уже [немного] лучше, но Вы же сами писали про максимальные размеры сумм, я правильно помню?
Их надо помнить, да. И ставить лимиты, и делать что-то ещё когда к ним подходишь. Я нигде не утверждал, что будет легко.
> Я и в SQL могу задать последовательность вычислений. Если надо. "Закат солнца вручную" (и не факт, что каждый раз сделаете, кстати). А вот с numeric ничего задавать не надо — оно просто работает. > округление до копеек именно это и делало, если что Не делало (особенности поведения типов от этого не меняются). Если что-то "считается" специальным образом ("подгоняется" под правила расчётов, которые не соответствуют математике) — это совсем другое дело. И, опять-таки, см. про фундамент.
А я (и не только я, см. хоть документацию, хоть... да что угодно, в принципе), как раз, утверждаю, что c numeric хотя бы с базовыми вещами мучиться не нужно, поэтому лучше бы использовать этот тип для подобных задач, вот и всё.
Зато numeric достаточно спецыфичен для SQL, хужэ того, то на что вы рассчитываете — вообще для postgres, а int64 есть везде.
> Зато numeric достаточно спецыфичен для SQL Эээ... сейчас не 1990-ые, и arbitrary precision types в современных языках общего назначения сильно прибавилось. > то на что вы рассчитываете — вообще для postgres В других СУБД, насколько мне известно, (constrained) numerics / decimals работают ровно так же. К тому же, кого волнует, что там в других СУБД? ;)
Как минимум в java есть BigDecimal, который нативно маппится с numeric в обе стороны
Например, вы предлагали сначала unconstrained. Кроме того, например, правила вычисления scale у оракла я так и не понял... Но рассчитывать на то, что оно просто работает — не приходится.
> Например, вы предлагали сначала unconstrained. Хмм... разве? Тем не менее, для работы с деньгами в PostgreSQL хватит и их. > правила вычисления scale у оракла я так и не понял... И, опять-таки, кого это волнует? Это проблемы тех, кто собирается использовать COBOL Oracle. ;) Кстати, можно поискать, какие конкретные типы данных используются для денег в БД "больших" серийных ERP и т.п. систем. > Просто скобки в правильном порядке расставить. Может, с бубном ещё сплясать? От радости работы с системами, где от перемены мест слагаемых меняется сумма надо тщательно выбирать, какое из математически эквивалентных выражений работает так, как нужно... > Возьмём тогда типичную ставку 0.1(6) Я не понял, какую (и где она типична).
Ставку в долях ноль запятая один и шэсть в периоде. Это сейчас российский НДС, если считать его от цэны товара.
И да, сплясать — обязательно. Это налоговый кодэкс, без плясок тут вообще никак. А выбирать — считать налог по каждой позицыи и это суммировать либо суммировать и считать налог — тожэ обязательно. Как раз попытки игнорировать вопрос — как оно считается правильно — и, например, считать всегда с запредельной точностью, и приводят к результатам, не подтверждающимся документами.
> Это сейчас российский НДС, если считать его от цэны товара. А, 20/120 (выделение НДС / "обратная ставка"), ясно. Тем не менее, где-то что-то должно быть написано про требования к вычислениям и округлению — от них и отталкиваются. > И да, сплясать — обязательно. Это налоговый кодэкс, без плясок тут вообще никак. Не надо путать "пляски" с самими вычислениями (которые лежат в фундаменте расчётов) и "пляски" с тем, чтобы "подогнать" результаты под налоговый кодекс. > Как раз попытки игнорировать вопрос Я тут ничего не игнорирую, это Вы пытаетесь мешать мух (проблемы самих типов данных) с котлетами (задачей считать так, чтобы это соответствовало чьим-то требованиям).
Кстати, в COBOL как раз вполне вменяемый decimal и управляемое округление.
Обсуждают сегодня