SQL Serverは精度/スケールをどのように決定しますか?


8

SQL Server 2012を実行しています

SELECT 
   0.15 * 30 / 360,
   0.15 / 360 * 30 

結果:

 0.012500, 
 0.012480

これは私を混乱させるほどです。

DECLARE @N INT = 360
DECLARE @I DECIMAL(38,26) = 0.15 * 30 / 360     
DECLARE @C DECIMAL(38,26) = 1000000     

SELECT @C *  @I *  POWER(1 + @I, @N)  / ( POWER(1 + @I, @N) - 1 )
SELECT @C * (@I *  POWER(1 + @I, @N)  / ( POWER(1 + @I, @N) - 1 ) )

最初の選択では正しい結果が得られます:12644.44022 2番目の選択では結果が切り捨てられます:12644.00000


1
この回答を参照してください:stackoverflow.com/a/51445919/87015、入力データ型と操作に基づいて予想されるデータ型を決定するサンプルコードが含まれています。
Salman A

回答:


13

式から生じる精度とスケールの決定はネズミの巣であり、特に10進数(またはfloat!)とintを混合する場合、誰もがすべてのシナリオの正確なルールを理解していないと思います。gbnによるこの回答を参照してください。

もちろん、式を調整して、より詳細な明示的な変換を行うことで、必要なものを得ることができます。これはおそらくやり過ぎですが、

SELECT 
   CONVERT(DECIMAL(15,6), CONVERT(DECIMAL(15,6), 0.15) 
   * CONVERT(DECIMAL(15,6), 30) 
   / CONVERT(DECIMAL(15,6), 360)),
   CONVERT(DECIMAL(15,6), CONVERT(DECIMAL(15,6), 0.15) 
   / CONVERT(DECIMAL(15,6), 360) 
   * CONVERT(DECIMAL(15,6), 30));

どちらの結果も、浮動小数点演算が壊れていたり、精度やスケールが大幅に間違っていたりするために誤って丸められることはありません。

0.012500    0.012500

6

Aaron Bertrandが述べたように、式は予測が非常に困難です。

そこに行くなら、次のスニペットを使用して洞察を得ることができます。

DECLARE @number SQL_VARIANT
SELECT @number = 0.15 / 360
SELECT @number
SELECT  
    SQL_VARIANT_PROPERTY(@number, 'BaseType') BaseType,
    SQL_VARIANT_PROPERTY(@number, 'MaxLength') MaxLength,
    SQL_VARIANT_PROPERTY(@number, 'Precision') Precision

これが結果です:

------------
0.000416

(1 row(s) affected)

BaseType     MaxLength    Precision
------------ ------------ ----------
numeric      5            6

(1 row(s) affected)

3

この質問に優れた回答がすでに追加されているにもかかわらず、SQL Serverでのデータ型の変換には、明示的に定義された優先順位があります。

演算子が異なるデータ型の2つの式を組み合わせる場合、データ型の優先順位の規則は、優先順位の低いデータ型を優先順位の高いデータ型に変換することを指定します。変換がサポートされている暗黙の変換ではない場合、エラーが返されます。両方のオペランド式のデータ型が同じ場合、演算の結果はそのデータ型になります。

SQL Serverは、データ型に次の優先順位を使用します。

user-defined data types (highest)
sql_variant
xml
datetimeoffset
datetime2
datetime
smalldatetime
date
time
float
real
decimal
money
smallmoney
bigint
int
smallint
tinyint
bit
ntext
text
image
timestamp
uniqueidentifier
nvarchar (including nvarchar(max) )
nchar
varchar (including varchar(max) )
char
varbinary (including varbinary(max) )
binary (lowest)

したがって、たとえば、SELECT 0.5 * 1(10進数にintを乗算して)結果が10進数値に変換される場合、データ型よりも優先順位decimal高いためですint

詳細については、http://msdn.microsoft.com/en-us/library/ms190309.aspxを参照してください

以上のことをすべて言っても、実際にはすべての入力が10進数であるため、おそらく10進数の値を返すSELECT @C * (@I * POWER(1 + @I, @N) / (POWER(1 + @I, @N) - 1 )); はずです。興味深いことに、それSELECTを次のように変更することにより、正しいように見える結果を強制できます。

DECLARE @N INT = 360;
DECLARE @I DECIMAL(38,26) = 0.15 * 30 / 360;
DECLARE @C DECIMAL(38,26) = 1000000;

SELECT @C *  @I *  POWER(1 + @I, @N)  / (POWER(1 + @I, @N) - 1);
SELECT @C * (@I *  POWER(1 + @I, @N)  / (POWER(1E0 + @I, @N) - 1));

これは次を返します:

ここに画像の説明を入力してください

私が説明するために途方に暮れていますはっきりけれどもそれは、任意の違いはそれがありません。私の推測では、関数の1E0(明示的な浮動小数点)は、POWER(SQL ServerにPOWER関数の出力タイプに対して異なる選択を行わせます。私の仮定が正しい場合、ドキュメントの最初の入力は浮動小数点数、または暗黙的に浮動小数点数に変換できる数値であると記載されているPOWERため、関数のバグの可能性を示しています。POWER()


2
精度、スケール、長さへのリンクも適切です。
ypercubeᵀᴹ

2

SELECT .. INTO 構文に精通していますか?これは、指定されたSELECTリストに適切なデータ型のみを使用してテーブルを即座に作成するため、このような状況を解体するのに役立ちます。

計算を構成ステップに分割し、SQL Serverの優先順位ルールを適用しながら適用して、定義がどのように変化するかを確認できます。最初の例は次のようになります。

use tempdb;

SELECT
   0.15             as a,
   0.15 * 30        as b,
   0.15 * 30 / 360  as c
into #Step1;

select * from #Step1;

select
    c.name,
    t.name,
    c.precision,
    c.scale
from sys.columns as c
inner join sys.types as t
    on t.system_type_id = c.system_type_id
where object_id = object_id('#Step1');

drop table #Step1;

これは出力です:

name    name        precision   scale
a       numeric     2           2
b       numeric     5           2
c       numeric     9           6

1
しかし、これは必ずしも助けにはなりません。(1+1)2はどちらもタイプですintが、この質問には、タイプが異なる結果が生成される例があります。
マーティンスミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.