速 成 指 南
5分钟搞懂MySQL数据类型
之浮点型--FLOAT类型
MySQL支持的浮点类型有两个:FLOAT和DOULE。单精度的FLOAT占用4个字节的空间,双精度的DOUBLE占用8个字节的空间。
对于FLOAT,MySQL支持非标准语法,如:FLOAT(M,D)、REAL(M,D)或DOUBLE PRECISION(M,D)。这里(M,D)分别是数值位数和小数点后的位数。例如FLOAT(7,4)的最大值即是999.9999。MySQL会对保存的数值进行四舍五入,例如插入999.00009到类型定义为FLOAT(7,4)的列时,保存的近似值是999.0001。
mysql> create table t1(v float(7,4));
Query OK, 0 rows affected, 1 warning (0.13 sec)
mysql> insert into t1 values (999.00009);
Query OK, 1 row affected (0.02 sec)
mysql> select * from t1;
+----------+
| v |
+----------+
| 999.0001 |
+----------+
1 row in set (0.01 sec)
浮点数中保存的是近似值而非精确值,如果把它当成精确值处理,可能会造成一定的困扰。SQL语句里的浮点数可能会跟内部展现的不同,这个示例还是很能说明问题:
mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
Query OK, 0 rows affected (0.05 sec)
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
-> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
-> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
-> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
-> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
-> (6, 0.00, 0.00), (6, -51.40, 0.00);
Query OK, 16 rows affected (0.01 sec)
Records: 16 Duplicates: 0 Warnings: 0
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1 GROUP BY i HAVING a <> b;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
| 6 | -51.4 | 0 |
+------+-------+------+
6 rows in set (0.00 sec)
前5条记录a/b的值肉眼看起来完全相同,但为什么没能被having条件过滤掉呢,因为a/b两列只是看起来没有区别,而实际上确实不同,只不过数值的差异是在小数点10位以后,具体保存的数值基于计算机体系架构或编译版本和优化级别,比如说不同CPU类型计算的浮点型数值可能不同。
正确的执行浮点数值比较的方式,首先确定一个可接受的数值比较的精度值,比如说我们能够接受浮点数的精度在千分之一内就认为相同,那么我们可以将比较语句做些修改:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1 GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 6 | -51.4 | 0 |
+------+-------+------+
1 row in set (0.00 sec)
如果D1/D2两列是DECIMAL数值类型,那么上述SELECT语句应该只返回一条记录。所以,如果确实对数值的存储精度没有要求,可以考虑使用FLOAT类型,否则还是使用精确数值的数据类型较好。
另外,从MySQL8.0.17版本开始,非标的FLOAT(M,D)和DOUBLE(M,D)语法即已被废弃,并且FLOAT和DOUBLE类型的列不再支持AUTO_INCREMENT属性,使用时也要注意。