Арифметика MySQL с результатом FLOOR не увеличивается - PullRequest
0 голосов
/ 06 ноября 2018

У меня есть процедура, в которой я увеличиваю идентификатор вручную, добавляя случайное число к значению идентификатора итеративно. Иногда это приводит к тому, что 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

1 Ответ

0 голосов
/ 06 ноября 2018

Вот что, скорее всего, произойдет:

  • RAND() возвращает число с плавающей запятой
  • FLOOR(FLOAT VALUE) возвращает число с плавающей запятой
  • BIGINT VALUE + FLOAT VALUE возвращает число с плавающей запятой
  • Плавания не точны, и здесь у вас есть проблема

Одним из значений 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;
...