Search code examples
mysqlsqlinnodbmariadbmyisam

Can someone explain why this query sometimes brings back zero rows other times multiple rows when I am only ever expecting 1 record


I am trying to a get a single random row back from a table and to do this I am can using FLOOR(1 + rand() * 50) to get a valid row ID. 50 equals the amount of records in my table (I actually have a couple of thousand but as an example I'm keeping this small).

The SQL ends up being this

<!-- language: lang-sql -->
SELECT id FROM ids WHERE id = FLOOR(1 + rand() * 50)

But when I run this query either returns

  • 0 records
  • 1 record
  • 2+ records

The issue is that I always need 1 record back; I could put a LIMIT on there but sometimes I even don't get a record back plus I shouldn't need to as FLOOR(1 + rand() * 50) will always return a valid row ID.

I know there are other ways I can do this but now I just need to understand why this is happening. To demonstrate here is my example table

<!-- language: lang-sql -->
CREATE TABLE `ids` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

I then run the following 50 times

<!-- language: lang-sql -->
INSERT INTO `ids` (`id`) VALUES (NULL);

So now my table looks like this (but with another 48 records underneath)

id |
---|
1  |
2  |
.
.

With the table of IDs set up I proceed to keep running the first query, remembering that FLOOR(1 + rand() * 50) always returns a valid ID within range and I get

id |
---|
25 |
30 |
43 |

or

id |
---|

or

id |
---|
41 |

Declaring the ID you are going to look up does actually get around this problem.

SET @randomID = FLOOR(1 + rand() * 50); 
SELECT id FROM ids WHERE id = @randomID;

Though I am still not understanding why I get more than 1 record back in the original query.


Solution

  • The standard docs says -

    RAND() in a WHERE clause is evaluated for every row (when selecting from one table)

    If you don't want to declare a variable -

    select id from `ids`
    join (select FLOOR(1 + rand() * 50) as id) b using(id);
    

    DOC LINK