2

我有一个查询,旨在从结果集中检索随机行。我不想使用ORDER BY Rand(),因为它似乎效率很低。

我的方法如下:

  • 生成 [0,1) 之间的单个随机数
  • 给结果查询的每一行一个唯一的“排名”数字。即给第一行一个值1,第二行一个值2,依此类推
  • 使用随机数获得一个介于 1 和结果中的行数之间的数字
  • 返回 rank == 从随机数生成的数字所在的行

示例查询:

SELECT * FROM(

    (SELECT @rand := RAND(), @rank := 0) r1
    CROSS JOIN
    (SELECT (@rank:=@rank+1) as num, A.id FROM
    A JOIN B
    ON A.id = B.id
    WHERE B.number = 42
)
WHERE num = FLOOR(1 + @rand * @rank) LIMIT 1

这适用于检索一行,但我想要 10 个随机行。更改LIMIT 1LIMIT 10不起作用,因为如果num + 10 > number of rows查询不返回 10 行。

我能想到的唯一解决方案是在 sql 查询中生成 10 个随机数,检查它们彼此不同并且有几WHERE num = random_number_1行。或者,我可以调用该查询 10 次,检查所选行是否唯一。我不知道如何做前者,而后者似乎效率很低。除非可能有一些很棒的缓存可以使相同的查询运行得非常快?

有没有人有任何想法?谢谢你

4

1 回答 1

1

您可以尝试以下方法:

select sq2.c1 
  from  ( select * 
            from (select @count :=  0) sq0
           cross join  
                 (select t1.c1, @count := @count+1        
                    from t t1       
                    join t t2      
                   using(c1)      
                   where t2.c2 = 42    
                 ) sq1  
         ) sq2   
 --use a probability to pick random rows
 where if(@count <= 5, 1, floor(1 + rand() * (@count-1))) <= ceiling(log(pow(@count,2)))+1
 limit 5;

结果将是随机的,除非结果集小于限制(或大小相同)。如果这是一个问题,您可以包装整个事情:

select sq3.* from ( select ... limit 5 ) sq3 
order by rand().  

这只会随机化少量有效的输出行(最多 5 个)。

当然,您始终可以使用临时表:

create temporary table rset (row_key int auto_increment, key(row_key))
as ( select .... where c2 = 42 ) engine=myisam;

set @count := select count(*) from rset;

select rset.c1 
  from rset 
 where row_key in (    (floor(1 + rand() * (@count-1))),
(floor(1 + rand() * (@count-1))),
(floor(1 + rand() * (@count-1))),
(floor(1 + rand() * (@count-1))),
(floor(1 + rand() * (@count-1))) );

drop table rset;

如果要保证获得五个唯一行,则可以使用第二个临时表:

create temporary table row_keys ( row_key int not null primary key );
-- do this successful five times.  if you get a unique key error try again
insert into row_keys values (floor(1 + rand() * (@count-1));

select rset.c1
  from rset
  join row_keys
  using(row_key);
于 2012-08-04T03:32:09.247 回答