关于Mysql的float字段的比较处理

作/译者:吴炳锡 来源:http://coolriver.cublog.cn,wubingxi#gmail.com

首先声明:Mysql手册上已经说明,FLOAT或DOUBLE列与具有数值类型的数值进行比较,不能使用等式(=)比较.

但实际还有这种需要.

今天就来研究一下float,double 字段在where 字段中的处理.
>desc olympic_result;
+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| id      | int(10) unsigned |      | PRI | NULL    | auto_increment |
| user_id | int(10) unsigned |      |     | 0       |                |
| types   | tinyint(4)       |      |     | 0       |                |
| result  | float            |      | MUL | 0       |                |
| times   | int(10) unsigned |      |     | 0       |                |
+---------+------------------+------+-----+---------+----------------+
> select * from olympic_result;
+----+---------+-------+--------+-----------+
| id | user_id | types | result | times     |
+----+---------+-------+--------+-----------+
|  1 |     243 |     1 |  10.28 | 143243242 |
+----+---------+-------+--------+-----------+
> select * from olympic_result where result=10.28;  
Empty set (0.00 sec)
>Why?

让我们看一下:
>alter table olympic_result add cresutl varchar(32);
>update olympic_result set cresult=10.28 where id=1;
mysql> select * from olympic_result;
+----+---------+-------+--------+-----------+-------------------------+
| id | user_id | types | result | times     | cresult                 |
+----+---------+-------+--------+-----------+-------------------------+
|  1 |     243 |     1 |  10.28 | 143243242 | 10.27999973297119140625 |
可以看到10.28,这样的浮点值在电脑存放为10.27999973297119140625 这种形式.
听高手讲是:因为10进制和2进制之间的误差.看样子误差也在第七位出现了.所以小数据保留七位或是八位是有依据的.
在大多数计算机语言中,该问题很常见,这是因为,并非所有的浮点值均能以准确的精度保存。在某些情况下,将FLOAT更改为DOUBLE可更正该问题。

解决方法:
第一个是用区间:
select * from olympic_result where result>10.27 and result<10.29;
第二个就是在设计根本不设计float型的字段,而是用一个int型+标识这个int型的小数位来代替float型,也就是result=10.28在数据库中存的是result=10.28,precision=2
缺点:但这种方法,排序时,不好解决.
第三个方法:设计时多做一个字符字段:
如:alter table olympic_result add cresutl varchar(32);
插入更新时,加上引号.
>update olympic_result set cresult='10.28' where id=1;
这样去处理.
查询:
mysql> select * from olympic_result where cresult='10.28';
+----+---------+-------+--------+-----------+---------+
| id | user_id | types | result | times     | cresult |
+----+---------+-------+--------+-----------+---------+
|  1 |     243 |     1 |  10.28 | 143243242 | 10.28   |
+----+---------+-------+--------+-----------+---------+

排序时可以按result进行.

思考:
如果小数点后面位数固定可以用decimal类型.也可以解决问题.