【问题标题】:Getting random results from large tables从大表中获取随机结果
【发布时间】:2012-09-03 14:49:06
【问题描述】:

我正在尝试从包含大约 700 万条记录的表中获取 4 个随机结果。此外,我还想从同一个表中获取 4 条按类别过滤的随机记录。

现在,正如您想象的那样,对这么大的表进行随机排序会导致查询花费几秒钟,这并不理想。

我为 non-filtered 结果集想到的另一种方法是让 PHP 选择 1 - 7,000,000 左右之间的一些随机数,然后使用查询执行 IN(...) 以仅获取这些行 - 和是的,我知道这种方法有一个警告,如果具有该 id 的记录不再存在,您可能会得到少于 4 个。

但是,上述方法显然不适用于类别过滤,因为 PHP 不知道哪些记录号属于哪个类别,因此无法选择要从中选择的记录号。

有没有更好的方法可以做到这一点?我能想到的唯一方法是将每个类别的记录 ID 存储在另一个表中,然后从中选择随机结果,然后在辅助查询中仅从主表中选择那些记录 ID;但我敢肯定还有更好的方法!?

【问题讨论】:

    标签: php mysql large-data-volumes


    【解决方案1】:

    对于 MySQl,您可以使用

    兰德()

    SELECT column FROM table
    ORDER BY RAND()
    LIMIT 4
    

    【讨论】:

      【解决方案2】:

      您当然可以在使用LIMITWHERE(用于类别)的查询中使用RAND() 函数。但是,正如您所指出的,这需要对数据库进行扫描,这需要时间,尤其是在您的情况下,由于数据量很大。

      正如您所指出的,您的另一种选择是将 id/category_id 存储在另一个表中可能会更快一些,但该表上必须有一个 LIMITWHERE 也将包含相同的数量记录作为主表。

      另一种方法(如果适用)是为每个类别创建一个表并在其中存储 ID。如果您的类别是固定的或不经常更改,那么您应该能够使用该方法。在这种情况下,您将有效地从子句中删除 WHERE,并在每个类别表上获得带有 LIMITRAND() 会更快,因为每个类别表都将包含主表中的记录子集。

      其他一些替代方法是仅针对该操作使用键/值对数据库。 MongoDb 或 Google AppEngine 可以提供帮助,而且速度非常快。

      您也可以在 MySQL 中采用主/从的方法。从站实时复制内容,但是当您需要执行昂贵的查询时,您查询的是从站而不是主站,从而将负载传递到另一台机器。

      最后,您可以使用更易于安装和维护的 Sphinx。然后,您可以将这些类别查询中的每一个视为文档搜索,并让 Sphinx 随机化结果。这样你就可以将这个昂贵的操作转移到不同的层,让 MySQL 继续执行其他操作。

      只是一些需要考虑的问题。

      【讨论】:

      • 感谢所有伟大的想法。 :)
      【解决方案3】:

      使用随机数方法

      • 获取数据库中的最大 id。
      • 创建一个临时表来存储您的匹配项。
      • 循环 n 次,执行以下操作
        • 生成一个介于 1 和 maxId 之间的随机数
        • 获取记录 ID 大于随机数的第一条记录并将其插入临时表中
      • 您的临时表现在包含您的随机结果。

      或者您可以使用联合动态生成 sql 以一步完成查询。

         SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
         UNION
         SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
         UNION
         SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
         UNION
         SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
      

      注意:我的sql可能无效,因为我不是mysql的人,但理论应该是合理的

      【讨论】:

      • 一个问题可能是两次生成相同的随机数,尽管可能性不大。
      【解决方案4】:

      首先你需要获取行数......像这样

      select count(1) from tbl where category = ? 然后选择一个随机数

      $offset = rand(1,$rowsNum); 并选择具有偏移量的行

      select * FROM tbl LIMIT $offset, 1

      这样可以避免丢失 ID。唯一的问题是您需要多次运行第二个查询。在这种情况下,联合可能会有所帮助。

      【讨论】:

        猜你喜欢
        • 2016-05-27
        • 2019-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-15
        • 1970-01-01
        相关资源
        最近更新 更多