在Access中查找随机记录(真随机)

时间:2011-06-07 14:17:25

标签: ms-access vba random module

我想要做的是每次程序打开时,表单上的图像都是不同的。所以我有一个包含2列ID和ImagePath的简单表,如何创建代码以便在表单加载事件或类似的东西上选择随机记录(ImagePath)? Rnd并不好,因为每次重新打开数据库时它都会是相同的图像。

谢谢!

5 个答案:

答案 0 :(得分:3)

在第一次调用Rnd之前尝试调用一次Randomize。因为,Rnd的帮助主题说:“在调用Rnd之前,使用不带参数的Randomize语句用基于系统计时器的种子初始化随机数生成器。”

答案 1 :(得分: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

答案 2 :(得分:2)

我写了几个我自己的函数来返回一个随机记录,然后将它们与这里提供的其他解决方案一起计时。我的两个人都击败了Harkins方法,但他们都无法触及@ ray023(稍微修改为基准测试)解决方案。 @ ray023的解决方案也可以说是最简单的。拿Susan Harkins来说吧!

这是代码。您可以将其复制并粘贴到标准模块中以测试您的数据。您只需要更改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倍)。

但无论如何,请根据自己的数据进行基准测试。

答案 3 :(得分:1)

Susan Harkins在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

警告:此方法需要针对表的每一行运行一个函数。对于中小型桌子,这可能是可以容忍的。但是你不应该在非常大的表中使用它。

答案 4 :(得分: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(),则将直接Round(100 * Rnd(), 0)替换为调用Randomize()的函数,然后返回Round(100 * Rnd(), 0)

但也许我错过了一些重要的细节,这些细节使得这比我认为的要复杂得多。