SELECT * FROM table ORDER BY RAND() LIMIT 1 是最直觀的隨機查寫法,但官方文件明確警告:**資料超過 30 萬筆時效能急劇下降**,因 MySQL 必須為全表產生隨機值、排序、再取第一筆。本文提供三種漸進式優化方案,從簡單到高效。
-- 步驟 1:取得 ID 範圍(極快,僅掃描索引)
SELECT MIN(id), MAX(id) FROM big_table;
-- 步驟 2:應用層生成隨機 ID(PHP 範例)
$id = mt_rand($min_id, $max_id);
-- 步驟 3:查詢大於等於隨機 ID 的第一筆
SELECT * FROM big_table WHERE id >= $id LIMIT 1;
優點:僅需一次索引範圍掃描 + 一次主鍵查找,毫秒級完成。
缺點:需兩次往返;若 ID 有間隙,分佈不完全均勻(可接受)。
SELECT * FROM `news` AS t1
JOIN (
SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `news`) - (SELECT MIN(id) FROM `news`)) + (SELECT MIN(id) FROM `news`)) AS id
) AS t2 ON t1.id >= t2.id
LIMIT 10;
子查詢計算隨機 ID 閾值,外層 JOIN 取首筆。適合不想修改應用程式碼的場景。
-- 假設有 uuid 欄位
SELECT * FROM big_table
WHERE uuid >= (SELECT uuid FROM big_table ORDER BY RAND() LIMIT 1)
LIMIT 1;
同樣利用索引範圍掃描,避免全表排序。
| 方法 | 耗時 | 說明 |
|---|---|---|
| ORDER BY RAND() LIMIT 1 | ~12 秒 | 全表掃描 + 檔案排序 |
| 應用層隨機 ID + 主鍵查詢 | ~3 ms | 兩次索引查找 |
| 單條 SQL JOIN 隨機 ID | ~5 ms | 子查詢 + 索引查找 |
WHERE id IN (隨機ID列表),但需確保 ID 存在。TABLESAMPLE(MySQL 8.0.25+ 支援 SYSTEM / BERNOULLI)。WHERE 中直接用 RAND() 比較(如 WHERE RAND() < 0.01),仍會全表掃描。