ORDER BY RAND() 會為每一行生成隨機數,然後在臨時表中排序,時間複雜度 O(N log N),且無法使用索引。資料量超過 1 萬行時,查詢時間呈指數級增長(15 萬行約 8 秒)。
CREATE TABLE rand_number (num INT PRIMARY KEY);
INSERT INTO rand_number VALUES (-5),(-4),..., (5);
SELECT num FROM rand_number ORDER BY RAND() LIMIT 1;
-- 取 -5 到 5 的隨機整數
SELECT ROUND((0.5 - RAND()) * 2 * 5);
SELECT t1.*
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;
核心原理:子查詢僅掃描索引計算隨機 ID,主查詢利用主鍵索引定位,避免全表排序。實測 15 萬行從 8 秒降至 0.01 秒。
-- 1. 取得 ID 範圍
SELECT MIN(id), MAX(id) FROM `table`;
-- 2. PHP 生成 20 個隨機 ID(多取幾個防止刪除導致不足)
$idList = implode(',', array_map(fn() => mt_rand($min, $max), range(1, 20)));
-- 3. 單次查詢並保持隨機順序
SELECT * FROM `table`
WHERE id IN ($idList)
ORDER BY FIELD(id, $idList)
LIMIT 12;
| 方案 | 15萬行單次耗時 | 適用場景 |
|---|---|---|
| ORDER BY RAND() | 8 秒+ | 極小資料集(<1萬) |
| WHERE id >= RAND()*MAX(id) | 0.5 秒 | 簡單隨機單筆 |
| JOIN + 索引 | 0.01 秒 | 百萬級單筆/少量隨機 |
| 應用層 ID 列表 + IN | 0.001 秒 | 高併發、批量隨機展示 |