2016-07-26 58 views
2

每次都必須在得到從表中10個隨機行,但是當我重複查詢行永遠不會重複。從SQL Server獲取隨機數據,但沒有重複值

但是,如果我得到所有的行,它會從一個重複,如表中有20行,第一次我得到10個隨機行,第二次,我將需要得到剩餘10行,並在我的第三個查詢我需要隨機獲得10行。

目前我用於獲取10行隨機查詢:

SELECT TOP 10 * 
FROM tablename 
ORDER BY NEWID() 

但MSDN建議此查詢

SELECT TOp 10 * FROM Table1 
    WHERE (ABS(CAST(
    (BINARY_CHECKSUM(*) * 
    RAND()) as int)) % 100) < 10 

獲得良好的性能。但是這個查詢不會返回常量行。能否請您提出這個

+0

顯示樣本數據請。 – NEER

回答

2

東西因爲你的第二個查詢所需要的結果取決於第一個查詢的(隨機)的結果,該查詢不能是無狀態的。您需要以某種方式在某處存儲狀態(關於以前的查詢/查詢的信息)。

最簡單的解決方案可能會被存儲已經檢索到的行或它們的ID在一個臨時表,然後在第二個查詢查詢... where id not in (select id from temp_table)

+0

數據非常大,就像我們有20000行和更多,那麼我們如何將數據存儲到臨時表中並一次又一次地運行,它會降低性能,對嗎? – Manikandan

+1

是的,當你向臨時表中添加記錄時,性能會下降,但是如果你想得到結果,那麼你唯一的選擇就是將已經選擇的行存儲在某個地方,也許一個帶索引的表可以爲你提供足夠的性能。 – Doliveras

+0

20k行不是太多的數據。例如。如果你的行有一個整數ID並且你只存儲了使用行的ID,那可能意味着你的臨時表可能適合100kB,這可以很容易地緩存在內存中,甚至可以在沒有索引的情況下快速訪問。 –

0

正如吉日Tousek說,每運行一個查詢必須知道什麼返回前面的查詢。

不是將先前返回的行的ID插入表中,而是檢查新的結果是不是在該表中,我只是簡單地向表中添加一列,並使用隨機數來定義一個新的隨機順序的行。

您需要用隨機數一次此列。

這會記得行的隨機順序,並使其穩定,因此,所有你需要你的查詢之間要記住的是多少隨意行到現在爲止您要求。然後,從您在先前查詢中停止的位置開始,根據需要獲取儘可能多的行。


將一列RandomNumber binary(8)添加到表中。您可以選擇不同的尺寸。 8個字節應該足夠了。

用隨機數填充它。一旦。

UPDATE tablename 
SET RandomNumber = CRYPT_GEN_RANDOM(8) 

RandomNumber列創建索引。獨特的索引。如果事實證明有重複的隨機數(這對於20,000行是不可能的,對於8個字節的隨機數是不可能的),然後重新生成隨機數(再次運行UPDATE語句),直到所有數字都是唯一的。

申請前10個隨機行:

SELECT TOP(10) * 
FROM tablename 
ORDER BY RandomNumber 

當你處理/使用這些10個隨機行記得上次使用的隨機數。最好的方法取決於你如何處理這10個隨機行。

DECLARE @VarLastRandomNumber binary(8); 
SET @VarLastRandomNumber = ... 
-- the random number from the last row returned by the previous query 

請求下一個10個隨機行:

SELECT TOP(10) * 
FROM tablename 
WHERE RandomNumber > @VarLastRandomNumber 
ORDER BY RandomNumber 

過程並記住上次使用的隨機數。

重複。作爲獎勵,您可以在每次迭代中請求不同數量的隨機行(每次不一定是10)。

+1

可行的方式,如果a)OP是否能夠修改表本身,b)該表在整個過程中穩定新的或刪除的行),c)一次只有一個會話執行此過程。 OP沒有提供任何具體細節,所以我們不知道。 –

0

我會做的是有兩個新的領域,SELECTED(int)和TimesSelected(整數),然後

UPDATE tablename SET SELECTED = 0; 

WITH CTE AS (SELECT TOP 10 * 
FROM tablename 
ORDER BY TimesSelected ASC, NEWID()) 
UPDATE CTE SET SELECTED = 1, TimesSelected = TimesSelected + 1; 

SELECT * from tablename WHERE SELECTED = 1; 

因此,如果您使用的每一次,一旦選擇了記錄轉到樁頂,並且它下面的記錄是隨機選擇的。

,你可能想要把所選擇的指數,並做

UPDATE tablename SET SELECTED = 0 WHERE SELECTED = 1; -- for performance 
+0

是的,我認爲這將工作,一旦記錄被選中,成爲1它不能重新選擇,直到所有其他記錄是1,當發生這種情況時,一些記錄將成爲2,然後不符合條件,直到其他人趕上 – Cato

0

最優雅的解決方案,只要你做了一定的時間內,連續查詢,將使用遊標:

DECLARE rnd_cursor CURSOR FOR 
    SELECT col1, col2, ... 
    FROM tablename 
    ORDER BY NEWID(); 

OPEN rnd_cursor; 
FETCH NEXT FROM rnd_cursor; -- Repeat ten times 

保持光標處於打開狀態,只需在需要時隨時獲取行。關閉遊標時,即可大功告成:

CLOSE rnd_cursor; 
DEALLOCATE rnd_cursor; 

至於你的問題的第二部分,一旦你獲取了最後一排,打開一個新的光標:

IF @@FETCH_STATUS <> 0 
BEGIN 
    CLOSE rnd_cursor; 
    OPEN rnd_cursor; 
END;