2012-03-28 144 views
5

我正在尋找一種從MySQL表中隨機選擇100行滿足特定條件的有效方法,這些行可能有數百萬行。如何從MySQL表中隨機選擇滿足特定條件的多行?

幾乎所有我發現的建議避免使用ORDER BY RAND(),因爲性能和可伸縮性較差。

但是,this article建議ORDER BY RAND()仍然可以用作獲取randow數據的「快捷方式」。

基於這篇文章,下面是一些示例代碼,顯示了我試圖完成的任務。我的問題是:

  1. 這是從一個表,可能有數百萬行隨機選擇100(或多達幾百個)行的有效途徑?

  2. 什麼時候成績會成爲問題?

 
    SELECT user.* 
    FROM (
      SELECT id 
      FROM user 
      WHERE is_active = 1 
      AND  deleted = 0 
      AND  expiretime > '.time().' 
      AND  id NOT IN (10, 13, 15) 
      AND  id NOT IN (20, 30, 50) 
      AND  id NOT IN (103, 140, 250) 
     ORDER BY RAND() 
      LIMIT 100 
      ) 
      AS  random_users 
    STRAIGHT JOIN user 
    ON  user.id = random_users.id 
+0

在索引字段上選擇隨機值是有意義的。 – Kayser 2012-03-28 16:52:34

+0

@Kayser,我擔心我們仍然需要掃描WHERE條件的所有行。這是否會影響性能與巨大的表(可能數百萬行)? – user1298692 2012-03-28 17:05:04

+0

使用pk-subselect的方法很可能只會稍微減少執行時間。這是因爲無論使用或不使用這種技術,都會爲所有匹配的行調用rand(),並且要排序的行數相同。假設「用戶」有很多列或者大的列,並且mysql在LIMIT發生後沒有足夠的智能來實現用戶*(這應該被測試),那麼這很有趣。 – 2012-03-28 19:49:33

回答

0

恐怕沒有人會能夠作出準確回答你的問題。如果你真的想知道你需要針對你的系統運行一些基準測試(而不是理想的實況測試,但確切的副本)。基於不同解決方案(例如,使用PHP獲取隨機行),並將這些數據與您/您的客戶認爲的「良好性能」進行比較,然後提高您的數據,儘量保持列值的分佈接近真實你可以看到性能開始下降,說實話,如果它現在對你有一定的提升空間,那麼我會努力去做,當(如果!)它成爲瓶頸,那麼你可以看看它再次 - 或者只是扔掉多餘的鐵在你的數據庫...

1

強烈敦促您閱讀本article的最後一段將是覆蓋多個隨機行的選擇,你應該能夠注意到SELECT聲明。將在那裏描述的PROCEDURE這將是你添加特定的WHERE con的地方揚長避短。

ORDER BY RAND()的問題在於此操作的複雜度爲n*log2(n),而我鏈接的文章中描述的方法具有幾乎不變的複雜度。

讓我們假設,在選擇從表,其中包含10個條目,使用ORDER BY RAND()隨機行佔用1 time unit

entries | time units 
------------------------- 
     10 |   1  /* if this takes 0.001s */ 
     100 |  20 
    1'000 |  300 
    10'000 |  4'000 
    100'000 | 50'000 
1'000'000 | 600'000  /* then this will need 10 minutes */ 

而且你寫的,你是對的數以百萬計的規模處理表。

0

預處理儘可能 嘗試像(VB類爲例)

Dim sRND = New StringBuilder : Dim iRandom As New Random() 
Dim iMaxID As Integer = **put you maxId here** 
Dim Cnt as Integer=0 
While Cnt < 100 
     Dim RndVal As Integer = iRandom.Next(1, iMaxID) 
     If Not ("10,13,15,20,30,50,103,140,250").Contains(RndVal) Then 
      Cnt += 1 
      sRND.Append("," & RndVal) 
     end if 
End While 
String.Format("SELECT * FROM (Select ID FROM(User) WHERE(is_active = 1) AND deleted = 0 AND expiretime > {0} AND id IN ({1}) .blahblablah.... LIMIT 100",time(), Mid(sRND.ToString, 2)) 

我沒有檢查語法,但你會明白我的意思,我希望。 這將使MySql讀取符合'IN'的記錄,並在達到100時停止,而無需先預處理所有記錄。

請讓我知道經過時間的差異,如果你嘗試它。 (我是qurious)

相關問題