web-dev-qa-db-ja.com

SQL ServerでMONEYまたはDECIMAL(x、y)データ型を選択しますか?

私はmoneyデータ型とdecimal(19,4)のようなものの間に本当の違いがあるかどうかについて興味があります(それはお金が内部的に使うものです、と私は思います)。

私はmoneyがSQL Serverに特有であることを知っています。一方を選択する説得力のある理由があるかどうかを知りたいです。ほとんどのSQL Serverサンプル(AdventureWorksデータベースなど)は、価格情報などのためにmoneyではなくdecimalを使用します。

単にmoneyデータ型を使い続けるべきでしょうか、それとも代わりにdecimalを使うことに利点がありますか?お金は入力する文字数が少ないですが、それは正当な理由ではありません:)

395
Wayne Molina

あなたはお金を使うべきではありません。それは正確ではありません、そしてそれは純粋なゴミです。常に10進数/数値を使用してください。

これを実行して、私の言っていることを確認してください。

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
289
SQLMenace

SQLMenaceはお金が不正確だと言った。しかし、あなたはお金をお金で割ったり割ったりしないでください! 3ドル×50セントはいくらですか。 150ドル?お金をスカラーで乗算/除算します。スカラーは10進数でなければなりません。

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であることを確認してください。

260
configurator

自分のしていることがわからなければすべてが危険です

高精度の10進数型でもその日を節約することはできません。

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であるべき


money型は整数です

smallmoneydecimal(10,4)のテキスト表現は似ているかもしれませんが、それはそれらを交換可能にしません。日付がvarchar(10)として保存されているのを見たときに、あなたはがっかりしていますか?これは同じことです。

舞台裏では、money/smallmoneyは単なるbigint/intです。moneyのテキスト表現の小数点は、yyyy-mm-ddの日付のダッシュのように視覚的にはっきりしたものです。 SQLは実際にはそれらを内部に格納しません。

decimalmoneyに関しては、ニーズに合ったものを選んでください。 money型が存在するのは、アカウンティング値を単位の10000分の1の整数倍として格納するのが非常に一般的だからです。また、単純な足し算と引き算を超えて実際のお金と計算を扱うのであれば、データベースレベルでそれを行うべきではありません!アプリケーションレベルでそれを行うライブラリBanker's Rounding(IEEE 754)をサポート

55
Anon

私は、WayneMが、お金はSQL Serverに固有のものであることを知っていると述べたことを理解しています。しかし、彼は10進以上のお金を使う理由があるかどうかを尋ねています、そして私は1つの明白な理由がまだ述べられるべきであると思います - それは起こり得る。

システムをできるだけ柔軟にしてください。

40
Dean

まあ、私はMONEYname__が好きです!これはDECIMALname__より1バイト安価であり、(隠れて)加算および減算演算は本質的に整数演算であるため、計算はより高速に実行されます。 @SQLMenaceの例(これは気付かない人にとってはすばらしい警告です)は、結果がゼロになるINTname__egersにも同様に適用できます。しかし、整数を使用しない理由はありません。-適切な場合

ですから、あなたが扱っているものがMONEYname__であるときにMONEYname__を使用し、それに続く数学的ルールに従って使用するのは完全に「安全」で適切です(INTname__egerと同じ)。

SQL ServerがMONEYname__のDECIMALname__s(またはFLOATname__s?)への除算と乗算を促進した場合、それは良かったのでしょうが、おそらくそうしなかったでしょう。また、それらを分割するときにINTname__egersをFLOATname__sにプロモートすることも選択しませんでした。

MONEYname__には精度の問題はありません。 DECIMALname__sが計算中に使用されるより大きな中間型を持つようになるということは、その型を使用することの「機能」にすぎません(その「機能」がどこまで拡張されるかは実際にはわかりません)。

特定の質問、「説得力のある理由」に答えるには?まあ、xname__がDECIMALname__またはMONEYname__のいずれかである可能性があるSUM(x)で絶対的な最大パフォーマンスが必要な場合、MONEYname__にはEdgeがあります。

また、小さいいとこ、SMALLMONEY—わずか4バイトであることを忘れないでください。ただし、214,748.3647で最大になります。

より大きな中間型を使用してそのポイントを証明するために、中間体を変数に明示的に割り当てた場合、DECIMALname__でも同じ問題が発生します。

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を生成します(少なくとも、DECIMALname__が切り捨てられるのではなく、MONEYname__が丸められます。整数と同じです)。

31
dsz

他の答えの一般的な推力へのカウンターポイントとして。 SQLCATのリレーショナルエンジンガイドお金の多くの利点…データタイプ!を参照してください。

具体的には次のように指摘します。

顧客の実装に取り​​組んでいると、moneyデータ型に関して興味深いパフォーマンス値がいくつか見つかりました。たとえば、Analysis ServicesをSQL Serverのmoneyデータ型と一致するようにcurrencyデータ型(doubleから)に設定した場合、処理速度(行/秒)は13%向上しました。 SSIS 2008 - 世界記録のETLパフォーマンスに記載されているように、30分以内に1.18 TBをロードするためのSQL Server Integration Services(SSIS)内でのパフォーマンスの向上TPC-H LINEITEMテーブルの5バイトの列のサイズがmoney(8バイト)になると、バルク挿入速度が20%向上しました。これは、コンパクトなバイナリ形式で、SQL Serverの内部記憶形式にできるだけ近い形でデータを転送するための重要な設計原則です。経験的に、これはSSIS 2008 - Kernrateを使用した世界記録ETLパフォーマンステストの間に観察されました。データ型が10進数からmoneyに切り替えられると、プロトコルは大幅に低下しました。これにより、データ転送が可能な限り効率的になります。複雑なデータ型は、固定幅型よりも処理に追加の解析とCPUサイクルが必要です。

それで、質問への答えは「それは依存します」です。精度を維持するためには、特定の算術演算にもっと注意を払う必要がありますが、パフォーマンスを考慮するとこれが価値があることがわかります。

12
Martin Smith

私たちは非常によく似た問題に遭遇したところで、今ではトップレベルのプレゼンテーションを除いてMoneyを決して使わないことに対して非常に+1しています。それぞれに履歴上の理由で1つ以上のMoneyフィールドが含まれる複数のテーブル(事実上、売上伝票と売上請求書)があります。合計請求額のうち、税のどれだけがそれぞれに関連するかを計算する必要があります。販売伝票の行私たちの計算は

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

これにより、実社会の貨幣/貨幣の計算が行われ、その結果、分割部分にスケールエラーが発生し、それが誤ったバットの比率にまで増加します。これらの値が後で追加されると、合計請求額に加算されない付加価値税比率の合計になります。角括弧内の値のいずれかが10進数であれば(そのような値の1つをキャストしようとしています)、付加価値税率は正しいでしょう。

括弧が元々機能していなかったときは、値が大きいため効果的に高いスケールをシミュレートしていたと思います。括弧を追加したのは、最初に乗算を行っていたためです。これはまれに計算に使用できる精度を落とすことがありましたが、現在ではこれがはるかに一般的なエラーの原因となっています。

10
Kaideejee

私は自分の専門知識と経験に基づいて、金額と数値の見方を変えたいと思います。ここでの私の視点は金額です。これはかなり長い間働いてきたので、実際に数値をあまり使わなかったからです。 。

お金プロ:

  • ネイティブデータ型。これは、CPUレジスタ(32または64ビット)と同じネイティブデータ型(integer)を使用するので、計算に不要なオーバーヘッドが不要なので小さいおよび速い... MONEYには8バイトが必要で、NUMERICAL(19、4) )9バイト(12.5%大きい)が必要です...

    それはそれが(お金として)であることを意味していたために使用されている限りお金が速いです。どのくらい速いのか? 100万データに対する私の単純なSUMテストは、MONEYが275ミリ秒、NUMERICが517ミリ秒であることを示しています...それはほぼ2倍の速さです...なぜSUMテスト?次を参照してくださいPropoint

  • Best for Money。 MONEYは、経理などでお金を貯めて操作を行うのに最適です。 SUM操作の実行後、単一のレポートで何百万もの加算(SUM)と数回の乗算を実行できます。非常に大きな会計アプリケーションでは、のほぼ2倍の速さであるであり、非常に重要です...
  • 低精度のお金。現実のお金はそれほど正確である必要はありません。私は、多くの人が1セント米ドルを気にするかもしれませんが、0.01セント米ドルはどうですか?実際、私の国では、銀行はセントを気にする必要がなくなりました(小数点以下の桁数は10進数)。私はアメリカの銀行や他の国について知りません...

お金コン:

  • 限られた精度。 MONEYは4桁(カンマの後)の精度しかないため、除算などの操作を行う前に変換する必要があります。しかし、ここでもやはりmoneyはそれほど正確である必要はなく、単なるお金としてではありません。数...

しかし...大きいですが、ここでもあなたのアプリケーションにリアルマネーが関係していますが、会計のようにSUM操作の多くでそれを使用しないでください。あなたが代わりにたくさんの除算と乗算を使うなら、あなたはお金を使うべきではありません...

6
Susilo

値を掛け算/除算する必要があるときは、お金を使うべきではありません。 Moneyは整数と同じ方法で格納されますが、decimalは小数点と小数桁数として格納されます。これは、ほとんどの場合お金が正確さを落とすことを意味しますが、10進数は元のスケールに戻されたときにのみ正確さを落とします。お金は固定小数点なので、計算中にその規模は変わりません。ただし、(2進文字列の固定位置としてではなく)10進数文字列として印刷される場合は固定小数点であるため、4の位取りまでの値は正確に表現されます。だから足し算と引き算では、お金は大丈夫です。

10進数は10進数で内部的に表されるため、小数点の位置も10進数に基づいています。これは、お金の場合と同様に、小数部分が正確にその値を表すようにします。違いは、decimalの中間値は38桁までの精度を維持できるということです。

浮動小数点数では、値はまるでそれが整数であるかのように2進数で格納され、10進数(または2進数、ahem)の点の位置は数を表すビットに対する相対位置です。これは2進小数点であるため、10進数は小数点の直後の精度を失います。 1/5、つまり0.2は、この方法では正確に表現できません。お金も小数もこの制限を受けません。

お金を10進数に変換し、計算を実行し、その結果得られた値をMoneyのフィールドまたは変数に格納するのは簡単です。

私のPOVから、あまり考えないで数字に起こることがちょうど起こるようにしたいです。すべての計算が10進数に変換されるのであれば、私には10進数を使用したいと思います。私はお金のフィールドを表示目的で保存します。

サイズ的には、私の考えを変えるほどの違いはありません。 Moneyは4 - 8バイトを取りますが、decimalは5、9、13、および17にすることができます。9バイトは、8バイトのお金が可能な範囲全体をカバーできます。索引に関して(比較と検索は比較可能であるべきです)。

2
Gerard ONeill

精度の問題で、お金よりも小数を使用する理由が見つかりました。

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

それをテストして決定を下すだけです。

1
QMaster

私はこのブログ記事を見たところです。SQL ServerにおけるMoney vs. Decimal

これは基本的にお金には正確さの問題があると言っています...

declare @m money
declare @d decimal(9,2)

set @m = 19.34
set @d = 19.34

select (@m/1000)*1000
select (@d/1000)*1000

money型の場合、19.34ではなく19.30となります。計算のためにお金を1000の部分に分割するアプリケーションシナリオがあるかどうかはわかりませんが、この例ではいくつかの制限があります。

1
Harsha

これまでの投稿はすべて有効な点をもたらしていますが、その一部は正確に答えていません。

問題は、それがあまり正確ではないデータ型であり、複雑な計算に使用されるとエラーを引き起こす可能性があることをすでにわかっているのに、なぜ誰かがお金を好むのはなぜですか?

複雑な計算を行わずにこの精度を他のニーズと交換できる場合は、お金を使います。

たとえば、上記の計算を行う必要がなく、1バイトを節約する必要がある場合(お金には8バイト、10進数(19,4)には9バイトかかります)。

もう1つの例は、上記の計算をする必要がなく、大量のデータを処理するのに速度が必要な場合です。

別の例としては、上記の計算を行う必要がなく、有効な通貨テキスト文字列からインポートする必要がある場合です。他の数値型でこれを試してみてください。SELECT CONVERT(MONEY、 '$ 1,000.68')

0
Weber K.