在mysql中使用大型数据集对结果进行随机化的最快方法

时间:2018-08-27 05:54:56

标签: mysql sql random sql-order-by

我想从要扫描大量行的表中随机返回行顺序

尝试:

1)通过rand()限制1从表顺序中选择*

2)从表中的id中选择*(通过rand()限制1从表顺序中选择ID)

2快于1,但在具有大行的表上仍然太慢

更新: 查询用于实时应用程序。插入,选择和更新大约为10 /秒。因此,缓存将不是理想的解决方案。在这种情况下,所需的行数为1。但是,在快速查询且所需行数> 1的情况下,还要寻找一种通用解决方案。

4 个答案:

答案 0 :(得分:0)

最快的方法是在mysql和limit中使用预处理语句

select @offset:=floor(rand()*total_rows_in_table);
PREPARE STMT FROM 'select id from table limit ?,1';
EXECUTE STMT USING @offset; 

total_rows_in_table =表中的总行数。

与上面两个相比,它要快得多。

局限性:获取1行以上并不是真正随机的事情。

答案 1 :(得分:0)

在执行查询之前生成随机的一组ID(如果需要,您也可以很快获得MAX(id))。然后以id IN (your, list)进行查询。这将使用索引仅查看您请求的ID,因此速度非常快。

限制:如果某些随机选择的ID不存在,查询将返回较少的结果,因此您需要循环执行这些操作,直到获得足够的结果为止。

答案 2 :(得分:0)

如果您可以在同一“调用”中运行两个查询,则可以执行类似的操作,可悲的是,这假设您的数据库中没有已删除的记录...如果它们在某个查询中不返回任何内容。

我测试了一些本地记录,而我能做的最快的就是...表示我在没有删除行的表上进行了测试。

while(true)

另一个解决方案来自修改此问题的答案,以及您自己的解决方案: Using variables as OFFSET in SELECT statments inside mysql's stored functions

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        // Set a `wood` variable for the class.
        protected int wood { get; set; }

        static void Main(string[] args)
        {
            Program program = new Program(); // Making use of non-static methods.
            program.Handler();
        }

        public void Handler()
        {
            Console.WriteLine("Write \"gamble\" to hit the tree.");
            string message = Console.ReadLine();

            if (message == "gamble")
            {
                addWood(); // Call the non-static method.
            }


        }

        public bool addWood()
        {
            this.wood = this.wood + 10;

            Console.WriteLine("You now have {0} wood!", this.wood);

            Handler(); // Call the Handler() method again for infinite loop.


            return true;
        }
    }
}

答案 3 :(得分:0)

我想象一个有100万个条目的表。您想随机选择一行,因此您需要为每行生成一个随机数,即一百万个随机数,然后使用生成的最小数查找行。涉及两个任务:

  1. 生成所有这些数字
  2. 找到最低人数

然后访问记录。

如果您想要多于一行,则DBMS可以对所有记录进行排序,然后返回n条记录,但是希望它宁愿应用某些仅对n个最小数字进行检测的部分排序操作。还是要完成一些任务。

我想,没有彻底的方法来规避这一点。如果您想随机访问,这就是方法。

但是,如果您准备好接受随机性较低的结果,我建议您制作ID存储桶。想象一下ID桶000000-0999999、100000-1999999,...然后随机选择一个桶,并从中随机选择一行。好吧,诚然,这看起来不是很随意,使用这种存储桶只会得到旧的记录,也可能只会得到新的记录。但这说明了这项技术。

您不是使用值创建存储桶,而是使用模函数创建它们。 id % 1000会给您1000个存储桶。第一个ID为xxx000,第二个ID为xxx001。这样可以解决新记录/旧记录的问题,并使存储桶平衡。由于ID仅仅是技术性的东西,因此绘制的ID看起来如此相似根本没有关系。即使打扰您,也不要赚1000桶,而是说997。

现在创建一个计算列:

alter table mytable add column bucket int generated always as (id % 997) stored;

添加索引:

create index idx on mytable(bucket);

并查询数据:

select *
from mytable
where bucket = floor(rand() * 998)
order by rand()
limit 10;

这里只有约0.1%的表格进入排序。因此,这应该相当快。但我想那只能用很大的桌子和大量的水桶来支付。

该技术的缺点:

  • 可能发生的是,您没有得到想要的太多行,然后必须再次查询。
  • 您必须明智地选择模数。如果表中只有2000条记录,那么您当然不会制造1000个存储桶,但可能会产生100个存储桶,并且一次最多不会请求超过10行。
  • 如果表格不断增长,那么一旦选择的数字可能不再是最佳选择,您可能想要更改它。

上个月的链接:http://rextester.com/VDPIU7354

更新:我刚意识到,如果生成的列不是基于ID的取模,而是基于RAND值,则存储桶实际上是随机的:

alter table mytable add column bucket int generated always as (floor(rand() * 1000)) stored;

但是MySQL抛出错误“生成的列'bucket'的表达包含不允许的函数”。这似乎没有意义,因为使用STORED选项可以确定函数,但至少在版本5.7.12中不起作用。也许在某些更高版本中?