ORDER BY 子句中不可使用 RAND()。利用主鍵 id 連續性(或近似),在應用層產生隨機 ID,再回表查詢。
-- 適用於 AUTO_INCREMENT 主鍵、刪除較少、ID 分布均勻
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;
原理:子查詢計算隨機 ID 範圍,外層查詢走主鍵索引 range 掃描,極快(< 0.01 秒)。
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;
實測 10 次平均 0.015 秒,比 WHERE 子查詢版(0.147 秒)快 10 倍。
方法 A:應用層循環 N 次方案一(推薦,簡單高效)。
方法 B:單次查詢(複雜):
SELECT * FROM `table` WHERE id IN (
SELECT FLOOR(RAND() * (max_id - min_id) + min_id)
FROM (SELECT MAX(id) max_id, MIN(id) min_id FROM `table`) t
-- 重複 N 次 UNION ALL 或用變數生成 N 個隨機數
);
random_picks (id PRIMARY KEY),定期任務填入所有 ID。SELECT id FROM random_picks ORDER BY RAND() LIMIT N(輔助表較小)。TABLESAMPLE(MySQL 8.0.23+ 支援 BERNOULLI/SYSTEM 採樣)。| 方案 | 單次耗時 | 適用場景 | 備註 |
|---|---|---|---|
| ORDER BY RAND() LIMIT 1 | 8.0 秒 | 嚴禁生產 | 全表掃描+排序 |
| WHERE id >= (RAND*MAX) | 0.5 秒 | 簡單場景 | 子查詢優化不穩 |
| JOIN (RAND*MAX) | 0.01 秒 | 生產首選 | 穩定走主鍵索引 |
| 應用層隨機 ID + IN | 0.001 秒 | 多行隨機 | N 次點查極快 |
MIN(id) 判斷,否則 50% 機率落在表頭前幾行。random_int()、Java SecureRandom)。拒絕 ORDER BY RAND(),擁抱索引友好的隨機查詢模式。