【问题标题】:Is this a fast method to select random row from a table这是从表中选择随机行的快速方法吗
【发布时间】:2013-05-26 13:52:54
【问题描述】:

我读过一篇关于如何从包含大量数据的 SQL 表中随机选择一行(在某些条件下)的文章。他们得到 id 的最大值和最小值,并在它们之间生成一个随机数,并得到第一行的 id 比那个大。但是,我的 id 分布不均匀,所以我没有得到真正随机的行。例如,如果我的 id 是 1、100、101,我将几乎没有机会获得后面的两行。

但我想到了另一种解决方案。我没有获取最大 id,而是计算查询中的所有行,获取一个随机数 i 并选择第 i 个。代码是这样的

$count_res = $mysqli->query("SELECT COUNT(*) FROM quest WHERE category IN ({$mem['my_cate']})");
$count = $count_res->fetch_array();
$rand_id = rand(0, $count[0] - 1);

$result = $mysqli->query("SELECT * FROM quest WHERE category IN ({$mem['my_cate']}) LIMIT 1 OFFSET $rand_id");

但是,我怀疑它的有效性。任何人都可以给我一些想法,或者为我的案例提出解决方案。谢谢。

【问题讨论】:

  • 当我需要一个类似的查询时,我研究了一段时间,这与我最终采用的方法相同。如果您使用的是 MISAM,count() 会很快,并且您没有使用 ORDER BY,这会有所帮助。你做过一些基准测试吗?
  • 使用大的OFFSET 会导致延迟,因为 MySQL 必须实际遍历结果集才能到达请求的位置(与选择大于给定值的第一个 ID 不同,它可以检查找到正确行的索引)。
  • @markdwhite:不,我只在我的小桌子上测试过。
  • @Amber:我们有解决方案吗?
  • @dvtrung94 并非如此,除了向您的数据添加索引且均匀分布的 id 列。

标签: php mysql


【解决方案1】:

好的,我已经做了一些基准测试。我创建了一个表,其中只有一列具有自动增量 ID。然后我添加了 1,700,000 条记录。由于只有一列,我想它会比实际更快,但这是我的基准测试:

方法一: 选择行数,然后使用 PHP 选择一个随机数,然后根据偏移量进行选择。 (我将偏移量设置在表格的末尾,因为它会比表格的开头慢)。

选择计数:12ms

选择偏移:513ms

总计:525 毫秒

方法2:在整个表上用RAND()选择1。

总计:2,190 毫秒

获胜者 = 方法 1

可能的方法 3: 这只是我想出来的,不一定适用于所有情况。所以想法是你得到表中最后一个自动增量 id,生成一个介于 1 和最后一个自动增量号之间的随机数,然后选择大于或等于该 id 号的第一行。您必须做大于或等于,因为可能会缺少 ID 号。

选择最后一个id:10.1ms

选择随机行:6.3ms

总计:16.4 毫秒

【讨论】:

  • 非常感谢。不幸的是,我不能使 id 的分布均匀(因为我在某些条件下从表中选择行)。也许我会根据返回表的大小考虑使用这两种方法。
  • 这很有趣,我不认为使用方法 3 差距越大,被选中的行的几率就越大
【解决方案2】:

使用类似的东西可能会更快:

$result = $mysqli->query("SELECT * FROM quest WHERE category IN ({$mem['my_cate']}) ORDER BY Rand() LIMIT 1");

因为它只使用一个查询,您可以跳过该顶部。您可以通过在循环中尝试两种方式来对其进行基准测试,几千次或您决定的任何次数,然后比较循环前后的 microtime()。

【讨论】:

  • 按 rand() 排序可能是解决这个问题的最低效的方法。要按 rand 排序,数据库必须遍历整个数据集并为每条记录分配一个随机数,以便对其进行排序。