MySQL“大于”条件有时会返回值相等的行

MySQL "greater than" condition sometimes returns row with equal value

提问人:sveti petar 提问时间:4/28/2014 最后编辑:O. Jonessveti petar 更新时间:5/18/2014 访问量:10030

问:

我在基本的MySQL查询中遇到了一个令人困惑的问题。

这是我的表格:

id | rating
1  | 1317.17
2  | 1280.59
3  | 995.12
4  | 973.88

现在,我正在尝试查找列大于特定值的所有行。如果我尝试以下查询:rating

SELECT * FROM (`users`) WHERE `rating` > '995.12'

它正确地返回 。2

但是,如果我尝试

SELECT * FROM (`users`) WHERE `rating` > '973.88'

它回来了!所以就好像它认为表中的 973.88 大于 973.88,但它并没有犯与 995.12 相同的错误。无论我是从 PHP 脚本还是在 phpMyAdmin 中运行查询,都会发生这种情况。4

有什么想法吗?

mysql 精度 浮点转换

评论

0赞 Abhik Chakraborty 4/28/2014
表中的数据类型是什么?rating
5赞 P. Camilleri 4/28/2014
这可能是与“浮点运算”相关的问题
0赞 sveti petar 4/28/2014
数据类型为float(8,2)
1赞 Tamim Al Manaseer 4/28/2014
我第二个@M.Massias,我的猜测它与浮点算术有关
1赞 Abhik Chakraborty 4/28/2014
好吧,浮点比较有时会产生奇怪的结果,如果您更改表格并将评级数据类型设置为 DECIMAL(8,2) 或其他东西,看看是否仍然有同样的问题。

答:

1赞 Hituptony 4/28/2014 #1

比较数字时不需要单引号。

去掉单引号,然后重试。

但是,这已被指出是您可以解决的问题ROUND(SUM(column), 2) * 1

如果单引号不起作用,请将其与此^^的值进行比较

请参阅链接:https://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html

2赞 Ilesh Patel 4/28/2014 #2

@Hituptony是正确的。试试这个:

SELECT * FROM (`users`) WHERE `rating` > 973.88

SQL FIDDLE

24赞 Alma Do 4/28/2014 #3

决定和后果

这就是你得到的后果,因为你决定使用浮点数据类型。浮点数不精确。这意味着:是的,你可以得到 a>a = true

例如,您的第四行:

mysql> SELECT *  FROM t WHERE id=4;
+------+--------+
| id   | rating |
+------+--------+
|    4 | 973.88 |
+------+--------+
1 row in set (0.00 sec)

我已经按照您发布的方式保留了数据类型,它是.我们到了:FLOAT

mysql> SELECT rating>973.88 FROM t WHERE id=4;
+---------------+
| rating>973.88 |
+---------------+
|             1 |
+---------------+
1 row in set (0.00 sec)

哎呀!


幕后花絮

为什么?要理解为什么会这样,您应该了解浮点数据类型的表示方式。长话短说。但是 - 我将简要概述一下。

以下是它的表示方式:其中: enter image description here

  • s是标志
  • b基础。它的意思与基数相同
  • e指数

这意味着我们可以用不同的方式表示一个数字 - 这取决于我们将选择哪个基数。最常见的是 。但并非所有实数都可以用这个基数精确表示,即使在十进制基数中它们看起来“不错”。著名的例子是 - 无法精确表示 - 因此它被近似存储。同样,你可以在这里看到很长的故事 - 但我只想指出,不可能用基数 2 精确地表示它。b=20.1b=2

结果是:即使数字在十进制基数中是精确的,仍然可能无法精确地表示它 - 因此,它将被近似地存储。这就是它的工作原理,事实上,这是有意为之的——因为浮子本身的结构。


应采取的措施

固定精度

好吧,首先,你应该问问自己:你真的需要浮动吗?注意:我说:漂浮。因为 - 也有固定点数。它们将以固定精度表示数字。简单来说:使用定点数据类型,您可以确定将准确地存储您在屏幕上看到的内容。所以如果是 - 那么它是而不是.转到交易:973.88973.88973.8800000439234

mysql> ALTER TABLE t CHANGE rating rating DECIMAL(8,2);
Query OK, 4 rows affected, 4 warnings (0.47 sec)
Records: 4  Duplicates: 0  Warnings: 4

和。。

mysql> SELECT rating>973.88 FROM t WHERE id=4;
+---------------+
| rating>973.88 |
+---------------+
|             0 |
+---------------+
1 row in set (0.00 sec)

多田!奇迹发生了。您的号码现在以固定精度存储,因此,此类比较失败。

使用 float

然后,可能会有一些用例,当你被浮点数困住时(但是,在DBMS的情况下,我甚至很难记住一个这样的用例 - 如果不是大量计算的情况,这可能会导致性能影响,请参阅下面的描述)。然后仍然有办法让它工作。您应该决定适合您的精度。也就是说:从哪一点开始,您将把数字视为平等。

您只存储了两个有效数字,因此我认为 的精度绰绰有余。然后,您的查询将如下所示:1E-5

mysql> set @eps=1E-5;
Query OK, 0 rows affected (0.00 sec)

并搭配使用:

SELECT * FROM t WHERE rating>973.88+@eps

这将导致

+------+---------+
| id   | rating  |
+------+---------+
|    1 | 1317.17 |
|    2 | 1280.59 |
|    3 |  995.12 |
+------+---------+

哪个更好?

要意识到这一点,您需要再次查看掩体。我已经简要概述了什么是数据类型以及为什么它不精确。但是,数据类型也有其弱点。在DBMS的上下文中,我们可能不应该担心这一点,但我要提到的是:数据类型通常会导致性能影响。这将取决于您将在 DBMS 中进行多少计算。floatfixedfixed

在MySQL中,点数据类型(例如)被实现为BCD字符串(所以长话短说 - 再次,这里是wiki链接)。这意味着与它相比会导致性能问题。但是,如果您不打算经常在 DBMS 中进行计算,那么这种影响甚至不会很明显 - 我之所以提到它,是因为浮点和定点这两种类型都有自己的问题。DECIMALfloat


结论

DBMS和所有其他计算机一样,并不完美。它只是使用一些内部的东西来完成工作。这意味着:在某些情况下,你必须意识到内部的东西是如何运作的,才能理解为什么你会得到一些奇怪的结果。

特别是,浮点数并不精确。是的,互联网上有大量这样的答案,但我会再说一遍。它们并不精确。当涉及到浮点数时,你不应该依赖精度。而且 - 在几乎所有的 DBMS 中,都有定点数据类型。而且 - 在像您这样的情况下,您应该使用它们。它们将执行相同的工作,但有了它们,您将确定所选的精度。

但是,如果要在 DBMS 中进行过多计算,则可能需要使用浮点数。但是,另一方面,这是关于 - 你为什么要这样做?为什么不使用应用程序来生成这些计算(因此,避免使用定点数据类型和浮点数的前提问题对性能的影响 - 因为使用定点和平均计算量是可以的)

评论

0赞 sveti petar 4/28/2014
感谢您的深入回答。我已经切换了数据类型,问题就消失了。这让我想知道我还在哪里犯了依赖数据类型的相同错误,因为我以前从未使用过,没有意识到浮点数的不准确性。FLOATDECIMAL
1赞 Alma Do 4/28/2014
正如我所发布的,在 DBMS 的上下文中使用浮点数据类型几乎没有意义。在大多数情况下,固定数据类型的内置精度将绰绰有余
0赞 eggyal 5/15/2014
-1 建议人们应该始终使用定点类型,除非有压倒一切的理由使用浮点数,否则一般来说,这是非常糟糕的建议。大多数测量值都是近似值,并且仅精确到一定数量的有效数字,这正是浮点数所代表的(a 的精度超过 7 个十进制,而 a 的精度接近 16)。正如你所指出的,仅仅因为一个值在以 10 为基数中看起来是“圆形”的并没有什么特别的:在许多其他基数中它不会是圆形的(我们选择使用 base-10 是相当武断的)。FLOATDOUBLE
0赞 eggyal 5/15/2014
此外,严格来说,定点类型并不比浮点类型更准确:在MySQL的情况下,它可以简单地提供更重要的精度数字(代价是可表示范围大幅减少,存储效率降低,操作性能差)。除此之外,大多数应用程序代码都将使用浮点数,因此在任何情况下都会转换值(伴随着精度问题)。
0赞 eggyal 5/15/2014
当人们记得MySQL的定点类型有其自身的问题时,例如溢出处理,很明显,最好了解两种数据类型的优缺点,并选择最适合存储的数据类型。在99%的情况下,这将是一个浮动:但是的,这确实意味着必须适当地处理它们。执行任何其他操作都等同于将日期存储在字符串中,因为正确使用该类型“太难”了。DATE