我想从一个包含大量要扫描行的表中按随机顺序返回行 尝试: 1) 从表order by rand()limit 1中选择* 2) select*from table where id in(select id from table order by rand()limit 1) 2比1快,但在具有大行的表上仍然太慢 更新:查询用于实时应用程序。插入、选择和更新大约为10/秒。所以缓存不是理想的解决方案。此特定案例所需的行数为1。但也在寻找一种通用的解决方案,即查询速度快且所需行数大于1
SET @randy = CAST(rand()*(SELECT MAX(id) FROM yourtable) as UNSIGNED);
SET @q1 = CONCAT('SELECT * FROM yourtable LIMIT 1 OFFSET ', @randy);
PREPARE stmt1 FROM @q1;
EXECUTE stmt1;
在执行查询之前生成一组随机的id(如果需要,还可以很快获得max(id))。然后按以下步骤进行查询 id IN (your, list) . 这将使用索引只查看您请求的id,因此速度非常快。 限制:如果您随机选择的一些id不存在,查询将返回较少的结果,因此您需要在循环中执行这些操作,直到有足够的结果为止。
4条答案
按热度按时间irlmq6kh1#
我想象一张有一百万条记录的table。您希望随机选取一行,因此每行生成一个随机数,即一百万个随机数,然后查找生成数最小的行。涉及两项任务:
生成所有这些数字
求最小数
当然,然后访问记录。
如果需要多行,dbms可以对所有记录进行排序,然后返回n个记录,但希望它更愿意应用部分排序操作,只检测n个最小值。不管怎么说,这是一项艰巨的任务。
我想没有什么彻底的办法可以避免这一点。如果你想随机访问,这是最好的方法。
不过,如果你愿意接受一个不那么随机的结果,我建议你做一个id桶。想象一下id桶000000-0999999,100000-1999999。。。然后随机选择一个桶,从中选择你的随机行。好吧,无可否认,这看起来不是很随机,你要么只会得到旧的或只有新的记录与这样的桶;但它说明了这项技术。
不是按值创建bucket,而是使用模函数创建它们。
id % 1000
会给你1000桶。第一个是ids000,第二个是ids001。这将解决新/旧记录的问题,并使桶平衡。由于id仅仅是一个技术上的东西,所以绘制的id看起来如此相似并不重要。即使这让你烦恼,也不要做1000桶,而是说997桶。现在创建一个计算列:
添加索引:
并查询数据:
这里只有大约0.1%的表进入排序。所以这应该很快。但我想只有一张非常大的table和大量的水桶才合算。
该技术的缺点:
可能会出现这样的情况:您无法获得所需的行数,然后必须再次查询。
你必须明智地选择模数。如果表中只有两千条记录,你当然不会制造1000个桶,但可能是100条,而且每次的需求永远不会超过,比方说,十行。
如果表不断增长,一个曾经选定的数字可能不再是最优的,您可能需要更改它。
测试仪链接:http://rextester.com/vdpiu7354
更新:我刚刚意识到,如果生成的列不是基于id上的模,而是基于
RAND
而不是值:但是mysql抛出一个错误“生成的列'bucket'的表达式包含一个不允许的函数”。这似乎没有意义,因为非确定性函数应该可以使用
STORED
选项,但至少在版本5.7.12中,这不起作用。也许在以后的版本里?0tdrvxhp2#
如果你可以在同一个“调用”中运行两个查询,你可以这样做,不幸的是,这意味着你的数据库中没有删除的记录。。。如果他们发现某个查询不会返回任何内容。
我测试了一些当地的记录,我能做的最快的就是这个。。。也就是说,我在没有删除行的表上测试了它。
另一个解决方案来自于对这个问题的答案稍加修改,以及您自己的解决方案:在mysql存储函数的select语句中使用变量作为偏移量
gpnt7bae3#
在执行查询之前生成一组随机的id(如果需要,还可以很快获得max(id))。然后按以下步骤进行查询
id IN (your, list)
. 这将使用索引只查看您请求的id,因此速度非常快。限制:如果您随机选择的一些id不存在,查询将返回较少的结果,因此您需要在循环中执行这些操作,直到有足够的结果为止。
ttcibm8c4#
最快的方法是在mysql和limit中使用prepared语句
total \u rows \u in \u table=表中的总行数。
它比上面两个快得多。
限制:获取多于1行并不是真正随机的。