このセクションでは、MySQL での精度の高い数学的クエリー結果を示す例をいくつか示します。 これらの例は、セクション12.25.3「式の処理」およびセクション12.25.4「丸め動作」で説明されている原則を示しています。
例 1 数値は、可能であれば、指定されたとおりにその厳密値で使用されます。
mysql> SELECT (.1 + .2) = .3;
+----------------+
| (.1 + .2) = .3 |
+----------------+
| 1 |
+----------------+
浮動小数点値の場合、結果は不正確です。
mysql> SELECT (.1E0 + .2E0) = .3E0;
+----------------------+
| (.1E0 + .2E0) = .3E0 |
+----------------------+
| 0 |
+----------------------+
厳密値と近似値の処理の違いを確認するための別の方法として、合計値に小さい数値を何回も加える方法があります。 ある変数に .0001
を 1,000 回加える次のストアドプロシージャーを考えてみます。
CREATE PROCEDURE p ()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE d DECIMAL(10,4) DEFAULT 0;
DECLARE f FLOAT DEFAULT 0;
WHILE i < 10000 DO
SET d = d + .0001;
SET f = f + .0001E0;
SET i = i + 1;
END WHILE;
SELECT d, f;
END;
論理的には、d
と f
の両方の合計値が 1 になるはずですが、それは 10 進数の計算にしか当てはまりません。 浮動小数点の計算では、小さなエラーが発生します。
+--------+------------------+
| d | f |
+--------+------------------+
| 1.0000 | 0.99999999999991 |
+--------+------------------+
例 2 乗算は、標準 SQL に必要なスケールで実行されます。 つまり、スケール S1
と S2
を持つ 2 つの数値 X1
と X2
の場合、結果のスケールは
になります。
S1
+ S2
mysql> SELECT .01 * .01;
+-----------+
| .01 * .01 |
+-----------+
| 0.0001 |
+-----------+
例 3 厳密値数値に対する丸め動作は、適切に定義されています。
丸め動作 (たとえば、ROUND()
関数を使用した動作) は、ベースとなる C ライブラリの実装には依存しません。つまり、その結果は各プラットフォームにわたって一貫性があります。
-
厳密値カラム (
DECIMAL
と整数) および厳密値数値に対する丸めでは、「四捨五入」ルールが使用されます。 小数部が .5 以上の値は、次に示すようにゼロから最も近い整数に丸められます:mysql> SELECT ROUND(2.5), ROUND(-2.5); +------------+-------------+ | ROUND(2.5) | ROUND(-2.5) | +------------+-------------+ | 3 | -3 | +------------+-------------+
-
浮動小数点値に対する丸めでは、C ライブラリが使用されます。これは、多くのシステムでは「偶数丸め」ルールを使用します。 2 つの整数の間に小数部が完全に半分の値がある場合、最も近い偶数に丸められます:
mysql> SELECT ROUND(2.5E0), ROUND(-2.5E0); +--------------+---------------+ | ROUND(2.5E0) | ROUND(-2.5E0) | +--------------+---------------+ | 2 | -2 | +--------------+---------------+
例 4 厳密モードでは、カラムの範囲を外れている値を挿入すると、正当な値への切り捨てではなく、エラーが発生します。
MySQL が厳密モードで実行されていない場合は、正当な値への切り捨てが発生します。
mysql> SET sql_mode='';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO t SET i = 128;
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> SELECT i FROM t;
+------+
| i |
+------+
| 127 |
+------+
1 row in set (0.00 sec)
ただし、厳密モードが有効になっている場合は、エラーが発生します。
mysql> SET sql_mode='STRICT_ALL_TABLES';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t SET i = 128;
ERROR 1264 (22003): Out of range value adjusted for column 'i' at row 1
mysql> SELECT i FROM t;
Empty set (0.00 sec)
例 5: 厳密モードで ERROR_FOR_DIVISION_BY_ZERO
が設定されている場合は、0 による除算によって NULL
の結果ではなく、エラーが発生します。
非厳密モードでは、0 による除算によって NULL
の結果が生成されます。
mysql> SET sql_mode='';
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t SET i = 1 / 0;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT i FROM t;
+------+
| i |
+------+
| NULL |
+------+
1 row in set (0.03 sec)
ただし、適切な SQL モードが有効になっている場合、0 による除算はエラーになります。
mysql> SET sql_mode='STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t SET i = 1 / 0;
ERROR 1365 (22012): Division by 0
mysql> SELECT i FROM t;
Empty set (0.01 sec)
例 6 厳密値リテラルは、厳密値として評価されます。
近似値リテラルは浮動小数点を使用して評価されますが、正確な値リテラルは DECIMAL
として処理されます:
mysql> CREATE TABLE t SELECT 2.5 AS a, 25E-1 AS b;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> DESCRIBE t;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| a | decimal(2,1) unsigned | NO | | 0.0 | |
| b | double | NO | | 0 | |
+-------+-----------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
例 7 集約関数への引数が厳密値数値型である場合は、その結果も、少なくとも引数と同じスケールを持つ厳密値数値型になります。
これらのステートメントを考慮してください。
mysql> CREATE TABLE t (i INT, d DECIMAL, f FLOAT);
mysql> INSERT INTO t VALUES(1,1,1);
mysql> CREATE TABLE y SELECT AVG(i), AVG(d), AVG(f) FROM t;
結果が倍精度値になるのは浮動小数点引数の場合だけです。 厳密値型引数の場合は、結果も厳密値型になります。
mysql> DESCRIBE y;
+--------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------+------+-----+---------+-------+
| AVG(i) | decimal(14,4) | YES | | NULL | |
| AVG(d) | decimal(14,4) | YES | | NULL | |
| AVG(f) | double | YES | | NULL | |
+--------+---------------+------+-----+---------+-------+
結果が倍精度値になるのは浮動小数点引数の場合だけです。 厳密値型引数の場合は、結果も厳密値型になります。