-- 返回隨機 10 行
SELECT TOP 10 * FROM dbo.Orders ORDER BY NEWID();
-- 隨機排序全表
SELECT * FROM dbo.Orders ORDER BY NEWID();
NEWID() (生成唯一標識符),作為計算列。複雜度:O(N log N) 排序 + O(N) NEWID() 計算,隨表增長性能急劇下降。
| 查詢 | 邏輯讀 | CPU (ms) | 耗時 (ms) | TempDB 使用 |
|---|---|---|---|---|
TOP 10 ORDER BY NEWID() | 350,000 | 12,000 | 8,500 | 120 MB |
TABLESAMPLE (10 ROWS) | 120 | 5 | 3 | 0 |
BERNOULLI 0.001% | 8,000 | 50 | 40 | 0 |
-- 系統採樣:按頁採樣,同頁行要么全選要么全不選
SELECT * FROM dbo.Orders TABLESAMPLE SYSTEM (10 ROWS); -- 近似 10 行
SELECT * FROM dbo.Orders TABLESAMPLE BERNOULLI (0.01); -- 每行獨立 0.01% 概率
SYSTEM 非真隨機 (頁級相關);BERNOULLI 概率性,行數不定。WHERE 過濾後採樣 (需子查詢/視圖)。
-- 前提:主鍵 ID 連續或近似連續 (IDENTITY)
DECLARE @MinId BIGINT, @MaxId BIGINT, @RandomId BIGINT;
SELECT @MinId = MIN(Id), @MaxId = MAX(Id) FROM dbo.Orders;
SET @RandomId = @MinId + CAST(RAND() * (@MaxId - @MinId) AS BIGINT);
SELECT TOP 10 *
FROM dbo.Orders
WHERE Id >= @RandomId
ORDER BY Id;
-- 若不足 10 行,可 UNION ALL 再從頭取
ALTER TABLE dbo.Orders ADD RandVal BINARY(16) DEFAULT NEWID(); -- 或 CHECKSUM(NEWID())
CREATE INDEX IX_Orders_RandVal ON dbo.Orders (RandVal);
-- 查詢
SELECT TOP 10 * FROM dbo.Orders ORDER BY RandVal; -- 索引順序掃描,無排序!
-- 定期更新 RandVal 刷新隨機性
UPDATE dbo.Orders SET RandVal = NEWID(); -- 大表分批更新
Index Scan + Top,無 Sort,性能穩定 O(log N + K)。WHERE 過濾後隨機:WHERE Status=1 ORDER BY RandVal。
-- 1. 離線任務:每日生成隨機 ID 列表存入 Redis/配置表
-- SELECT Id FROM dbo.Orders WHERE CreateDate >= '2024-01-01' ORDER BY NEWID() LIMIT 10000;
-- 2. 應用層從列表隨機取 N 個 ID → 批量查詢
-- SELECT * FROM dbo.Orders WHERE Id IN (id1, id2, ...);
表大小 ≤ 10 萬行 → 直接 ORDER BY NEWID() 簡單可維護 表 10 萬 - 100 萬行 → TABLESAMPLE BERNOULLI / 主鍵隨機跳躍 表 > 100 萬行、需精確隨機 → 預計算 RandVal 列 + 索引 / 離線 ID 列表 表 > 100 萬行、允許近似 → TABLESAMPLE SYSTEM (最快)
RAND() 查詢中只計算一次,全表同一值,**不可用於 ORDER BY RAND()**。NEWID() 非確定性函數,導致計劃無法復用,高併發下編譯鎖爭用。ORDER BY NEWID() 不改變本質,仍是全掃描排序。隨機抽樣看似簡單,實則是「統計學隨機性」與「工程學性能」的博弈。**大表嚴禁直接 ORDER BY NEWID()**,請根據業務對隨機性精度要求、表規模、寫入頻率,選擇 TABLESAMPLE、主鍵跳躍、預計算列或離線 ID 列表方案,實現毫秒級隨機查詢體驗。