SQLが数学で間違っている可能性があると思ったことはありますか?クレイジーに聞こえます。ただし、SQL FLOATデータ型を使用したことがある場合は、これから説明する問題に遭遇した可能性があります。
このことを考慮。 0.1 + 0.2は0.3であるはずですよね?ただし、SQLFLOATデータ型を使用してこれを確認してください。
DECLARE @f1 FLOAT = 0.1
DECLARE @f2 FLOAT = 0.2
SELECT CASE WHEN @f1 + @f2 = .3 THEN 1 ELSE 0 END
正しい結果は1です。ただし、図1を確認してください。
私は今あなたの注意を引いていますか?私はそう確信している。正しい計算ができないシステムに依存するのは非常に怖いです。ただし、この記事はこれを回避するのに役立ちます。
やるべきことがいくつかあります。 FLOATデータ型とは何かから始める必要があります。
SQL FLOATデータ型とは何ですか?
SQL FLOATデータ型は、浮動小数点数に使用されるおおよその数値データ型です。非常に大きな数または非常に小さな数を格納できます。また、高速な処理時間を必要とする計算にも使用されます。
これらはすべて、精度が低下するという犠牲を払って発生します。さらに、計算後に小数点が配置される場所を指定することはできません。浮動小数点 。一方、DECIMALのような正確な数値は、小数点以下の位置が固定されます。
SQLFLOATデータ型を宣言する方法
構文はFLOAT[(n)]です。ここで、 n 浮動小数点数の仮数を科学的記数法で格納するために使用されるビット数です。それはまた、精度とストレージサイズを決定します。 nの可能な値 1から53の間です。nに注意してください オプションです。
次に例を示します:
DECLARE @floatValue1 FLOAT; -- Float variable without the number of bits
DECLARE @floatValue2 FLOAT(3) -- Float variable with 3 bits
nを指定しない場合 、デフォルトは53です。これは最大値でもあります。さらに、FLOAT(53)は倍精度浮動小数点数またはbinary64です。 FLOAT(53)を使用する以外に、DOUBLEPRECISIONとして宣言することもできます。
次の3つの宣言は機能的に同等です:
DECLARE @double1 FLOAT(53);
DECLARE @double2 FLOAT;
DECLARE @double3 DOUBLE PRECISION;
この表は、ビット数と対応するストレージサイズを示しています。
nの値 | ストレージサイズ |
1から24 | 4バイト |
25から53 | 8バイト |
SQL FLOATとREALは同じですか?
REALもFLOAT(24)です。単精度またはbinary32とも呼ばれます。
これを知ることが重要な理由
これがおおよその数値であることを知っていると、正確さが要求される計算に使用できなくなります。ストレージとメモリにも関心がありますか?大きすぎたり小さすぎたりする必要がない場合は、REALまたはFLOAT(24)を使用してください。
FLOATとDECIMALの違いは何ですか?
FLOATはおおよその数値です。 DECIMALは正確な数値です。表の違いの概要は次のとおりです。
フロート | DECIMAL | |
小数点 | 数字のどこにでも配置できます | 固定位置 |
最大制限 | 38桁または99,999,999,999,999,999,999,999,999,999,999,999,999 | FLOAT(53)の最大範囲は1.79E + 308または179で、その後に306個のゼロが続きます |
ストレージ | 最大8バイト | 最大17バイト |
計算結果 | おおよそ | 正確 |
比較チェック | =または<>は使用しないでください。四捨五入するときは避けてください | =または<>演算子。四捨五入に適しています |
図1で、FLOAT数の計算がどのように奇妙な結果をもたらすかをすでに見てきました。次のようにデータ型をDECIMALに変更した場合:
DECLARE @d1 DECIMAL(2,1) = 0.1
DECLARE @d2 DECIMAL(2,1) = 0.2
SELECT CASE WHEN @d1 + @d2 = 0.3 THEN 1 ELSE 0 END
結果は正しくなります。
不等式演算子の使用も問題です。以下のループを確認してください。
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue <> 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
どう思いますか?下の図2を参照してください。
ブーム!無限ループ!不等式は常に真になります。したがって、論理的な選択は、タイプをDECIMALに変更することです。
DECLARE @decimalValue DECIMAL(2,1) = 0.0
WHILE @decimalValue <> 5.0
BEGIN
PRINT @decimalValue;
SET @decimalValue += 0.1;
END
上記のコードは、@ decimalValueのときに確実に停止します。 5.0に等しい。下の図3をご覧ください。
良い!ただし、それでもFLOATを主張する場合は、無限ループがなくても問題なく機能します。
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue < 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
一方、四捨五入もオフになっています。次のことを考慮してください:
DECLARE @value FLOAT(2) = 1.15
SELECT ROUND(@value, 1) -- This will result to 1.1
1.20の代わりに、コードは1.1になります。ただし、DECIMALを使用すると、結果は正しくなります。
DECLARE @value DECIMAL(3,2) = 1.15
SELECT ROUND(@value, 1) -- This will result in 1.2 or 1.20
FLOATが正しく、DECIMALが正しくない場合
正確な数値は常にそれほど正確ではありませんか?この問題を再現するために、計算を使用してから、それを逆にします。まず、データを準備してみましょう。
CREATE TABLE ExactNumerics1
(
fixed1 DECIMAL(8,4),
fixed2 DECIMAL(8,4),
fixed3 DECIMAL(8,4),
calcValue1 AS fixed3 / fixed1 * fixed2
)
GO
INSERT INTO ExactNumerics1
(fixed1,fixed2,fixed3)
VALUES
(54,0.03,1*54/0.03)
上記の表では、最初の2列に固定値を使用します。 3番目の列に計算があります。最後に、計算列である4番目の列は、逆の計算を実行します。計算列の正しい結果は1である必要があります。
それでは、FLOATと比較するために、同様のテーブルとデータを作成しましょう。
CREATE TABLE ApproxNumerics1
(
float1 FLOAT(2),
float2 FLOAT(2),
float3 FLOAT(2),
calcValue1 AS float3 / float1 * float2
)
INSERT INTO ApproxNumerics1
(float1, float2, float3)
VALUES
(54,0.03,1*54/0.03)
質問しましょう。
SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1
結果?図4を確認してください。
ここで何が起こったのですか? FLOATはそれを正しく理解しましたが、DECIMALはそうではありませんでした。何か問題が発生しました。
暗黙の変換が再び行われます
SQLが寛容であるため、暗黙的な変換が発生します。計算にさまざまなデータ型が使用されている場合、SQLServerは背後で暗黙的な変換を使用してデータ型を変換しようとします。
変換は本当に起こりましたか?さらに、 ExactNumerics1のすべての列 テーブルは10進数です。
ExactNumerics1のテーブル構造を確認してみましょう SQL Server Management Studioのテーブル:
図3の赤いボックス領域に注目してください。計算列はDECIMAL(8,4)ではなく、DECIMAL(30,17)です。公式ドキュメントによると、精度とスケールが異なる2つのDECIMAL列は2つの異なるデータ型です 。ここで自分の目で確かめてください。違いがあるため、変換が必要です。そのため、暗黙の変換が機能します。
それらが異なり、暗黙の変換が行われた場合はどうなりますか?
繰り返しになりますが、公式ドキュメントに基づくと、暗黙の変換中に精度またはスケールが失われる可能性があります 。したがって、明示的なCASTが必要です。そのリファレンスの変換テーブルのDECIMALデータ型に注意してください。
ここでいくつかの損失が発生しました。計算列もDECIMAL(8,4)の場合、暗黙の変換は発生しません。
暗黙の変換を回避するには、公式ドキュメントに従ってください。テーブルの構造は次のようになっているはずです:
CREATE TABLE ExactNumerics2
(
fixed1 DECIMAL(8,4),
fixed2 DECIMAL(8,4),
fixed3 DECIMAL(8,4),
calcValue1 AS CAST(fixed3 / fixed1 * fixed2 AS DECIMAL(8,4)) -- the explicit CAST
)
計算列の明示的なCASTにより、データ型の一貫性が保証されます。この構造にも従い、同じデータを挿入すると、結果は正しくなります。下の図6の新しい出力を確認してください。
最終的に、2つ以上のDECIMAL値の間で暗黙の変換が発生した場合、正確な数値は正確ではなくなります。
これを知ることが重要な理由
これにより、テーブルと変数に何が必要かがわかります。さらに、暗黙の変換により、正確な数値でさえもおかしくなります。したがって、精度とスケールを明示的に定義し、計算でそれと一致させてください。
財務データにSQLFLOATを使用する必要がありますか?
円グラフのすべてのスライスでパーセンテージを計算する場合、合計は100%である必要があります。要約レポートと詳細レポートの合計も一貫している必要があります。結果の精度が重要な場合、FLOATのような近似データ型は機能しません。このための論理的な選択はDECIMALです。
しかし、疑問が残ります。
いつFLOATを使用する必要がありますか?
銀河間の距離などの天文学的値を必要とするデータには、FLOATを使用します。一方、DECIMALデータ型は、このタイプのデータで算術オーバーフローを起こします。原子核の直径などの小さな値も、FLOATを使用して適合します。精度を必要としない科学データやその他の値も、FLOATの恩恵を受けることができます。
これを知ることが重要な理由
FLOATが悪いとか、DECIMALが良いとか、その逆とは言いません。それぞれの正しいユースケースを知ることで、あなたとあなたのユーザーは意図した結果を得ることができます。そしてまた、あなたはあなたのユーザーを幸せにしたいですよね?
結論
一日の終わりまでに、私たちは皆、自分の仕事をして、それらを上手にやりたいと思っています。数学は常に私たちの仕事の一部になります。また、正しい数値データ型を知っていると、それに対処するのにも役立ちます。自分が何をしているのかを知っていれば、難しくはありません。
この記事がSQLServerの奇妙な計算を回避するのに役立つことを願っています。
他に追加するものはありますか?次に、コメントセクションでお知らせください。お気に入りのソーシャルメディアプラットフォームでもこれを共有してください。