「SELECT POWER(10.0、38.0);」が算術オーバーフローエラーをスローするのはなぜですか?


15

私は更新しています私のIDENTITYオーバーフローチェックスクリプトをのアカウントにDECIMALしてNUMERIC IDENTITY

チェックの一環として、すべてのIDENTITY列のデータ型の範囲のサイズを計算します。それを使用して、その範囲の何パーセントが使い果たされたかを計算します。以下のためにDECIMAL及びNUMERIC その範囲のサイズである2 * 10^p - 2場合pの精度です。

私はテストを持つテーブルの束を作成DECIMALし、NUMERIC IDENTITY列を、次のようにその範囲を計算しようとしました。

SELECT POWER(10.0, precision)
FROM sys.columns
WHERE 
       is_identity = 1
   AND type_is_decimal_or_numeric
;

これにより、次のエラーがスローされました。

Msg 8115, Level 16, State 6, Line 1
Arithmetic overflow error converting float to data type numeric. 

IDENTITYタイプの列DECIMAL(38, 0)(つまり、最大精度)に絞り込んだのでPOWER()、その値で直接計算を試みました。

以下のすべてのクエリ

SELECT POWER(10.0, 38.0);
SELECT CONVERT(FLOAT, (POWER(10.0, 38.0)));
SELECT CAST(POWER(10.0, 38.0) AS FLOAT);

また、同じエラーが発生しました。

  • なぜ、SQL Serverは、出力変換しようとしないPOWER()タイプのものであり、FLOATに、NUMERIC(場合は特にFLOAT優先順位が高いの)?
  • すべての可能な精度(もちろんを含む)のa DECIMALまたはNUMERIC列の範囲を動的に計算するにはどうすればよいp = 38ですか?

回答:


18

POWERドキュメントから:

構文

POWER ( float_expression , y )

引数

float_expressionは
です表現型のフロートまたは暗黙的に変換できるタイプのフロート

y
float_expressionを上げるべき乗数です。yは、ビットデータ型を除く、正確な数値または近似数値のデータ型カテゴリの式です。

戻り型

float_expressionで送信されたものと同じ型を返します。たとえば、10進数(2,0)がfloat_expressionとして送信される場合、返される結果は10進数(2,0)です。


最初の入力はfloat、必要に応じて暗黙的にキャストされます。

内部計算はfloat、標準Cランタイムライブラリ(CRT)関数による算術を使用して実行されますpow

floatからの出力powは、左側のオペランドの型にキャストバックされます(numeric(3,1)リテラル値10.0を使用する場合を意味します)。

floatあなたの場合、明示的な使用はうまくいきます:

SELECT POWER(1e1, 38);
SELECT POWER(CAST(10 as float), 38.0);

10 38の正確な結果decimal/numericは、39桁の精度(1の後に38個のゼロ)が必要になるため、SQL Serverに格納できません。最大精度は38です。


23

マーティンの答えにさらに干渉する代わりに、POWER()ここでの私の発見の残りを追加します。

ニッカーズを保持します。

前文

最初に、MSDNのドキュメントPOWER() Aを展示します。

構文

POWER ( float_expression , y )

引数

float_expression float型の式、または暗黙的にfloat型に変換できる型の式です。

戻り型

と同じfloat_expression

POWER()戻り値の型である最後の行を読んでから結論を出すことができますがFLOAT、もう一度読んでください。float_expression「フロート型または暗黙的にフロートに変換できる型」です。そのため、その名前にfloat_expressionもかかわらず、実際にはa FLOAT、a DECIMAL、またはanになりINTます。の出力はの出力とPOWER()同じであるためfloat_expression、それもこれらのタイプの1つである可能性があります。

したがって、入力に依存する戻り値の型を持つスカラー関数があります。それは可能性が?

観察

POWER()入力に応じて出力を異なるデータ型にキャストするテストを示すテストBを紹介します

SELECT 
    POWER(10, 3)             AS int
  , POWER(1000000000000, 3)  AS numeric0     -- one trillion
  , POWER(10.0, 3)           AS numeric1
  , POWER(10.12305, 3)       AS numeric5
  , POWER(1e1, 3)            AS float
INTO power_test;

EXECUTE sp_help power_test;

DROP TABLE power_test;

関連する結果は次のとおりです。

Column_name    Type      Length    Prec     Scale
-------------------------------------------------
int            int       4         10       0
numeric0       numeric   17        38       0
numeric1       numeric   17        38       1
numeric5       numeric   17        38       5
float          float     8         53       NULL

起こっているように見えるのは、を含まず、それに適合する最小の型にPOWER()キャストfloat_expressionすることBIGINTです。

したがって、10 38の結果を保持するのに十分な大きさにキャストされないSELECT POWER(10.0, 38);ため、オーバーフローエラー10.0NUMERIC(38, 1)失敗しますこれは、10 38が10進数の前に39桁を占めるように展開するのに対しNUMERIC(38, 1)、10進数の前に37桁と1桁を格納できるためです。したがって、最大値をNUMERIC(38, 1)保持することができるが10である37 - 0.1です。

この理解を武器に、次のように別のオーバーフロー障害を作成できます。

SELECT POWER(1000000000, 3);    -- one billion

10億は(にキャストされる最初の例の1兆ドルとは対照的にNUMERIC(38, 0))に収まるほど十分に小さいINTです。ただし、10億を3乗すると、には大きすぎINTてオーバーフローエラーになります。

他のいくつかの関数は、出力タイプが入力に依存する同様の動作を示します。

  • 数学関数POWER()CEILING()FLOOR()RADIANS()DEGREES()、およびABS()
  • システム関数表現NULLIF()ISNULL()COALESCE()IIF()CHOOSE()、とCASE表現
  • 算術演算子SELECT 2 * @MAX_INT;およびの両方SELECT @MAX_SMALLINT + @MAX_SMALLINT;は、たとえば、変数の名前がデータ型である場合、算術オーバーフローになります。

結論

この特定の場合、解決策はを使用することSELECT POWER(1e1, precision)...です。これは、とんでもないほど大きな数を保持できる1e1キャストされるためFLOAT、すべての可能な精度で機能します

これらの関数は非常に一般的であるため、結果が丸められたり、その動作によりオーバーフローエラーが発生したりする可能性があることを理解することが重要です。出力に特定のデータ型が必要な場合、または依存する場合は、必要に応じて関連する入力を明示的にキャストしてください。

それで、子供たち、あなたはこれを知ったので、あなたは出て行って、繁栄するかもしれません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.