带楼层结果的mysql算法不递增

bjp0bcyl  于 2021-06-18  发布在  Mysql
关注(0)|答案(1)|浏览(301)

我有一个过程,通过迭代地向id值添加一个随机数,手动递增id。有时这会导致id不递增,即使我可以确认 (FLOOR(RAND()*(1000-2+1))+2) 始终返回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
8hhllhi2

8hhllhi21#

以下是最可能发生的情况: RAND() 返回浮点值 FLOOR(FLOAT VALUE) 返回浮点值 BIGINT VALUE + FLOAT VALUE 返回浮点值
浮点数不精确,这就是问题所在
对我来说失败的nextid值之一是793991813579709184。看看你加50华氏度会发生什么。。。浮点值不递增:

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;

相关问题