【问题标题】:MySQL: what's the most efficient way to select multiple random rowsMySQL:选择多个随机行的最有效方法是什么
【发布时间】:2012-04-20 17:47:51
【问题描述】:

我有一个相当大的数据集和一个需要两个连接的查询,所以查询的效率对我来说非常重要。我需要从数据库中检索 3 个满足基于连接结果的条件的随机行。 Most obvious solution 被指出效率低下 here,因为

[这些解决方案] 需要对所有表进行顺序扫描(因为需要计算与每一行关联的随机值 - 以便可以确定最小的值),即使是中等大小的表也可能会非常慢。

但是,那里的作者建议的方法(SELECT * FROM table WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table) LIMIT 1,其中 num_value 是 ID)对我不起作用,因为某些 ID 可能丢失(因为某些行可能已被用户删除)。

那么,在我的情况下,检索 3 个随机行的最有效方法是什么?

编辑: 解决方案不需要是纯 SQL。我也使用 PHP。

【问题讨论】:

  • 这需要纯 SQL 还是您可以访问脚本语言?
  • 如果您遗漏了一些 ID 值,并且无法知道哪些 ID 值,除了全部扫描,那么您必须全部扫描……所以这是最有效的方法。 (“逻辑!”):)
  • 或者重试样本。哪个可能更快,……或者不是。

标签: mysql sql random


【解决方案1】:

由于您不想要很多结果,因此有几个有趣的选项可以使用 LIMITOFFSET

我将假设一个 id 列是唯一的并且适合排序。

第一步是执行一个COUNT(id),然后在PHP中从0COUNT(id) - 1中随机选择3个数字。 (如何做到这一点是一个单独的问题,最佳方法取决于总行数和您想要的数量)。

第二步有两个选项。假设您选择的随机数是 0、15、2234。在 PHP 中都有一个循环

// $offsets = array(0, 15, 2234);
foreach ($offsets as $offset) {
    $rows[] = execute_sql('SELECT ... ORDER BY id LIMIT 1 OFFSET ?', $offset);
}

或建立一个UNION。注意:这需要子选择,因为我们使用的是 ORDER BY。

// $offsets = array(0, 15, 2234);
$query = '';
foreach ($offsets as $index => $offset) {
    if ($query) $query .= ' UNION ';
    $query .= 'SELECT * FROM (SELECT ... ORDER BY id LIMIT 1 OFFSET ?) Sub'.$index;
}
$rows = execute_sql($query, $offsets);

【讨论】:

    【解决方案2】:

    将您的 RAND() 调用添加到 ORDER BY 子句中应该允许您忽略 ID。试试这个:

    SELECT * FROM table WHERE ... ORDER BY RAND() LIMIT 3;
    

    在指出性能问题后,您最好的选择可能是这些方面的东西(使用 PHP):

    $result = PDO:query('SELECT MAX(id) FROM table');
    $max    = $result->fetchColumn();
    $ids    = array();
    $rows   = 5;
    
    for ($i = 0; $i < $rows; $i++) {
        $ids[] = rand(1, $max);
    }
    
    $ids     = implode(', ', $ids);
    $query   = PDO::prepare('SELECT * FROM table WHERE id IN (:ids)');
    $results = $query->execute(array('ids' => $ids));
    

    此时您应该能够选择前 3 个结果。这种方法的唯一问题是处理已删除的行,如果您没有收到至少 3 个结果,您可能必须增加 $rows 变量或添加一些逻辑来执行另一个查询。

    【讨论】:

    • 这似乎效率很低,因为正如我链接的页面上所述,它强制对整个表进行顺序扫描(在我的情况下很大)。还是我误解了你的回答?
    • 似乎确实如此。我不认为有一个好方法可以完全通过 MySQL 来做到这一点,同时仍然考虑到已删除的 ID。您最好的选择可能是选择最高 ID,然后选择 > 3 个随机 ID,以便您有足够的时间来处理已删除的行。
    • 此代码返回的行数也可能少于预期,因为随机 ID 生成器不会检查 ID 是否尚未生成 - WHERE id IN (1,1,2) 将返回 2 行。
    猜你喜欢
    • 2012-04-08
    • 1970-01-01
    • 2021-04-27
    • 2012-01-30
    • 1970-01-01
    • 2020-04-22
    • 2020-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多