Стоит ли выбирать типы данных MONEY или DECIMAL (x, y) в SQL Server? - PullRequest
395 голосов
/ 24 февраля 2009

Мне любопытно, существует ли реальная разница между типом данных money и чем-то вроде decimal(19,4) (я полагаю, что именно это используется внутри страны).

Я знаю, что money относится к SQL Server. Я хочу знать, есть ли веская причина выбрать одно из другого; большинство примеров SQL Server (например, база данных AdventureWorks) используют money, а не decimal для таких вещей, как информация о цене.

Должен ли я просто продолжать использовать тип данных money, или вместо него есть преимущество использования десятичного числа? Деньги набирают меньше символов, но это не является веской причиной:)

Ответы [ 12 ]

289 голосов
/ 24 февраля 2009

Никогда не пользуйся деньгами. Это не точно, и это чистый мусор; всегда используйте десятичную / числовую.

Запустите это, чтобы увидеть, что я имею в виду:

DECLARE
    @mon1 MONEY,
    @mon2 MONEY,
    @mon3 MONEY,
    @mon4 MONEY,
    @num1 DECIMAL(19,4),
    @num2 DECIMAL(19,4),
    @num3 DECIMAL(19,4),
    @num4 DECIMAL(19,4)

    SELECT
    @mon1 = 100, @mon2 = 339, @mon3 = 10000,
    @num1 = 100, @num2 = 339, @num3 = 10000

    SET @mon4 = @mon1/@mon2*@mon3
    SET @num4 = @num1/@num2*@num3

    SELECT @mon4 AS moneyresult,
    @num4 AS numericresult

Вывод: 2949,0000 2949,8525

Некоторым из людей, которые сказали, что вы не делите деньги на деньги:

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

select t1.index_id,t2.index_id,(avg(t1.monret*t2.monret)
    -(avg(t1.monret) * avg(t2.monret)))
            /((sqrt(avg(square(t1.monret)) - square(avg(t1.monret))))
            *(sqrt(avg(square(t2.monret)) - square(avg(t2.monret))))),
current_timestamp,@MaxDate
            from Table1 t1  join Table1 t2  on t1.Date = traDate
            group by t1.index_id,t2.index_id
259 голосов
/ 24 февраля 2009

SQLMenace говорит, что деньги неточны. Но вы не умножаете / делите деньги на деньги! Сколько стоит 3 доллара 50 центов? 150 долларовых центов? Вы умножаете / делите деньги на скаляры, которые должны быть десятичными.

DECLARE
@mon1 MONEY,
@mon4 MONEY,
@num1 DECIMAL(19,4),
@num2 DECIMAL(19,4),
@num3 DECIMAL(19,4),
@num4 DECIMAL(19,4)

SELECT
@mon1 = 100,
@num1 = 100, @num2 = 339, @num3 = 10000

SET @mon4 = @mon1/@num2*@num3
SET @num4 = @num1/@num2*@num3

SELECT @mon4 AS moneyresult,
@num4 AS numericresult

Результат в правильном результате:

moneyresult           numericresult
--------------------- ---------------------------------------
2949.8525             2949.8525

money хорошо, если вам не нужно больше 4 десятичных цифр, и вы убедитесь, что ваши скаляры - которые не представляют деньги - decimal с.

55 голосов
/ 26 февраля 2013

Все опасно, если вы не знаете, что делаете

Даже десятичные типы высокой точности не могут спасти день:

declare @num1 numeric(38,22)
declare @num2 numeric(38,22)
set @num1 = .0000006
set @num2 = 1.0
select @num1 * @num2 * 1000000

1,000000 <- должно быть 0,6000000 </p>


Типы money являются целыми числами

Текстовые представления smallmoney и decimal(10,4) могут выглядеть одинаково, но это не делает их взаимозаменяемыми. Вы передергиваетесь, когда видите даты, хранящиеся как varchar(10)? Это то же самое.

За кулисами money / smallmoney - это всего лишь bigint / int. Десятичная точка в текстовом представлении money - это визуальный пух, как черты в гггг-мм-дд Дата. SQL на самом деле не хранит их внутри.

Относительно decimal против money, выберите все, что подходит для ваших нужд. Существуют типы money, поскольку хранение учетных значений в виде целых чисел, кратных 1/10000-й единице, очень распространено. Кроме того, если вы имеете дело с реальными деньгами и вычислениями, помимо простого сложения и вычитания, вам не следует делать это на уровне базы данных! Делайте это на уровне приложения с библиотекой, которая поддерживает Banker's Округление (IEEE 754)

40 голосов
/ 13 августа 2012

Я понимаю, что WayneM заявил, что он знает, что деньги являются специфическими для SQL Server. Тем не менее, он спрашивает, есть ли какие-либо причины использовать деньги сверх десятичной или наоборот, и я думаю, что еще нужно указать одну очевидную причину, а именно использование десятичной дроби означает, что беспокоиться о том, стоит ли вам когда-либо менять свою СУБД, - это еще один повод для беспокойства. - что может случиться.

Сделайте ваши системы максимально гибкими!

31 голосов
/ 29 апреля 2013

Ну, мне нравится MONEY! Это на байт дешевле, чем DECIMAL, и вычисления выполняются быстрее, потому что (под прикрытием) операции сложения и вычитания по сути являются целочисленными операциями. Пример @ SQLMenace & mdash; который является отличным предупреждением для незнающих & ndash; также может быть применен к INT egers, где результат будет нулевым. Но это не причина, чтобы не использовать целые числа & mdash; , где это уместно .

Таким образом, совершенно «безопасно» и целесообразно использовать MONEY, когда вы имеете дело с MONEY и использовать его в соответствии с математическими правилами, которые следует (то же, что INT eger).

Было бы лучше, если бы SQL Server способствовал делению и умножению MONEY на DECIMAL s (или FLOAT s?) - возможно, но они не решили этого делать; и при этом они не решили повысить INT эгеров до FLOAT s при их разделении.

MONEY не имеет проблем с точностью; то, что DECIMAL получит больший промежуточный тип, используемый во время вычислений, является просто «особенностью» использования этого типа (и я не уверен, насколько далеко эта «особенность» распространяется).

Чтобы ответить на конкретный вопрос, «веская причина»? Что ж, если вам нужна абсолютная максимальная производительность в SUM(x), где x может быть либо DECIMAL, либо MONEY, тогда MONEY будет иметь преимущество.

Кроме того, не забывайте, что это двоюродный брат меньшего размера, SMALLMONEY - всего 4 байта, но он достигает максимума при 214,748.3647 - что довольно мало для денег - и поэтому не всегда подходит.

Чтобы доказать смысл использования промежуточных типов большего размера, если вы явно присваиваете промежуточное значение переменной, DECIMAL сталкивается с той же проблемой:

declare @a decimal(19,4)
declare @b decimal(19,4)
declare @c decimal(19,4)
declare @d decimal(19,4)

select @a = 100, @b = 339, @c = 10000

set @d = @a/@b

set @d = @d*@c

select @d

Производит 2950.0000 (хорошо, поэтому по крайней мере DECIMAL округлено, а не MONEY усечено - тоже самое, что и целое число.)

12 голосов
/ 13 августа 2015

В качестве контрапункта общей направленности других ответов. См. Множество преимуществ денег… Тип данных! в Руководство SQLCAT по реляционному движку

В частности, я бы указал на следующее

Работая над реализацией клиента, мы нашли несколько интересных показатели производительности относительно типа данных денег. Например, когда Службам аналитики был задан тип данных валюты (от двойного) до соответствует типу данных денег SQL Server, улучшение составило 13% скорость обработки (строк / сек). Чтобы повысить производительность в SQL Службы интеграции серверов (SSIS) для загрузки 1,18 ТБ в возрасте до тридцати минут, как отмечалось в SSIS 2008 - мировой рекорд производительности ETL, это было заметил, что меняются четыре десятичных (9,2) столбца размером 5 байт в таблице TPC-H LINEITEM к деньгам (8 байт) скорость вставки на 20% ... Причиной повышения производительности является протокол SQL Server Tabular Data Stream (TDS), который имеет ключевой принцип проектирования для передачи данных в компактной двоичной форме и как можно ближе к внутренней памяти формат SQL Server. Опытным путем это наблюдалось во время SSIS 2008 - мирового теста производительности ETL с использованием Kernrate; Протокол значительно упал, когда тип данных был переведен на деньги с десятичной. Это делает передача данных максимально эффективно. Сложный тип данных требует дополнительного анализа и циклов ЦП для обработки, чем тип фиксированной ширины.

Так что ответ на вопрос "это зависит". Вы должны быть более осторожными с некоторыми арифметическими операциями, чтобы сохранить точность, но вы можете обнаружить, что соображения производительности делают это полезным.

10 голосов
/ 25 октября 2011

Мы только что столкнулись с очень похожей проблемой, и теперь я очень +1 за то, что никогда не использую Деньги, за исключением презентации на высшем уровне. У нас есть несколько таблиц (фактически, ваучер продаж и счет-фактура продаж), каждая из которых содержит одно или несколько полей Money по историческим причинам, и нам необходимо выполнить пропорциональный расчет, чтобы определить, какая часть общего налога на счет-фактуру относится к линия на ваучер продаж. Наш расчет

vat proportion = total invoice vat x (voucher line value / total invoice value)

Это приводит к реальному расчету денег / денег, который вызывает ошибки масштаба в части деления, которая затем умножается до неправильной пропорции НДС. Когда эти значения впоследствии добавляются, мы получаем сумму пропорций НДС, которые не суммируются с общей стоимостью счета. Если бы любое из значений в скобках было десятичным (я собираюсь привести одно из них как таковое), пропорция НДС была бы правильной.

Когда скобок изначально не было, это работало, я думаю, из-за больших значений, это эффективно имитировало более высокую шкалу. Мы добавили скобки, потому что сначала выполнялось умножение, которое в некоторых редких случаях снижало точность вычисления, но теперь это привело к гораздо более распространенной ошибке.

6 голосов
/ 26 сентября 2016

Я хочу дать другое представление о ДЕНЬГАХ и НУМЕРИЧЕСКИХ, в значительной степени основанных на моем собственном опыте и опыте ... Моя точка зрения здесь - ДЕНЬГИ, потому что я работал с ним в течение достаточно долгого времени и никогда по-настоящему не использовал NUMERICAL. много ...

ДЕНЬГИ Pro:

  • Собственный тип данных . Он использует собственный тип данных ( integer ) так же, как регистр ЦП (32 или 64-битный), поэтому для вычислений не требуются лишние издержки, поэтому он меньше и быстрее ... ДЕНЬГАМ нужно 8 байтов, а ЦИФРОВЫМ (19, 4) нужно 9 байтов (на 12,5% больше) ...

    ДЕНЬГИ быстрее, если их использовать, как предполагалось (в виде денег). Как быстро? Мой простой SUM тест на 1 миллион данных показывает, что ДЕНЬГИ составляют 275 мс, а ЦИФРОВОЕ 517 мс ... Это почти в два раза быстрее ... Почему тест SUM? См. Pro Point

  • Лучшее за деньги . ДЕНЬГИ лучше всего подходят для хранения денег и выполнения операций, например, в бухгалтерском учете. Один отчет может выполнить миллионы сложений (SUM) и несколько умножений после выполнения операции SUM. Для очень больших бухгалтерских приложений это почти в два раза быстрее , и это чрезвычайно важно ...
  • Низкая точность денег . Деньги в реальной жизни не должны быть очень точными. Я имею в виду, что многие люди могут заботиться о 1 центе, а как насчет 0,01 цента? На самом деле, в моей стране банки больше не заботятся о центах (цифры после десятичной запятой); Я не знаю о банке США или другой стране ...

ДЕНЬГИ Con:

  • Ограниченная точность . ДЕНЬГИ имеют только четыре цифры (после запятой), поэтому их необходимо преобразовать перед выполнением таких операций, как деление ... Но опять же, money не обязательно должен быть настолько точным и предназначен для использования в качестве денег, не просто число ...

Но ... Большой, но здесь даже ваше приложение связано с реальными деньгами, но не используйте его во многих операциях SUM, например, в бухгалтерском учете. Если вместо этого вы используете множество делений и умножений, вам не следует использовать ДЕНЬГИ ...

2 голосов
/ 13 августа 2015

Вы не должны использовать деньги, когда вам нужно сделать умножения / деления на значение. Деньги хранятся так же, как целое число, тогда как десятичная дробь хранится в виде десятичной точки и десятичных цифр. Это означает, что в большинстве случаев деньги будут снижать точность, тогда как десятичная дробь будет делать это только при преобразовании обратно в исходный масштаб. Деньги - это фиксированная точка, поэтому ее масштаб не изменяется при расчетах. Однако, поскольку это фиксированная точка, когда она печатается в виде десятичной строки (в отличие от фиксированной позиции в строке с основанием 2), значения до масштаба 4 представляются точно. Так что за сложение и вычитание денег вполне подойдет.

Десятичная дробь представлена ​​в основании 10 внутри, и, таким образом, положение десятичной точки также основано на числе 10. Что делает его дробную часть точно отражающей его стоимость, точно так же как с деньгами. Разница в том, что промежуточные значения десятичной дроби могут поддерживать точность до 38 цифр.

При использовании числа с плавающей запятой значение сохраняется в двоичном виде, как если бы оно было целым числом, а позиция десятичной (или двоичной, ага) точки относительно битов, представляющих число. Поскольку это двоичная десятичная точка, базовые числа 10 теряют точность сразу после десятичной точки. 1/5 или 0,2 не могут быть представлены именно таким образом. Ни деньги, ни десятичные числа не страдают от этого ограничения.

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

Из моего POV я хочу, чтобы вещи, которые случаются с числами, просто происходили без необходимости слишком много думать о них. Если все вычисления будут преобразованы в десятичную, то для меня я бы просто хотел использовать десятичную. Я бы сохранил поле денег для показа.

По размеру я не вижу достаточной разницы, чтобы передумать. Деньги занимают 4 - 8 байтов, тогда как десятичные могут быть 5, 9, 13 и 17. 9 байтов могут охватывать весь диапазон, который могут 8 байтов денег. По индексу (сравнение и поиск должны быть сопоставимы).

1 голос
/ 21 января 2014

Я нашел причину использования десятичной запятой в теме точности.

DECLARE @dOne   DECIMAL(19,4),
        @dThree DECIMAL(19,4),
        @mOne   MONEY,
        @mThree MONEY,
        @fOne   FLOAT,
        @fThree FLOAT

 SELECT @dOne   = 1,
        @dThree = 3,    
        @mOne   = 1,
        @mThree = 3,    
        @fOne   = 1,
        @fThree = 3

 SELECT (@dOne/@dThree)*@dThree AS DecimalResult,
        (@mOne/@mThree)*@mThree AS MoneyResult,
        (@fOne/@fThree)*@fThree AS FloatResult

Просто проверь и прими решение.

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