Dieser Abschnitt zeigt Beispiele für die Ergebnisse von Anfragen mit Präzisionsberechnungen in MySQL 5.1.
Beispiel 1. Zahlen werden möglichst mit ihrem genauen Wert wie vorgegeben benutzt:
mysql> SELECT .1 + .2 = .3;
+--------------+
| .1 + .2 = .3 |
+--------------+
| 1 |
+--------------+
Bei Fließkommawerten sind die Ergebnisse Näherungen:
mysql> SELECT .1E0 + .2E0 = .3E0;
+--------------------+
| .1E0 + .2E0 = .3E0 |
+--------------------+
| 0 |
+--------------------+
Eine andere Möglichkeit, den Unterschied in der Behandlung von
genauen und Näherungswerten zu zeigen, besteht darin, eine kleine
Zahl immer wieder zu einer Summe zu addieren. Betrachten Sie
folgende gespeicherte Prozedur, die zu einer Variablen tausendmal
den Wert .0001
addiert.
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;
Logischerweise sollte die Summe für d
und
f
beide Male 1 ergeben, aber in Wirklichkeit
ist dies nur bei der Dezimalberechnung der Fall. In der
Fließkommaberechnung wird ein Fehler eingeführt:
+--------+------------------+ | d | f | +--------+------------------+ | 1.0000 | 0.99999999999991 | +--------+------------------+
Beispiel 2. Multiplikationen
werden mit der durch den SQL-Standard vorgegebenen Anzahl
Dezimalstellen ausgeführt: Für zwei Zahlen
X1
und X2
,
die S1
beziehungsweise
S2
Dezimalstellen haben, entsteht ein
Ergebnis mit
Dezimalstellen:
S1
+
S2
mysql> SELECT .01 * .01;
+-----------+
| .01 * .01 |
+-----------+
| 0.0001 |
+-----------+
Beispiel 3. Das Rundungsverhalten ist wohldefiniert:
So ist das Rundungsverhalten (beispielsweise der Funktion
ROUND()
) unabhängig von der Implementierung
der zugrunde liegenden C-Bibliothek, sodass die Ergebnisse
plattformübergreifend konsistent sind.
DECIMAL
-Spalten und genaue Zahlen werden nach
der Regel „Ab der Hälfte wird aufgerundet“
behandelt: Werte mit einem Dezimalteil von .5 oder mehr werden,
wie das nächste Beispiel zeigt, immer von null weg auf den
nächstgelegenen Integer aufgerundet:
mysql> SELECT ROUND(2.5), ROUND(-2.5);
+------------+-------------+
| ROUND(2.5) | ROUND(-2.5) |
+------------+-------------+
| 3 | -3 |
+------------+-------------+
Dagegen stützt sich die Rundung von Fließkommawerten auf die C-Bibliothek, die auf vielen Systemen nach dem Grundsatz „Runde auf die nächste gerade Zahl“ verfährt. Auf solchen Systemen werden Werte mit einem Bruchteilsanteil auf die nächste gerade Zahl gerundet:
mysql> SELECT ROUND(2.5E0), ROUND(-2.5E0);
+--------------+---------------+
| ROUND(2.5E0) | ROUND(-2.5E0) |
+--------------+---------------+
| 2 | -2 |
+--------------+---------------+
Beispiel 4. Wenn Sie im Strict-Modus einen zu großen Wert einfügen, führt dies zu einem Überlauf und verursacht einen Fehler. Der Wert wird nicht auf ein zulässiges Maß zurechtgestutzt.
Läuft MySQL jedoch nicht im Strict-Modus, wird der Wert abgeschnitten, bis er passt:
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)
Immerhin wird eine Überlaufbedingung registriert, wenn der Strict-Modus eingeschaltet ist:
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)
Beispiel 5: Sind der Strict-Modus
und ERROR_FOR_DIVISION_BY_ZERO
eingeschaltet,
verursacht eine Division durch null einen Fehler, anstatt das
Ergebnis NULL
nach sich zu ziehen.
Im nichtstrikten Modus hat die Division durch null das Ergebnis
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)
Sind jedoch die richtigen SQL-Modi eingeschaltet, verursacht eine Division durch null immer einen Fehler:
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)
Beispiel 6. Vor MySQL 5.0.3 (also vor der Einführung von Präzisionsberechnungen) wurden sowohl genaue als auch näherungsweise Literale in Fließkommazahlen mit doppelter Genauigkeit umgewandelt:
mysql>SELECT VERSION();
+------------+ | VERSION() | +------------+ | 4.1.18-log | +------------+ 1 row in set (0.01 sec) mysql>CREATE TABLE t SELECT 2.5 AS a, 25E-1 AS b;
Query OK, 1 row affected (0.07 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql>DESCRIBE t;
+-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | a | double(3,1) | | | 0.0 | | | b | double | | | 0 | | +-------+-------------+------+-----+---------+-------+ 2 rows in set (0.04 sec)
Seit MySQL 5.0.3 werden näherungsweise Literale zwar immer noch
in Fließkommazahlen konvertiert, genaue hingegen werden als
DECIMAL
behandelt:
mysql>SELECT VERSION();
+-----------------+ | VERSION() | +-----------------+ | 5.1.6-alpha-log | +-----------------+ 1 row in set (0.11 sec) 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)
Beispiel 7. Wenn das Argument einer Aggregatfunktion ein genauer numerischer Typ ist, ist ihr Ergebnis ebenfalls ein genauer numerischer Typ, und zwar mit mindestens so vielen Dezimalstellen, wie das Argument hatte.
Betrachten Sie folgende Anweisungen:
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;
Vor MySQL 5.0.3 (also vor der Einführung von Präzisionsberechnungen in MySQL) sähe das Ergebnis folgendermaßen aus:
mysql> DESCRIBE y;
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| AVG(i) | double(17,4) | YES | | NULL | |
| AVG(d) | double(17,4) | YES | | NULL | |
| AVG(f) | double | YES | | NULL | |
+--------+--------------+------+-----+---------+-------+
Das Resultat ist ein Double-Wert, egal welchen Typ das Argument hatte.
Mit MySQL 5.0.3 hat sich dies geändert:
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 | |
+--------+---------------+------+-----+---------+-------+
Hier entsteht nur für das Fließkommaargument ein Ergebnis vom Typ Double. Argumente mit genauen Typen haben genaue Ergebnisse.
Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.