SELECT * FROM table ORDER BY RAND() LIMIT 5; 看似簡單,實則在大表上是效能殺手:
「你不能在 ORDER BY 子句中使用 RAND() 值使用列,因為 ORDER BY 將重複計算列多次。」
SELECT * FROM `table` AS t1
JOIN (
SELECT ROUND(RAND() * (
(SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`)
) + (SELECT MIN(id) FROM `table`)) AS id
) AS t2 ON t1.id >= t2.id
ORDER BY t1.id LIMIT 1;
SELECT * FROM `table`
WHERE id >= (
SELECT FLOOR(RAND() * (
(SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`)
) + (SELECT MIN(id) FROM `table`))
)
ORDER BY id LIMIT 1;
| 方案 | 平均耗時 | 穩定性 | 適用條件 |
|---|---|---|---|
| ORDER BY RAND() | ~2.5 秒 | 極差 | 僅適合 < 1 萬行 |
| WHERE 子查詢 | 0.5 秒 | 良好 | 需連續 ID、MySQL 4.1+ |
| JOIN 子查詢 | 0.015 秒 | 極佳 | 需連續 ID、MySQL 4.1+ |
id >= 隨機值 的範圍查詢,可使用 Primary Key 索引MIN(id) 和 MAX(id) 計算實際範圍,配合 >= 確保能命中存在的行因單次查詢會返回連續記錄,需執行多次並去重:
<?php
function getRandomRecords($mysqli, $table, $count = 5) {
$ids = [];
$results = [];
$maxAttempts = $count * 3; // 防無限循環
// 先取得 ID 範圍
$range = $mysqli->query("SELECT MIN(id) AS min_id, MAX(id) AS max_id FROM `$table`")->fetch_assoc();
$minId = $range['min_id'];
$maxId = $range['max_id'];
while (count($results) < $count && $maxAttempts-- > 0) {
$randomId = mt_rand($minId, $maxId);
$stmt = $mysqli->prepare("SELECT * FROM `$table` WHERE id >= ? ORDER BY id LIMIT 1");
$stmt->bind_param("i", $randomId);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
if ($row && !in_array($row['id'], $ids)) {
$ids[] = $row['id'];
$results[] = $row;
}
}
return $results;
}
?>
SELECT COUNT(*) 取得總數LIMIT 1 OFFSET $randomORDER BY RAND() 簡單明瞭SRANDMEMBER