2011-06-07 81 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