SQL Server を SUM/AVG でオーバーフローさせてみよう

ちょっと誤差関係で気になったことを試してみる。

統計学では平均は分散を使うことが多いのだけど、標本の数が多い場合にデータベースの sum や avg は有効に働くのだろうか?という疑問がでてくる。単純に言えば、SQL Server などのデータベースで扱える数値はどのくらい大きいあるいは精度が出るのだろうか?

以下は、SQL Server 2016 で試した結果

計算結果は38桁まで有効

単純に有効数値を確認するためにはオーバーフローさせればよい。例の0.01をどこまで掛けられるかを「0」を打ちながら試してみる。

こんな感じで、入力するときに38桁でエラーになる。

同じように、計算結果が38桁を超えるようにすると、やっぱりエラーになる。

どうやら、内部での計算は numeric 型の38桁の計算(これはint型よりもずっと大きい)でなされているようだということが分かる。

numeric( 8,3 ) で制限して10万回足したらどうなる?

numeric ってのは、有効桁数と小数点以下の桁数で指定するので、numeric( 8,3 ) は、有効桁数が8桁で小数点以下が3桁になる。99999.999 までが有効になる。

これで、1.999 のようなランダムな数を作って 10 万回足したらどうなるだろうか?と思ってやってみると、以下のようにふつうに sum の結果がでる。合計値は 535469.446 となるので、 numeric( 8,3 ) の範囲を超えているが、特に問題ないらしい。カラムの精度と計算結果の精度とは違うことがわかる。

image

たぶん、sum の計算結果は numeric の 38桁に拡張されているはずだ。

フルで 38 桁の精度を持たせた場合はどうなるのか?

では、カラムの設定を numeric( 38, 28 ) のようにして、有効桁数38桁、小数点以下28桁が有効にするとどうなるだろうか?

データとしては、1999999999.0000000000000000000000000001 を 20個ほど入れておいて、加算するとオーバーフローするように仕掛けておく。

insert into TT ( v, vv ) values ( 1, 1999999999.0000000000000000000000000001 );

こんな風にデータを入れておく。

image

そして sum する。

image

結果は予想通り、numeric の 38桁の精度を超えてしまうので計算できない。

じゃあ平均(AVG)の計算は?

平均を計算してみよう。同じ値しか入れていないので、平均は 1999999999.0000000000000000000000000001 で明白なんだけど、SQL Server の AVG 関数を使うと、以下のようにオーバーフローになる。

image

これ、前回の記事にも書いたが平均を計算する対象の最大値/最小値を適当に取ってきて、類推できる平均な値を使えば、オーバーフローにならずに計算することができる(当然、幅によってできないこともある)。だから、SQL Server の AVG 関数は、(おそらく)内部で SUM を使っていて個数で割ることをしているのだろう。だから、計算途中でオーバーフローしてしまうのだ。

「ドメイン知識」大切ですね、にもつながる。

カテゴリー: 雑談 パーマリンク