web-dev-qa-db-ja.com

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

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

8
cpacheco

式から生じる精度とスケールの決定は ラットのネスト であり、特に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
13
Aaron Bertrand

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)
6
Oliver Rahner

この質問に優れた回答が既に追加されているにもかかわらず、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進数値に変換される結果が得られます。これは、decimalhigherであるためですintデータ型よりも優先されます。

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

すべてのことを言っても、実際にはすべての入力が10進数であるため、SELECT @C * (@I * POWER(1 + @I, @N) / (POWER(1 + @I, @N) - 1 ));shouldはおそらく10進数の値を返します。興味深いことに、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));
_

これは次を返します:

enter image description here

howは説明に困惑していますが、明確にそれはです。私のguessは_1E0_関数の_POWER(_(明示的な浮動小数点)であり、SQL ServerはPOWERの出力タイプに異なる選択を強制します関数。 ドキュメントの状態POWER()への最初の入力は浮動小数点数、または暗黙的に指定できる数値であるため、私の推測が正しい場合、POWER関数のバグの可能性を示していますfloatに変換されます。

3
Max Vernon

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
2
Michael Green