我一直在研究和测试如何在MySQL中进行快速随机选择。在这个过程中,我遇到了一些意想不到的结果,现在我不完全确定我知道兰德()是如何工作的。
我一直认为,当你对表执行ORDER BY兰德()时,MySQL会向表中添加一个新列,其中填充了随机值,然后它会按该列对数据进行排序,然后例如:你取上面的随机值。我做了大量的谷歌搜索和测试,最终发现查询Jay offers in his blog确实是最快的解决方案:
SELECT * FROM Table T JOIN (SELECT CEIL(MAX(ID)*RAND()) AS ID FROM Table) AS x ON T.ID >= x.ID LIMIT 1;
在我的测试表上,普通的ORDER BY兰德()需要30-40秒,而他的查询只需要0.1秒。他在博客中解释了这是如何运作的,所以我将跳过这一点,最后转移到奇怪的事情。
我的表是一个普通的表,主键是id
,其他非索引的东西,如username
,age
等。这是我一直在努力解释的事情
SELECT * FROM table ORDER BY RAND() LIMIT 1; /*30-40 seconds*/
SELECT id FROM table ORDER BY RAND() LIMIT 1; /*0.25 seconds*/
SELECT id, username FROM table ORDER BY RAND() LIMIT 1; /*90 seconds*/
我有点期待看到所有三个查询的时间大致相同,因为我总是在一个列上排序。但由于某种原因,这并没有发生。如果你对此有什么想法,请告诉我.我有一个项目,我需要做快速订购兰德(),我个人更喜欢使用
SELECT id FROM table ORDER BY RAND() LIMIT 1;
SELECT * FROM table WHERE id=ID_FROM_PREVIOUS_QUERY LIMIT 1;
是的,它比Jay的方法慢,但是它更小,更容易理解。我的查询相当大,有几个JOIN和WHERE子句,虽然Jay的方法仍然有效,但查询变得非常大和复杂,因为我需要在JOINed(在他的查询中称为x)子请求中使用所有JOIN和WHERE。
谢谢你的时间!
7条答案
按热度按时间h4cxqtbf1#
虽然没有“兰德()快速排序”这样的东西,但对于您的特定任务有一个变通方案。
获取任意随机行,你可以像这个德国博主那样做:http://web.archive.org/web/20200211210404/http://www.roberthartung.de/mysql-order-by-rand-a-case-study-of-alternatives/(我看不到热链接URL。如果有人看到一个,请随意编辑链接。)
文本是德语的,但SQL代码在页面的下方,在大的白色框中,所以不难看到。
基本上,他所做的就是创建一个过程,来完成获取有效行的工作。这会生成一个0到max_id之间的随机数,尝试获取一行,如果它不存在,就继续获取,直到找到一个存在的行。他允许通过将它们存储在临时表中来获取x个随机行,所以你可以重写这个过程,以便只获取一行。
这样做的缺点是,如果你删除了很多行,并且有巨大的差距,它很可能会错过很多次,使其无效。
更新:执行时间不同
SELECT * FROM table ORDER BY兰德()LIMIT 1;/*30-40秒 */
SELECT id FROM table ORDER BY兰德()LIMIT 1;/*0.25秒 */
SELECT id,username FROM table ORDER BY兰德()LIMIT 1;/*90秒 */
我有点期待看到所有三个查询的时间大致相同,因为我总是在一个列上排序。但由于某种原因,这并没有发生。如果你对此有什么想法,请告诉我.
这可能与索引有关。
id
是索引的,可以快速访问,而将username
添加到结果中意味着它需要从每行读取并将其放入内存表中。对于*
,它也必须将所有内容读入内存,但它不需要在数据文件中跳转,这意味着没有时间浪费。只有当存在可变长度列(varchar/text)时,这才有区别,这意味着它必须检查长度,然后跳过该长度,而不是在每行之间跳过设置的长度(或0)。
uurv41yg2#
这可能与索引有关。id是索引的,可以快速访问,而将username添加到结果中意味着它需要从每一行读取并将其放入内存表中。使用 *,它也必须将所有内容读入内存,但它不需要在数据文件中跳转,这意味着没有时间浪费。只有当存在可变长度列时,这才有区别,这意味着它必须检查长度,然后跳过该长度,而不是仅跳过每行之间的设置长度(或
实践胜于一切理论!为什么不只是检查计划?:)
ovfsdjhp3#
我可以告诉你为什么
SELECT id FROM ...
比其他两个慢得多,但我不确定,为什么SELECT id, username
比SELECT *
快2-3倍。当你有一个索引(在你的情况下是主键),并且结果只包括索引中的列时,MySQL优化器只能使用索引中的数据,甚至不会查看表本身。每一行的开销越大,您将观察到的效果就越大,因为您用纯内存操作替代了文件系统IO操作。如果你在(id,username)上有一个额外的索引,你在第三种情况下也会有类似的性能。
wfypjpf44#
为什么不在表上添加一个索引
id, username
,看看这是否会迫使mysql使用索引,而不仅仅是一个文件排序和临时表。ndasle7k5#
PrimaryKey的索引。这样,“发现”更快。
如果你想要一个随机的(整行),但速度使用PrimaryKey与随机函数。。你可以试试这个(下面的代码):
使用derived-table来“查找”单个随机行的主键。然后你加入它..得到整排。
wi3ka0sx6#
使用
RAND()
比较慢。*
也比较慢。我无法解释的是为什么
id, username
比*
慢。这是一个我无法复制的奇怪现象。
最快的方法是获取
MAX(id)
并将其存储在内存中。然后,使用您的软件拉一个随机数与它作为天花板,然后在SQL如果没有行,回落到
如果你的MySQL安装没有bug,你应该这样做,
一个小型的中型数据集。做两个选择不能更快。但是软件是有缺陷的。
zzoitvuj7#
我认为这是一个快速的解决方案:我想出了以下方法,我用这个:
我在实时数据表上运行了一些查询:
更多信息(匈牙利文):https://dev.to/benjaminhu/villamgyors-mysql-order-by-rand-lekerdezes-9pj