Какой тип данных я должен использовать для хранения денежных значений? - PullRequest
5 голосов
/ 31 августа 2009

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

Что мне использовать?

Ответы [ 5 ]

13 голосов
/ 31 августа 2009

Papuccino,

Я не рекомендую типы money и smallmoney, если вы не уверены, что единственная арифметика, которую вы планируете делать, это сложение и вычитание. Если вы можете иметь дело с обменными курсами, процентами и т. Д., Вы рискуете реальными проблемами с этими типами.

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

declare @m1 money, @m2 money, @m3 money
declare @d1 decimal(19,4), @d2 decimal(19,4), @d3 decimal(19,4)
declare @f1 float, @f2 float, @f3 float;
set @m1 = 1.00;
set @m2 = 345.00;
set @m3 = @m1/@m2;
set @d1 = 1.00;
set @d2 = 345.00;
set @d3 = @d1/@d2;
set @f1 = 1.00;
set @f2 = 345.00;
set @f3 = @f1/@f2;
select @m3, @d3, @f3;

Результат: 0,0028 0,0029 0,00289855072463768

В зависимости от отрасли, могут быть рекомендации или нормативные акты, которые помогут вам выбрать правильный тип данных. Правильного ответа не существует.

Добавлены замечания:

Вы правы в том, что деньги / деньги не должны быть деньгами, но SQL Server (необъяснимо) дает именно такой результат: введите деньги из отношения двух денежных значений. Это фальшивка, но, как вы видите из приведенного ниже примера, это то, что вы получаете, даже если это не имеет смысла:

declare @m1 money, @m2 money;
declare @d1 decimal(19,4), @d2 decimal(19,4);
set @m1 = 1.00;
set @m2 = 345.00;
set @d1 = 1.00;
set @d2 = 345.00;
select @m1/@m2, @d1/@d2

Результат: 0,0028 0,0028985507246376811

Результат с типом money 0.0028 на 3-4% меньше правильного результата.

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

Предположим, вы обменяете 320 юаней, и банк дает вам 47,3 долларов США. Какой обменный курс вам дали?

Предположим, вы инвестируете 23 доллара, а год спустя это стоит 31 доллар. Какова ваша процентная норма прибыли?

Оба эти вычисления требуют деления валютных значений.

12 голосов
/ 31 августа 2009

Нет, деньги все еще должны работать.

2 голосов
/ 31 августа 2009

Почему деньги должны быть устаревшими? Он превышает 900 триллионов, сотни раз превышают бюджет федерального правительства - какие суммы денег вам, возможно, понадобится хранить? -) (Я полагаю, что, возможно, зимбабвийских долларов в конечном итоге могло стать проблемой , но они продолжали сбрасывать его на несколько миллиардов и триллионов, и в прошлом апреле он был окончательно приостановлен; теперь они используют доллары США или другую иностранную валюту для платежей и учета в Зимбабве).

1 голос
/ 06 ноября 2009

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

0 голосов
/ 01 марта 2017

Меня удивляет, что никто не упоминал об этом раньше.

money составляет 8 байтов. decimal - это 5, 9, 13 или 17 байт в зависимости от точности (точность - это не количество десятичных цифр после десятичной точки, это максимальное общее количество десятичных цифр, которое будет сохранено, оба слева и справа от десятичной точки). Итак, чтобы имитировать диапазон значений, которые поддерживает money (от -922,337,203,685,477.5808 до 922,337,203,685,477.5807), вам нужно decimal(19,4).

+-------------------+---------------+
| decimal precision | Storage bytes |
+-------------------+---------------+
| 1 - 9             |             5 |
| 10-19             |             9 |
| 20-28             |            13 |
| 29-38             |            17 |
+-------------------+---------------+

Если хранение 9 байтов вместо 8 не кажется большой проблемой, вам следует помнить, что money - это собственный тип процессора, такой как 64-битный bigint, но decimal - нет. Это означает, что суммирование миллиардов money значений будет быстрее, чем суммирование decimal значений. Выполнение других вычислений, таких как деление, будет иметь еще большую разницу.

На моей виртуальной машине с SQL Server 2014 Express я выполнил этот простой тест. dbo.Numbers - это таблица с 10000 строк с одним int столбцом Number со значениями от 1 до 10000.

CREATE TABLE [dbo].[Numbers](
    [Number] [int] NOT NULL,
CONSTRAINT [PK_Numbers] PRIMARY KEY CLUSTERED 
(
    [Number] ASC
))

Я запустил это в SQL Sentry Plan Explorer:

DECLARE @VarM money = 1234.5678;

SELECT
    AVG(@VarM / N1.Number)
FROM 
    dbo.Numbers AS N1
    CROSS JOIN dbo.Numbers AS N2
;


DECLARE @VarD decimal(19,4) = 1234.5678;

SELECT
    AVG(@VarD / N1.Number)
FROM 
    dbo.Numbers AS N1
    CROSS JOIN dbo.Numbers AS N2
;

План выполнения одинаков для обоих запросов (ну, существуют разные неявные преобразования Number: в money и decimal), но время выполнения составляет 15 секунд против 40 секунд. Это довольно заметно для меня.

money vs decimal


Конечно, вам нужно знать, что money имеет только 4 знака после запятой. Если вы выполняете вычисления, вам необходимо знать типы (и их приоритет, то есть то, что неявно преобразуется во что) и убедиться, что промежуточные результаты имеют соответствующий тип, приведя операнды в надлежащие типы, если это необходимо. Это предупреждение относится к вычислениям любых типов, а не только к money. Когда вы делите два значения int, вы должны знать, что результат равен int, и не удивляться, что 4 / 5 = 0.

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