MySQL 手冊明確警告:ORDER BY RAND() 會導致該欄位被多次掃描,且無法使用索引。15 萬筆資料取 5 筆耗時 8 秒以上,隨資料量線性惡化。
-- 產生連續 5 筆,非真隨機
SELECT * FROM t WHERE id >= (SELECT ROUND(RAND() * (SELECT MAX(id) FROM t))) ORDER BY id LIMIT 5;
需執行 5 次單筆查詢才能得到 5 筆不連續資料,雖單次 0.01s 但總耗時仍可觀。
SELECT * FROM t
WHERE id >= (SELECT FLOOR(MAX(id) * RAND()) FROM t)
ORDER BY id LIMIT 1;
SELECT * FROM t
WHERE id >= (SELECT FLOOR(RAND() * ((SELECT MAX(id) FROM t) - (SELECT MIN(id) FROM t)) + (SELECT MIN(id) FROM t)))
ORDER BY id LIMIT 1;
加入 MIN(id) 修正 ID 不從 1 開始的偏移問題,大幅提速。
SELECT *
FROM t AS t1
JOIN (
SELECT ROUND(RAND() * ((SELECT MAX(id) FROM t) - (SELECT MIN(id) FROM t)) + (SELECT MIN(id) FROM t)) AS id
) AS t2 ON t1.id >= t2.id
ORDER BY t1.id LIMIT 1;
| 寫法 | 總耗時 | 平均/次 |
|---|---|---|
| WHERE 子查詢版本 2 | 0.147s | 0.0147s |
| JOIN 版本 3 | 0.015s | 0.0015s |
JOIN 寫法快 10 倍,因優化器能更好處理衍生表物化與索引下推。
SELECT *
FROM `table` AS t1
JOIN (
SELECT FLOOR(RAND() * (
(SELECT MAX(id) FROM `table`) - (SELECT MIN(id) FROM `table`)
) + (SELECT MIN(id) FROM `table`)) AS rand_id
) AS t2 ON t1.id >= t2.rand_id
ORDER BY t1.id LIMIT 1;
SELECT * FROM t WHERE RAND() < 0.01 LIMIT 1000 (伯努利取樣),近似隨機且極快,適合統計分析。TABLESAMPLE SYSTEM (10) REPEATABLE (42) (需特定引擎支援) 或應用層抽取 ID 列表再 IN 查詢。拒絕 ORDER BY RAND()。採用「JOIN + 子查詢隨機主鍵定位 + 範圍掃描」模式,配合 MIN/MAX(id) 修正邊界,單筆隨機查詢可達毫秒級。大量取樣請改用伯努利取樣或應用層抽樣策略。