3

我有一个相当大的数据集和一个需要两个连接的查询,所以查询的效率对我来说非常重要。我需要从数据库中检索 3 个满足基于连接结果的条件的随机行。最明显的解决方案在这里被指出是低效的,因为

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

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

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

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

4

2 回答 2

3

由于您不想要很多结果,因此使用LIMIT和有几个有趣的选项OFFSET

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

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

第二步有两个选择。假设您选择的随机数是 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);
于 2012-09-04T11:06:01.853 回答
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 变量或添加一些逻辑来执行另一个查询。

于 2012-04-07T13:53:02.597 回答