mercredi 22 novembre 2017

MySQL: get a random unique integer ID

I tried to write a SQL-function that generates an unused unique ID in a range between 1000000 and 4294967295. I need numeric values, so UUID() alike is not a solution. It doesn't sound that difficult, but for some reason, the code below does not work when called within an INSERT-statement on a table as value for the primary key (not auto_increment, of course). The statement is like INSERT INTO table (id, content) VALUES ((SELECT getRandomID(0,0)), 'blabla bla'); (Since default values are not allowed in such functions, I shortly submit 0 for each argument and set it in the function to the desired value.)

Called once and separated from INSERT or Python-code, everything is fine. Called several times, something weird happens and not only the whole process but also the server might hang within REPEAT. The process is then not even possible to kill/restart; I have to reboot the machine -.- It also seems to only have some random values ready for me, since the same values appear again and again after some calls, allthough I actually thought that the internal rand() would be a sufficient start/seed for the outer rand(). Called from Python, the loop starts to hang after some rounds although the very first one in my tests always produces a useful, new ID and therefore should quit after the first round. Wyh? Well, the table is empty...so SELECT COUNT(*)... returns 0 which actually is the signal for leaving the loop...but it doesn't.

Any ideas? I'm running MariaDB 10.something on SLES 12.2. Here is the exported source code:

DELIMITER $$
CREATE DEFINER=`root`@`localhost` FUNCTION `getRandomID`(`rangeStart` BIGINT UNSIGNED, `rangeEnd` BIGINT UNSIGNED) RETURNS bigint(20) unsigned
READS SQL DATA
BEGIN
    DECLARE rnd BIGINT unsigned; 
    DECLARE i BIGINT unsigned; 

    IF rangeStart is null OR rangeStart < 1 THEN
        SET rangeStart = 1000000;
    END IF;
    IF rangeEnd is null OR rangeEnd < 1 THEN
        SET rangeEnd = 4294967295; 
    END IF; 

    SET i = 0;

    r: REPEAT
        SET rnd = FLOOR(rangeStart + RAND(RAND(FLOOR(1 + rand() * 1000000000))*10) * (rangeEnd - rangeStart));
        SELECT COUNT(*) INTO i FROM `table` WHERE `id` = rnd;    
    UNTIL i = 0 END REPEAT r; 

    RETURN rnd;
END$$
DELIMITER ;




Aucun commentaire:

Enregistrer un commentaire