В Access найдите случайную запись (true random) - PullRequest
3 голосов
/ 07 июня 2011

что я пытаюсь сделать, это каждый раз, когда программа открывает изображение в форме. Итак, у меня есть простая таблица с двумя столбцами ID и ImagePath, как мне создать код, чтобы выбрать случайную запись (ImagePath), при событии загрузки формы или что-то подобное? Rnd не годится, так как он будет одним и тем же изображением при каждом открытии базы данных.

Спасибо!

Ответы [ 5 ]

3 голосов
/ 07 июня 2011

Попробуйте вызвать Randomize один раз - перед первым вызовом Rnd. Как и в разделе справки для Rnd, говорится: «Перед вызовом Rnd используйте оператор Randomize без аргумента, чтобы инициализировать генератор случайных чисел начальным числом, основанным на системном таймере».

2 голосов
/ 07 июня 2011

Я написал несколько собственных функций для возврата случайной записи, а затем рассчитал их время вместе с другими решениями, предлагаемыми здесь. Оба из них побеждали метод Харкинса, но ни один из них не мог коснуться решения @ 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 

Я запускал его четыре раза в обратном порядке после первых двух, чтобы учесть потенциальные преимущества кэширования. Как видите, две мои функции работали примерно в два раза быстрее, чем решение Харкинса, но решение @ ray023 было самым медленным, более чем в 25 раз быстрее (и самым быстрым почти в 100 раз быстрее).

Но во что бы то ни стало, сравните с вашими собственными данными.

2 голосов
/ 07 июня 2011

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 голос
/ 07 июня 2011

См. Эту статью Сьюзен Харкинс на TechRepublic: http://www.techrepublic.com/blog/howdoi/how-do-i-retrieve-a-random-set-of-records-in-microsoft-access/149

Я использовал ее функцию GetRandomValue в этом запросе, которая каждый раз возвращает новую запись.

Public Function GetRandomValue(fld As Variant)

  Randomize

  GetRandomValue = Rnd(1)

End Function

Внимание : При таком подходе требуется запуск функции для каждой строки таблицы.Это может быть допустимо для небольших и средних столов.Но вы не должны использовать его с очень большими таблицами.

0 голосов
/ 09 июня 2011

Я, может быть, слишком прост, чтобы понять проблему, но мне кажется, что если вы хотите получить одно случайное изображение, то все, что вам нужно сделать, это сгенерировать одно случайное число, которое каким-то образом вводится в таблицу доступных изображений. тебе. Если на выбор доступно 100 изображений, вам нужно случайное число от 1 до 100.

Итак, вы генерируете это число:

  Round(100 * Rnd(), 0)

... и затем вы используете это, чтобы получить изображение. Если в таблице изображений есть Autonumber PK, вы можете просто использовать это, и это будет ОЧЕНЬ БЫСТРО. Если ваше изображение находится в подчиненной форме, вы можете установить для LinkMaster буквальное значение PK, и это позволит получить изображение для вас.

Что касается функции Randomize (), я не могу заставить ее повторяться, когда я вызываю Rnd () в окне Immediate, поэтому я не уверен, нужна ли она.

Но все это кажется мне очень простой операцией, которая может не требовать 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).

Но, может быть, я упускаю некоторые важные детали, которые делают это намного сложнее, чем мне кажется.

...