2014-02-09 191 views
5

我建立一個網站,我需要從數據庫中選擇隨機加權記錄 。大數據庫快速mysql隨機加權選擇

還有就是代碼SQL : select one row randomly, but taking into account a weight

SELECT t.*, RAND() * t.weight AS w 
FROM table t 
ORDER BY w DESC 
LIMIT 1 

它適用於記錄小樣本罰款文檔片斷。

嘗試接近100萬條記錄時,它在本地機器上變慢(1.3 - 1.8秒) ,我想我會在更大的機器上花費更長的時間。

它如何優化? 有沒有更好的方法隨機選擇加權記錄?

我的嘗試是定期計算權重,將它們存儲在單獨的表中,選擇隨機數programmaticaly並搜索最接近該記錄的記錄。

回答

1

您可以根據權重對數據進行分區,然後隨機選擇一個分區。

確定要使用的分區:O(n)的

SELECT Weight, FLOOR(RAND()*COUNT(*)) as Target 
FROM test 
GROUP BY Weight 
ORDER BY RAND()*(Weight)*count(Weight)/100 DESC 
LIMIT 1; 

使用權,並從以前的查詢目標得到的結果:O(日誌(n))的

SELECT test.* 
FROM test 
WHERE Weight = $Weight 
LIMIT $Target, 1 

測試:

CREATE TABLE `test` (
    `Id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `Weight` int(11) NOT NULL, 
    PRIMARY KEY (`Id`), 
    KEY `Weight` (`Weight`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 


insert into test (Weight) (select FLOOR(RAND()*1000)); 

運行20次,創造100萬個測試行:

insert into test (Weight) select FLOOR(rand()*1000) as Weight from test; 

由於GROUP BY,第一個查詢以O(n)運行。如果您維護一個記錄每個權重計數的第二個表,您可以將其記錄到log(n)運行時間。

我與第一個查詢中(6.089 s)運行測試表800萬行和(0.001 s)

0

第一第二數據庫中獲取所有的權重的總和,這樣就可以計算出每一行的概率選擇上蒼蠅。

SELECT SUM(weight) FROM t; 

我假設款額是通過名爲mysql的變量訪問@TOTAL_WEIGHT

SELECT t.* 
FROM t 
WHERE RAND() <= (weight/@TOTAL_WEIGHT) 
ORDER BY RAND() 
LIMIT 1; 

有一個機會,這個經歷整個表,仍然沒有找到一個匹配,在哪種情況下你可能只是運行另一個查詢來獲得一個隨機行。