2010-08-03 76 views
3

我希望能夠從數據庫中取回15條左右的記錄。我已經看到,使用WHERE id = rand()會導致性能問題,因爲我的數據庫變得更大。我所見過的所有解決方案都適用於選擇一個隨機記錄。我想獲得倍數。從mysql數據庫返回隨機行而不使用rand()

有誰知道有效的方法來做到這一點的大型數據庫?

編輯:

進一步編輯和測試:

我使用的MyISAM做了一個非常簡單的表格,一個新的數據庫上。我給了這3個字段:autokey(無符號自動數字鍵)bigdata(一個大的斑點)和somemore(一箇中等的int)。 然後,我將隨機數據應用到表中,並使用Navicat運行一系列查詢。下面是結果:

Query 1: select * from test order by rand() limit 15

Query 2: select * 
      from 
     test 
      join 
     (select round(rand()*(select max(autokey) from test)) as val from test limit 15)           as rnd 
     on 
      rnd.val=test.autokey;` 

(我試過兩個選擇,選擇不同的和它並沒有明顯的差異)

和:

Query 3 (I only ran this on the second test): 
SELECT * 
    FROM (
    SELECT @cnt := COUNT(*) + 1, 
      @lim := 10 
    FROM test 
    ) vars 
    STRAIGHT_JOIN 
    (
    SELECT r.*, 
      @lim := @lim - 1 
    FROM test r 
    WHERE (@cnt := @cnt - 1) 
      AND RAND(20090301) < @lim/@cnt 
    ) i 
 
ROWS:   QUERY 1:    QUERY 2:   QUERY 3: 
2,060,922   2.977s     0.002s   N/A 

3,043,406   5.334s     0.001s   1.260  

我想要做更多的行,所以我可以看到查詢3是如何縮放的,但是在這個時候電話,它似乎好像清晰的贏家是查詢2

我之前包裹了這個測試,並宣佈一個答案,而我擁有所有這些數據和測試環境的搭建,任何人都可以提出任何進一步的測試?

+0

這些可能會有所幫助:http://stackoverflow.com/questions/142242/what-是最好的方式來挑選一個隨機排從一個表在MySQL中,http://stackoverflow.com/questions/1244555/how-can-i-optimize-mysqls- order-by-rand-function,http://stackoverflow.com/questions/1868102/order-by-rand-alternative – 2010-08-03 15:12:06

回答

5

嘗試:

select * from table order by rand() limit 15 

另一個(也可能是更有效的方式)將加入一組隨機值。如果表中有一些連續的整數鍵,這應該起作用。下面是我會怎麼做它Postgres的(我的MySQL是一個有點生疏)

select * from table join 
    (select (random()*maxid)::integer as val from generate_series(1,15)) as rnd 
    on rand.val=table.id; 

其中maxid是table最高id。如果id有索引,那麼這意味着只有15個索引查找,所以它非常快。

UPDATE

看起來有沒有這樣的事情在MySQL generate_series。我的錯。我們實際上並不需要它:

select * 
from 
table 
join 
-- this just returns 15 random numbers. 
-- I need `table` here only to produce rows for rand() 
(select round(rand()*(select max(id) from table)) as val from table limit 15) as rnd 
on 
rnd.val=table.id; 

P.S.如果我不想重複返回,我可以在隨機生成器表達式中使用(選擇不同的[...])。

2

更新:退房接受的答案在this question。這是純粹的mySQL,甚至可以處理均勻分佈。

id = rand()或任何與PHP相當的問題是,您無法確定該特定ID是否仍然存在。因此,您需要使用LIMIT,對於大量數據可能會變慢。

作爲替代方案,您可以嘗試在PHP中使用循環。

什麼環路的作用是

  • 創建使用rand()隨機整數,與和0之間的範圍的記錄在數據庫中

  • 查詢數據庫是否與記錄中的號碼ID存在

  • 如果存在,則將該數字添加到數組中

  • 如果沒有,返回到步驟1

  • 結束循環時的隨機數的數組包含元素

所需數量的這種方法可能會導致大量的查詢以碎片化的表格,但它們應該很快執行。它可能在某些情況下比LIMIT rand()更快。

由@Luther概述的LIMIT方法當然是最簡單的代碼方式。

0

你可以做一個查詢的所有結果或然而,許多有限,則使用mysqli_fetch_all依次爲:

shuffle($a); 
$a = array_slice($a, 0, 15); 
+0

這幾乎沒有記憶效率 - 他問的是大量的數據。 – 2010-08-03 15:20:59

+0

他的確說過他只想要15行,它依賴於查詢返回的數量 – johnjoejohnjoe 2010-08-03 15:23:10

0

對於大型數據集做

select * from table order by rand() limit 15 

可以說是相當的時間和內存消耗。

如果您的數據記錄發生在被編號,你可以把和索引上的編號科拉姆並做

select * from table where no >= rand() limit 15 

甚至更​​好做你的應用程序中的隨機數生成並做

select * from table where no >= $rand and no <= $rand+15 

如果您的數據沒有經常更改,則可能需要在列中添加這樣的編號以使選擇更有效率。

+1

但這不是真正的15行隨機數。它是一個隨機行,隨後是結果中的下一個14行,如果$ rand恰好落在表中最後15條記錄的範圍內,它也不起作用。 – 2010-08-04 03:45:09

0

假設MySQL支持嵌套查詢和主鍵操作是快,我會嘗試像

select * from table where id in (select id from table order by rand() limit 15)