2011-06-07 97 views
3

我想要做的是每次程序打開窗體上的圖像是不同的。所以我有一個帶有2列ID和ImagePath的簡單表,我如何創建代碼以便在表單加載事件或類似的事件上選擇一個隨機記錄(ImagePath)? Rnd是不好的,因爲每次數據庫重新打開時它都是相同的圖像。在訪問找到一個隨機記錄(真隨機)

謝謝!

+1

如果rnd在每次打開數據庫時都返回相同的值,那麼您可能錯誤地使用了它。你怎麼使用它? – Heinzi 2011-06-07 14:21:49

+0

只是一個簡單的查詢.... SELECT TOP 1 Images.ID,Images.Path,Rnd([ID])AS Ran FROM Images ORDER BY Rnd([ID]); – Ben 2011-06-07 14:28:21

回答

1

查看TechRepublic的這篇文章由蘇珊·哈金斯:http://www.techrepublic.com/blog/howdoi/how-do-i-retrieve-a-random-set-of-records-in-microsoft-access/149

我在此查詢,它每次返回不同的記錄中使用她的GetRandomValue功能。

SELECT TOP 1 f.id, GetRandomValue(f.id) AS rnd_value 
FROM tblFoo AS f 
ORDER BY 2; 

功能:

Public Function GetRandomValue(fld As Variant) 

    Randomize 

    GetRandomValue = Rnd(1) 

End Function 

注意:這種方法需要運行對錶的每一行的功能。對於中小型餐桌,這可能是可以忍受的。但是你不應該在非常大的表格中使用它。

+0

對於相對較小的表格,這將工作正常。如果性能問題或表格較大(超過10,000條記錄),請查看@ ray023的解決方案。 – mwolfe02 2011-06-07 16:50:08

+0

其實,如果仔細觀察基準測試,我只需要調用每個函數10次。字符串連接可能佔用4,000或更多ms,但其餘的更多與在表的每一行上調用VBA函數有關。這就是事情崩潰的地方。 – mwolfe02 2011-06-07 17:09:45

+0

我確實傾向於認同這不是他的使用案例的問題。我假設他有一張桌子上有幾十個圖像文件名,性能應該是完全可以接受的。我原來的評論真的是針對未來的讀者,他們可能需要檢索一個隨機記錄,但有一個完全不同的用例。 – mwolfe02 2011-06-07 17:11:34

3

嘗試調用Randomize一次 - 在第一次調用Rnd之前。正如Rnd的幫助主題所說:「在調用Rnd之前,使用沒有參數的Randomize語句來使用基於系統定時器的種子初始化隨機數生成器。」

+0

男人...不管是什麼原因,我這個掙扎,所以我創建了一個模塊: 公共功能acbGetRandom(varFld爲Variant) 隨機化 acbGetRandom =賽第一輪 端功能 然後,我添加功能到我的查詢,使用:RandomID:acbGetRandom([ID])in the field .. 我希望這可以幫助別人! – Ben 2011-06-07 15:24:27

+0

爲了隨機化Rnd使用的「seed」,只需要調用Randomize一次。例如,您可以通過AutoExec宏或啓動窗體調用它。另一種方法是在產生一系列僞隨機數的開始稱它,就像在ray023的答案中一樣。至於你爲你的函數添加(但沒有使用)的「varFld」參數,當你在查詢中使用該函數時,這是強制函數對每條記錄進行一次評估(至少)一次的一種方法。然而,這在ray023的方法中不需要,因爲該函數在查詢之外被調用。 – 2011-06-08 11:37:58

2

Rnd不好?

Option Compare Database 
Option Explicit 

Sub Test() 
    Randomize 
    Dim x As Integer 
    'Print the first field of a 100 random records 
    For x = 0 To 100 
     CallRandomRecord 
    Next x 
End Sub 

Sub CallRandomRecord() 
    Dim rs As DAO.Recordset 
    Dim recordCount As Long 
    Dim randomRecord As Long 

    Set rs = CurrentDb.OpenRecordset("SELECT * FROM MyTable") 
    rs.MoveLast 'To get the count 
    rs.MoveFirst 
    recordCount = rs.recordCount - 1 
    randomRecord = CLng((recordCount) * Rnd) 

    rs.Move randomRecord 

    Debug.Print "Random Record No:" & randomRecord & " Field 1: " & rs.Fields(0) 

End Sub 
+1

這是發佈的最佳答案。查看我的答案以獲得基準測試結果。 – mwolfe02 2011-06-07 16:41:31

2

我寫了一些我自己的函數來返回一個隨機記錄,然後將它們與此處提供的其他解決方案一起計時。我的兩個人都擊敗了哈金斯方法,但他們都不能觸及@ ray023(稍做修改用於基準測試)解決方案。 @ ray023的解決方案也可以說是最簡單的。拿那個蘇珊哈金斯!

這是代碼。您可以複製它並粘貼到標準模塊中以測試您的數據。你只需要在TimeThem模塊頂部改變三個常量:

Private Declare Function GetTickCount Lib "kernel32"() As Long 

Sub TimeThem() 
Const Loops As Integer = 10 
Const TblName As String = "Batches" 
Const FldName As String = "BatchName" 
Const IndexFld As String = "BatchID" 
Dim i As Integer, s As Long, dummy As Variant 

    s = GetTickCount 
    For i = 1 To Loops 
     dummy = HarkinsRandom(TblName, FldName) 
    Next i 
    Debug.Print "Harkins:"; GetTickCount - s 

    s = GetTickCount 
    For i = 1 To Loops 
     dummy = RandomRecord(TblName, FldName) 
    Next i 
    Debug.Print "RandomRecord:"; GetTickCount - s 

    s = GetTickCount 
    For i = 1 To Loops 
     dummy = RandomRecordWithIndex(TblName, FldName, IndexFld) 
    Next i 
    Debug.Print "WithIndex:"; GetTickCount - s 

    s = GetTickCount 
    For i = 1 To Loops 
     dummy = CallRandomRecord(TblName, FldName) 
    Next i 
    Debug.Print "CallRandom:"; GetTickCount - s 


End Sub 

Function HarkinsRandom(TblName As String, FldName As String) 
    Dim rs As DAO.Recordset 
    Set rs = CurrentDb.OpenRecordset(" SELECT TOP 1 " & FldName & _ 
            " FROM " & TblName & _ 
            " ORDER BY GetRandomValue(" & FldName & ")", _ 
            dbOpenForwardOnly) 
    HarkinsRandom = rs(0) 
End Function 

Public Function GetRandomValue(fld As Variant) 
    Randomize 
    GetRandomValue = Rnd(1) 
End Function 

Function RandomRecord(TblName As String, FldName As String) 
Dim NumRecs As Long, RecNum As Long 
Dim SQL As String, SubSQL As String, rs As DAO.Recordset 
Dim IndexFld As String 

    Randomize 
    NumRecs = CurrentDb.OpenRecordset("SELECT Count(*) FROM " & TblName, dbOpenForwardOnly)(0) 
    RecNum = Int(Rnd() * NumRecs + 1) 
    SQL = " SELECT TOP 1 " & FldName & _ 
      " FROM (" & _ 
      " SELECT TOP " & RecNum & " " & FldName & " " & _ 
      " FROM " & TblName & _ 
      " ORDER BY " & FldName & ")" & _ 
      " ORDER BY " & FldName & " DESC" 
    Set rs = CurrentDb.OpenRecordset(SQL, dbOpenForwardOnly) 
    RandomRecord = rs(0) 
End Function 

Function RandomRecordWithIndex(TblName As String, FldName As String, _ 
           Optional IndexedFieldName As String) 
Dim NumRecs As Long, RecNum As Long 
Dim SQL As String, SubSQL As String, rs As DAO.Recordset 
Dim IndexFld As String 

    Randomize 
    NumRecs = CurrentDb.OpenRecordset("SELECT Count(*) FROM " & TblName, dbOpenForwardOnly)(0) 
    RecNum = Int(Rnd() * NumRecs + 1) 
    If Len(IndexedFieldName) = 0 Or IndexedFieldName = FldName Then 
     SQL = " SELECT TOP 1 " & FldName & _ 
       " FROM (" & _ 
       " SELECT TOP " & RecNum & " " & FldName & " " & _ 
       " FROM " & TblName & _ 
       " ORDER BY " & FldName & ")" & _ 
       " ORDER BY " & FldName & " DESC" 
    Else 
     SQL = " SELECT TOP 1 " & FldName & _ 
       " FROM (" & _ 
       " SELECT TOP " & RecNum & " " & FldName & ", " & IndexedFieldName & _ 
       " FROM " & TblName & _ 
       " ORDER BY " & IndexedFieldName & ")" & _ 
       " ORDER BY " & IndexedFieldName & " DESC" 

    End If 
    Set rs = CurrentDb.OpenRecordset(SQL, dbOpenForwardOnly) 
    RandomRecordWithIndex = rs(0) 
End Function 

Function CallRandomRecord(TblName As String, FldName As String) 
    Dim rs As DAO.Recordset 
    Dim recordCount As Long 
    Dim RandomRecord As Long 

    Set rs = CurrentDb.OpenRecordset("SELECT " & FldName & " FROM " & TblName) 
    rs.MoveLast 'To get the count 
    rs.MoveFirst 
    recordCount = rs.recordCount - 1 
    RandomRecord = CLng((recordCount) * Rnd) 

    rs.Move RandomRecord 

    CallRandomRecord = rs(0) 
'  Debug.Print "Random Record No:" & randomRecord & " Field 1: " & rs.Fields(0) 

End Function 

這裏是運行對錶約50,000條記錄(這是一個本地連接的Jet表中的測試結果,即,它是在同一臺計算機在那裏,我跑測試上一個.mdb):

Harkins: 4461 
RandomRecord: 2528 
WithIndex: 1918 
CallRandom: 172 

Harkins: 4150 
RandomRecord: 2278 
WithIndex: 2043 
CallRandom: 47 

CallRandom: 63 
WithIndex: 2090 
RandomRecord: 2324 
Harkins: 4197 

CallRandom: 46 
WithIndex: 1997 
RandomRecord: 2169 
Harkins: 4150 

我跑了四次前兩後反向排序來考慮潛在的高速緩存的優勢。正如你所看到的,我的兩個函數的運行速度約爲Harkins解決方案的兩倍,但@ ray023的解決方案速度最慢,速度提高了25倍以上(最快速度提高了近100倍)。

但通過一切手段,基準與您自己的數據。

0

我可能太簡單瞭解這個問題,但在我看來,如果你想檢索一個隨機圖像,那麼你所需要做的就是生成一個隨機數字,它以某種方式鍵入圖像表提供給你。如果有100個圖像以供選擇,你想從1到100

一個隨機數所以,你生成一個數字:

Round(100 * Rnd(), 0) 

...然後你用它來獲取圖像。如果圖像表具有自動編號PK,那麼您可以使用它,而且它會非常快。如果您的圖像位於子窗體中,則可以將LinkMaster設置爲文字PK值,然後爲您檢索圖像。

關於Randomize()的主題,當我在立即窗口中調用Rnd()時,似乎無法重複它,所以我不確定是否需要它。

但是這對我來說似乎是一個非常簡單的操作,可能不需要任何SQL或使用記錄集。如果您使用記錄集路線,我建議您打開一次並保存它,然後在每次需要時導航它,而不是在每次需要新圖像時重複打開它。但是如果我這樣做了,我會盡可能爲自己做一些簡單的事情,然後使用Autonumber PK路線獲取圖像。如果你想在SQL中執行它,那將是:

SELECT Images.ID, Images.Path 
    FROM Images 
    WHERE Images.ID = Round(100 * Rnd(), 0) 

Obvoiusly,你會將100更改爲適當的數字。如果您需要Randomize(),則用一個調用Randomize()的函數替換直接Round(100 * Rnd(), 0),然後返回Round(100 * Rnd(), 0)

但也許我錯過了一些重要的細節,使得這似乎比我想象的要複雜得多。

+0

由於記錄刪除和插入失敗,自動編號PK序列中的空位是什麼?如果'Round(100 * Rnd(),0)'計算爲57,並且沒有ID = 57的行... – HansUp 2011-06-09 03:08:58

+0

您會處理它。也就是說,如果沒有記錄被返回,你會得到下一個隨機數。如果你爲績效最大化,但在我看來,你想保證在PK領域沒有差距,但。也就是說,如果您要以這種方式使用數據,您需要確保數據符合某些要求儘可能高效的要求。雖然這違反了無意義PK的通常原則,但在這種情況下,我沒有任何問題,因爲你給它一種意義形式。 – 2011-06-12 20:36:30

+0

你在評論中寫的是全部真實的。我的評論是迴應你答案中的最後一句。這種方法並不像答案所建議的那麼簡單。 – HansUp 2011-06-12 21:12:29