Random Row in MySQL

2020-07-18 11:48发布

I am trying to get a random row in MySQL. Here is my approach:

SELECT * 
FROM users 
WHERE id = 
 (SELECT floor((max(id) - min(id) + 1) * rand()) + min(id)
  FROM users);

This can return an empty set when there are gaps in the ids. I am fine with that. However,

  1. I don't have gaps and I still get an empty set rather frequently.
  2. More disturbing: I will get two or more results every once in a while.

What is the reason for this strange behavior? How, do I get it right?

Note:

  • the table is quite big (10^6 ... 10^7); I can't use any of the obvious, and known, solutions based on random ordering or even based on using count().
  • I'am stuck with MySQL, I don't care how it's done on the client side ruby/PHP/whatever.
  • I tried to cast the floating types, but it did not help.
  • The corresponding PostgreSQL variant (random instead of rand, and some type casts) works fine.

5条回答
别忘想泡老子
2楼-- · 2020-07-18 11:50

This avoids repeated evaluation of random() during the aggregation and should be faster, too, as min(id) and random() are only evaluated once.

SELECT u.* 
FROM   users u
      ,(SELECT min(id) AS min_id, max(id) AS max_id FROM users) x
WHERE  u.id > (floor((x.max_id - x.min_id + 1) * rand()) + x.min_id)
LIMIT  1;
查看更多
虎瘦雄心在
3楼-- · 2020-07-18 12:04

How about:

SELECT * 
FROM users 
WHERE id >= (SELECT floor((max(id) - min(id) - 1) * rand())  + min(id) from users)
LIMIT 1

or similar?

Pretty much it should limit you to one result, and if it hits a gap (somehow), then it'll just take the next available one.

查看更多
做自己的国王
4楼-- · 2020-07-18 12:08

Try this one -

SELECT t.* FROM
  users t,
  (SELECT @id := (FLOOR((MAX(id) - MIN(id) + 1) * RAND()) + MIN(id)) FROM users) t2
WHERE
  t.id = @id;
查看更多
倾城 Initia
5楼-- · 2020-07-18 12:09
SELECT * FROM users ORDER BY rand() limit 1;

Will return a random row.

查看更多
爱情/是我丢掉的垃圾
6楼-- · 2020-07-18 12:12

I suspect that this is because RAND() is being evaluated for every row in the USERS table in the main query. I suggest:

SELECT u1.* 
from users u1,
     (SELECT floor((max(id) - min(id) + 1) * rand())  + min(id) lid from users) u2
WHERE u1.id > u2.lid
LIMIT 1
查看更多
登录 后发表回答