2016-09-07 118 views
2

如果已經完成了該主題,但我很苦惱從大型MySQL表中選擇一個隨機行,我非常抱歉。這是一個名爲photos的表,其主鍵是PhotoID。目前它的ID範圍從〜1500(由於在測試中創建的行被刪除)至〜12000,有一些差距,我預計它會變得更大。MySQL - 從大表中選擇隨機行

雖然它已經比較小我一直在使用:

SELECT PhotoID FROM photos 

...到一個PHP數組$All_IDs,然後在PHP中:

$RandomID = $All_IDs[mt_rand(0,count($All_IDs)-1)] 

則:

SELECT /* other columns */ FROM photos WHERE PhotoID = $RandomID 

這很好用,當我重複它時,我會得到一系列隨機照片。但是我不認爲這樣做會非常有效地加載整個PhotoID列來選擇一個隨機ID,然後是另一個查詢來獲得該記錄,特別是如果我要選擇幾個。同樣,我寧願不選擇整個表格(所有列)到一個數組中以挑選出一個。與其他幾個StackOverflow的答案的幫助下,我想出了以下內容:

SELECT MIN(PhotoID) INTO @MinID FROM photos; 
SELECT MAX(PhotoID) INTO @MaxID FROM photos; 
SELECT PhotoID,/* other columns */ FROM photos WHERE PhotoID >= (@MinID + RAND() * (@MaxID - @MinID)) ORDER BY PhotoID LIMIT 0,1 

我認爲這會工作,但我發現重複此查詢幾次只給我ID的短蔓延,在1500 - 1700的範圍內,如上所述,身份證目前將達到12,000。我無法理解這是爲什麼?

回答

1

我懷疑你看到那個值的小範圍,因爲RAND()(在WHERE條款)正在評估表中的每一行。而且該行上的PhotoID更有可能大於右側表達式返回的較低值。所以查詢返回的是一個更偏重於較低PhotoID值的集合。用ORDER BY,你會得到最低的。

爲了獲得更隨機的分佈,你需要讓RAND()只計算一個時間。另外,當我可以在單個語句中完成工作並且沒有用戶定義的變量時,我寧願不執行多個查詢(三個單獨的SELECT語句)。

要實現它看起來像你正試圖實現的算法,我想接近它是這樣的:

SELECT t.photoid 
     , ... 
    FROM photos t 
    JOIN (SELECT m.min_id + RAND() * (max_id - min_id) AS _rand 
      FROM (SELECT MIN(p.photoid) AS min_id 
         , MAX(p.photoid) AS max_id 
         FROM photos p 
        ) m 
     ) r 
     ON r._rand <= t.photoid 
    ORDER BY t.photoid 
    LIMIT 1 

在MySQL中,內嵌視圖(在MySQL的說法派生表)會首先在外部查詢之前進行物化。由於m返回單個行,因此r中的RAND()函數將僅評估一次。然後表達式中的單個值將用於外部查詢中。

+0

這很理想,謝謝...我避免了ORDER BY RAND(),因爲我知道每行都會調用RAND(),但是我認爲只有在它是WHERE子句的一部分時纔會調用它。我從未想過以這種方式使用'JOIN'。 – Iain

+0

注意:這種方法不一定是返回一組隨機行的最佳方法。我試圖解釋原始查詢中觀察到的行爲的原因,以及實現看起來是原始查詢的預期設計的示例。 (在這種情況下使用JOIN是可行的,因爲內聯視圖'r'將返回一行。)如果您需要使用多條語句(如原始語句),請將RAND()操作移出單獨的語句,並且將*單個*靜態值傳遞給實際查詢。這個答案中的查詢就是這樣做的。) – spencer7593

0

嘗試此查詢:

select * from photos order by rand() limit 1; 
+0

請注意,MySQL將評估表中每*行的RAND()函數。然後結果集將需要使用「使用filesort」操作來識別具有最低RAND()值的行。這種方法往往不能很好地適應大型集合。 – spencer7593

+0

http://jan.kneschke.de/projects/mysql/order-by-rand/ – spencer7593