MySQL FLOOR 结果不递增
MySQL Arithmetic with FLOOR result does not increment
我有一个手动递增 ID 的过程,方法是向 ID 值迭代添加一个随机数。有时这会导致 ID 不递增,即使我可以确认语句 (FLOOR(RAND()*(1000-2+1))+2)
总是 returns 2-1000 之间的值。然而下面的简单语句:
SET `nextid` = `nextid` + (FLOOR(RAND()*(1000-2+1))+2);
仍然时不时增加 nextid
失败。如果我将我的 (FLOOR(RAND()*(1000-2+1))+2)
值分成一个单独的变量并添加这两个变量,这不会发生。下面是重现该行为的最小示例测试用例。
DELIMITER $$
CREATE PROCEDURE `test`()
BEGIN
DECLARE `nextid` BIGINT DEFAULT 793991813529600000;
DECLARE `previous` BIGINT DEFAULT 0;
DECLARE `i` BIGINT DEFAULT 0;
DECLARE `random` BIGINT DEFAULT 0;
WHILE `i` < 100000 DO
SET `previous` = `nextid`;
-- Produce a random number between 2 and 1000.
SET `random` = (FLOOR(RAND()*(1000-2+1))+2);
SET `nextid` = `nextid` + `random`;
-- Error if the nextid is no different from the previous ID.
-- This is successful every time.
IF `nextid` = `previous` THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Oh no!', MYSQL_ERRNO = 2000;
END IF;
SET `i` = `i` + 1;
END WHILE;
SET `i` = 0;
WHILE `i` < 100000 DO
SET `previous` = `nextid`;
-- Increment the ID by a random number and do the set in a single statment.
SET `nextid` = `nextid` + (FLOOR(RAND()*(1000-2+1))+2);
-- This fails randomly, but reliably.
IF `nextid` = `previous` THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Whoops!', MYSQL_ERRNO = 2000;
END IF;
SET `i` = `i` + 1;
END WHILE;
END $$
DELIMITER ;
CALL `test`();
DROP PROCEDURE IF EXISTS `test`;
看起来可能是某种类型的转换问题,但我找不到任何可以确认的内容。所以我的问题是,是什么导致了这种行为?
其他详细信息;
mysql --version
mysql Ver 14.14 Distrib 5.7.24, for Linux (x86_64) using EditLine wrapper
以下是最有可能发生的情况:
RAND()
returns 一个浮点数
FLOOR(FLOAT VALUE)
returns 一个浮点数
BIGINT VALUE + FLOAT VALUE
returns 一个浮点数
- 浮点数不精确,这就是你遇到问题的地方
对我来说失败的 nextid 值之一是 793991813579709184。看看当你向它添加 50f 时会发生什么......浮点值不增加:
CREATE TABLE t (i BIGINT);
INSERT INTO t VALUES (793991813579709184);
INSERT INTO t VALUES (793991813579709184 + 50e0);
SELECT * FROM t;
| 793991813579709184 |
| 793991813579709184 |
在您的测试代码中,我更改了这一行并且它按预期工作:
SET `nextid` = `nextid` + CAST(RAND() * 999 AS SIGNED) + 2;
我有一个手动递增 ID 的过程,方法是向 ID 值迭代添加一个随机数。有时这会导致 ID 不递增,即使我可以确认语句 (FLOOR(RAND()*(1000-2+1))+2)
总是 returns 2-1000 之间的值。然而下面的简单语句:
SET `nextid` = `nextid` + (FLOOR(RAND()*(1000-2+1))+2);
仍然时不时增加 nextid
失败。如果我将我的 (FLOOR(RAND()*(1000-2+1))+2)
值分成一个单独的变量并添加这两个变量,这不会发生。下面是重现该行为的最小示例测试用例。
DELIMITER $$
CREATE PROCEDURE `test`()
BEGIN
DECLARE `nextid` BIGINT DEFAULT 793991813529600000;
DECLARE `previous` BIGINT DEFAULT 0;
DECLARE `i` BIGINT DEFAULT 0;
DECLARE `random` BIGINT DEFAULT 0;
WHILE `i` < 100000 DO
SET `previous` = `nextid`;
-- Produce a random number between 2 and 1000.
SET `random` = (FLOOR(RAND()*(1000-2+1))+2);
SET `nextid` = `nextid` + `random`;
-- Error if the nextid is no different from the previous ID.
-- This is successful every time.
IF `nextid` = `previous` THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Oh no!', MYSQL_ERRNO = 2000;
END IF;
SET `i` = `i` + 1;
END WHILE;
SET `i` = 0;
WHILE `i` < 100000 DO
SET `previous` = `nextid`;
-- Increment the ID by a random number and do the set in a single statment.
SET `nextid` = `nextid` + (FLOOR(RAND()*(1000-2+1))+2);
-- This fails randomly, but reliably.
IF `nextid` = `previous` THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Whoops!', MYSQL_ERRNO = 2000;
END IF;
SET `i` = `i` + 1;
END WHILE;
END $$
DELIMITER ;
CALL `test`();
DROP PROCEDURE IF EXISTS `test`;
看起来可能是某种类型的转换问题,但我找不到任何可以确认的内容。所以我的问题是,是什么导致了这种行为?
其他详细信息;
mysql --version
mysql Ver 14.14 Distrib 5.7.24, for Linux (x86_64) using EditLine wrapper
以下是最有可能发生的情况:
RAND()
returns 一个浮点数FLOOR(FLOAT VALUE)
returns 一个浮点数BIGINT VALUE + FLOAT VALUE
returns 一个浮点数- 浮点数不精确,这就是你遇到问题的地方
对我来说失败的 nextid 值之一是 793991813579709184。看看当你向它添加 50f 时会发生什么......浮点值不增加:
CREATE TABLE t (i BIGINT);
INSERT INTO t VALUES (793991813579709184);
INSERT INTO t VALUES (793991813579709184 + 50e0);
SELECT * FROM t;
| 793991813579709184 |
| 793991813579709184 |
在您的测试代码中,我更改了这一行并且它按预期工作:
SET `nextid` = `nextid` + CAST(RAND() * 999 AS SIGNED) + 2;