毎週水曜日を「サポート・サービスディ」とし、障害以外の製品利用技術に関してお客様からのご質問や疑問にお応えする機会を設けました(こちらは無償となります)。相談されたい案件をお持ちのお客様は、以下をご記載の上、ms8spday@microsoft.com 宛に電子メールをお送りください。 なお、弊社までお越しいただくことが難しい場合はオンライン会議での対応も可能ですので、是非ご検討ください。
①サポート契約番号 ②相談事項概要 後程、弊社担当者よりご連絡を差し上げ日程等を調整させて頂きます。 場所:日本マイクロソフト(株) 調布技術センター 対象製品:SQL Server, Internet Information Server, Internet Explorer, Visual Studio, SDK(但し最新バージョンと一世代前のバージョン) 受付期間:随時 サポート・サービスディ:休日を除く水曜日 午前10:00-午後05:00 議題:お客様相談事項 時間:2-3時間の予定 弊社参加者:マネージャ、エンジニア
詳細はこちら
神谷 雅紀 SQL Server Escalation Engineer
numeric/decimal データ型は、有効桁数 (精度) と小数点以下桁数 (スケール) が固定ではないため、演算を行う場合には考慮が必要です。
numeric/decimal の演算を行った場合、その結果の精度とスケールは以下のようになります。
e1, e2 はそれぞれ以下のように定義されているとします。
e1 numeric(p1, s1) e2 numeric(p2, s2)
演算
結果の精度
結果のスケール
※ CASE, INTERSECT, EXCEPT も UNION と同じ方法です。
例えば、
e1 numeric(10,3) e2 numeric(20,1)
である場合、e1 + e2 の結果の精度とスケールを上の表の計算式に従って計算すると、
精度 = max(3, 1) + max(10-3, 20-1) + 1 = 3 + 19 + 1 = 23 スケール = max(3, 1) = 3
となるため、e1 + e2 の結果のデータ型は
numeric(23,3)
となります。
整数部 7 桁、小数部 3 桁の数値と整数部 19 桁、小数部 1 桁の数値を足した結果を格納するためには、整数部 20 桁と小数部 3 桁が必要であるため、numeric(23, 3) が計算結果のデータ型となるのは、理解しやすいと思います。
これは以下のような SQL_VARIANT_PROPERTY 関数を使った T-SQL バッチで確認することができます。
declare @e1 numeric(10,3) declare @e2 numeric(20,1) set @e1=1111111.111 set @e2=2222222222222222222.2 select @e1+@e2 as R, sql_variant_property(@e1+@e2, 'Precision') as Precision, sql_variant_property(@e1+@e2, 'Scale') as Scale go R Precision Scale ------------------------ ---------- -------- 2222222222223333333.311 23 3
declare @e1 numeric(10,3) declare @e2 numeric(20,1) set @e1=1111111.111 set @e2=2222222222222222222.2 select @e1+@e2 as R, sql_variant_property(@e1+@e2, 'Precision') as Precision, sql_variant_property(@e1+@e2, 'Scale') as Scale go
R Precision Scale ------------------------ ---------- -------- 2222222222223333333.311 23 3
もし、この結果を変数に格納しようとしている場合には、その変数は numeric(23, 3) もしくはそれを超える精度とスケールの変数として定義されている必要があります。
これだけであればそれほど複雑ではありませんが、精度の計算結果が最大精度である 38 を超えるようになると若干複雑になります。
精度が numeric/decimal の最大精度である 38 を超える場合、小数部よりも整数部が優先され、可能な限り整数部で切り捨てが発生しないようにスケールが調整されます。
e1 numeric(38,3) e2 numeric(38,1)
である場合、e1 + e2 の結果は、
精度 = max(3, 1) + max(38-3, 38-1) + 1 = 3 + 37 + 1 = 41 スケール = max(3, 1) = 3
となりますが、41 は最大精度 38 を超えているため、精度は最大精度と等しい 38 になります。一方、スケールは、整数部が可能な限り切り捨てられないようにするために調整されます。
精度の計算上の値が最大精度を超える場合については、スケールが何になるのかを以下のような T-SQL を用いて確認する必要があります。
-- 加算の例 declare @e1 numeric(38,3) declare @e2 numeric(38,1) set @e1=1111111.111 set @e2=2222222222222222222.2 select @e1+@e2 as R, sql_variant_property(@e1+@e2, 'Precision') as Precision, sql_variant_property(@e1+@e2, 'Scale') as Scale go
R Precision Scale ------------------------ ---------- -------- 2222222222223333333.3 38 1
SUM 関数を numeric/decimal データに対して使う場合の戻り値のデータ型は numeric(38, s) です。
そのため、スケールの異なる列に対して CASE や UNION を行う場合には、結果のスケールを考慮しておく必要があります。
以下の例を見てみます。
create table t1 (c1 numeric(10, 3)) create table t2 (c1 numeric(10, 5)) go insert into t1 values (1111111.111) insert into t2 values (11111.11111) go
t1.c1 の SUM() の結果は t1.c1 が numeric(10, 3) なので numeric(38, 3) になります。
select SUM(c1) as A, sql_variant_property(SUM(c1), 'Precision') as Precision, sql_variant_property(SUM(c1), 'Scale') as Scale from t1 go A Precision Scale ------------ ---------- ------ 1111111.111 38 3
select SUM(c1) as A, sql_variant_property(SUM(c1), 'Precision') as Precision, sql_variant_property(SUM(c1), 'Scale') as Scale from t1 go
A Precision Scale ------------ ---------- ------ 1111111.111 38 3
t2.c1 の SUM() の結果は t2.c1 が numeric(10, 5) なので numeric(38, 5) になります。
select SUM(c1) as A, sql_variant_property(SUM(c1), 'Precision') as Precision, sql_variant_property(SUM(c1), 'Scale') as Scale from t2 go A Precision Scale ------------ ---------- ------ 11111.11111 38 5
select SUM(c1) as A, sql_variant_property(SUM(c1), 'Precision') as Precision, sql_variant_property(SUM(c1), 'Scale') as Scale from t2 go
A Precision Scale ------------ ---------- ------ 11111.11111 38 5
t1.c1 と t2.c1 の UNION は、numeric(38, 3) になります。その結果、t1.c2 の小数部は 3 桁となり、4 桁目以降は切り捨てられることになります。
select A, sql_variant_property(A, 'Precision') as Precision, sql_variant_property(A, 'Scale') as Scale from (select SUM(c1) as A from t1 union select SUM(c1) as A from t2) T go A Precision Scale ------------------ ------------ -------- 11111.111 38 3 1111111.111 38 3
select A, sql_variant_property(A, 'Precision') as Precision, sql_variant_property(A, 'Scale') as Scale from (select SUM(c1) as A from t1 union select SUM(c1) as A from t2) T go
A Precision Scale ------------------ ------------ -------- 11111.111 38 3 1111111.111 38 3
こういった切り捨てが発生しないように、もしくは、発生してもそれが問題とならないように、予め対策を行っておくことをお勧めします。(対策はシステム要件などにより異なると思いますので、ここでは特に言及しません。)
有効桁数、小数点以下桁数、および長さ (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms190476.aspx
SUM (Transact-SQL)
http://msdn.microsoft.com/ja-jp/library/ms187810.aspx