Select n random records per group (1 Viewer)

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
This procedure, written years ago, returns n random records per group. Problem being, it does so by creating/recreating a queryDef returning unique EmployeeIDs and then a tableDef. It loops thru the queryDef and creates a append query to the tableDef for each EmployeeID.

While it processes quickly, think it's a clunky process. Have tried for hours to create a query that will do the same thing, e.g. create an aliased Select Distinct EmployeeID from Orders, linked to Orders by EmployeeID. This works so long as it's selecting the top n records for each employee. But, when I try to throw the random business in it goes nowhere.

Here's what works, albeit much slower than Public Sub RandomOrders. If you see a way to modify it to return random records for each EmployeeID, I'd really appreciate it.

Thanks - Bob

Code:
SELECT  DISTINCT
    t.EmployeeID
  , Orders.Freight
FROM
   Orders AS t 
LEFT JOIN
   Orders 
ON
   t.EmployeeID = Orders.EmployeeID
WHERE
   (((Orders.Freight) In (
SELECT
    Top 2 [Freight] 
FROM
    Orders 
WHERE
    [EmployeeID]= t.[EmployeeID] 
ORDER BY
    [Freight] DESC)));

Code:
Public Sub RandomOrders(pMyTable As String, pNum As Integer)
'*******************************************
'Name:      RandomOrders (Function)
'Purpose:   Returns pNum randomly selected
'           orders from Northwind's Orders
'           table for each distinct employee.
'Re:        http://www.utteraccess.com/forums/showflat.php?Cat=&Board=access_97&Number=242714&page=0&view=collapsed&sb=5&o=&fpart=1#Post243004
'Code by:   raskew
'Calls:     Function Randomizer, written by
'           Joe Foster and displayed in the Access Web at
'           http://www.mvps.org/access/queries/qry0011.htm
'Inputs:    from debug window: call RandomOrders("zTestRan", 3)
'Output:    table zTestRan
'*******************************************

Dim db As Database
Dim rs As Recordset
Dim tName As String, test As String, strSql As String
Dim n As Integer, i As Integer, empHold As Integer, j As Integer
 
Set db = CurrentDb
tName = pMyTable
On Error Resume Next

'Step (1) Does table tName exist?  If so, delete it.
test = db.TableDefs(tName).name
If Err <> 3265 Then
   docmd.SetWarnings False 'Hide warning messages
   docmd.DeleteObject acTable, tName
   docmd.SetWarnings True  'Reinstate warning messages
End If
Err = 0
'*******************

' Step (2) ' Create new table.
strSql = "CREATE TABLE " & tName & " (EmployeeID Long," _
& " OrderID Long, Freight Currency" & ");"
db.Execute strSql
'*******************

' Step (3) ' Create recordset of employees
strSql = "SELECT distinct EmployeeID FROM Orders;"
Set rs = db.OpenRecordset(strSql)
' count the employees
rs.MoveLast
n = rs.RecordCount
rs.MoveFirst
'*******************

' Step (4) Create random listing of orders for each employee
For i = 1 To n  'loop thru the listing of employees
    empHold = rs!EmployeeID
' create append query
    strSql = "INSERT INTO " & tName & " ( OrderID, EmployeeID, Freight )" _
        & " SELECT TOP " & pNum & " Orders.OrderID, Orders.EmployeeID, Orders.Freight" _
        & " FROM Orders" _
        & " WHERE (((Orders.EmployeeID)= " & empHold & " ) AND ((randomizer())=0))" _
        & " ORDER BY Rnd(IsNull([Orders].[OrderID])*0+1);"
' execute the query
    db.Execute strSql
    rs.MoveNext
Next i
j
'*******************

docmd.OpenTable tName, acViewNormal

' Cleanup
rs.Close
db.Close
Set rs = Nothing
Set db = Nothing

End Sub

Code:
Function Randomizer() As Integer
'Source: http://www.mvps.org/access/queries/qry0011.htm
'Author: Joe Foster

Static AlreadyDone As Integer
   If AlreadyDone = False Then Randomize: AlreadyDone = True
   Randomizer = 0
End Function
 

ajetrumpet

Banned
Local time
Today, 14:19
Joined
Jun 22, 2007
Messages
5,638
Bob,

when I was trying to randomize, and perfect it, Wayne simplified it by using this:
Code:
SELECT table.field1, table.field2 FROM table
   GROUP BY table.field1
      ORDER BY rnd(table.field2)
This wasn't what I needed, but I never had used the Rnd() function before I saw it.

Field2 had to be an integer or a number data type, otherwise the randomizing with the Rnd() function wouldn't work.


Would that concept work for you too? It is just an initial thought...
Code:
SELECT  DISTINCT
    t.EmployeeID
  , Orders.Freight
FROM
   Orders AS t 
LEFT JOIN
   Orders 
ON
   t.EmployeeID = Orders.EmployeeID
WHERE
   (((Orders.Freight) In (
SELECT
    Top 2 [Freight] 
FROM
    Orders 
WHERE
    [EmployeeID]= t.[EmployeeID] 
ORDER BY
    [color=red]rnd([Freight])[/color] DESC))));
 

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
Adam -

Thanks for the quick response. Unfortunately, that SQL is doing the same thing I've been encountering -- instead of returning 18 records (2 for each employee), it returns 720 records.

I may end up having to stick with the RandomRecords() solution. Don't know if you tried it but it processes very quickly (and accurately). Just think the process sucks.

Basically, what I've been trying to do is modify this, from RandomRecords:
Code:
   strSql = "INSERT INTO " & tName & " ( OrderID, EmployeeID, Freight )" _
        & " SELECT TOP " & pNum & " Orders.OrderID, Orders.EmployeeID, Orders.Freight" _
        & " FROM Orders" _
        & " WHERE (((Orders.EmployeeID)= " & empHold & " ) AND ((randomizer())=0))" _
        & " ORDER BY Rnd(IsNull([Orders].[OrderID])*0+1);"

...into a select query, eliminating the separate query for distinct EmployeeID's and adding a second, distinct copy of Orders to provide the EmployeeID's. Just can't seem to get it right and I end up returning numerous records, instead of just 2 per employee. Plus, it takes forever to process.

Just seems like it is a doable concept.

Anyway, thanks again and any future thoughts will be much appreciated.

Bob
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
Hi Raskew Download the attached sample

This is the most efficient solution to your problem (execution time is not more than 1 sec as opposed to your query)

Specify on the form what should be the minimum top records selected and what should be the maximum for example if you enter 5 to 10 it will select top records randomly from each group between 5 and 10

Selected records will be inserted into a temporary table which would be between the range selected by you on form

Regards
 

Attachments

  • RandomTopRecordsPerGroup.zip
    67.7 KB · Views: 531
Last edited:

ajetrumpet

Banned
Local time
Today, 14:19
Joined
Jun 22, 2007
Messages
5,638
Just seems like it is a doable concept.

Anyway, thanks again and any future thoughts will be much appreciated.

Bob
I'm sure it is, and seeing how I am the one around here that can put multiple hours into a process just for figuring it out, I can almost guarantee you that I can make this more efficient. But that would probably take a long time to figure out... :D
 

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
Adam -

That works great and it is fast! Problem being it's returning the top N per employee, with no random sort.

I changed your AppendTemp sub to this and added the Randomizer() function to a standard module, and it's performing just as fast, and doing the random thing as advertised--returning different records each time.

Code:
Private Sub AppendTemp(Emp As Long, TopVal As Long)

Dim StrSql As String

'StrSql1 = "INSERT INTO TempRandomTopPerGroup ( EmployeeID, Freight ) SELECT TOP " & TopVal
'StrSql1 = StrSql1 & " Orders.EmployeeID , Orders.Freight FROM Orders "
'StrSql1 = StrSql1 & "WHERE Orders.EmployeeID = " & Emp
'StrSql1 = StrSql1 & " ORDER BY Orders.Freight DESC;"

    StrSql = "INSERT INTO TempRandomTopPerGroup ( EmployeeID, Freight )" _
        & " SELECT TOP " & TopVal & " EmployeeID, Freight" _
        & " FROM Orders" _
        & " WHERE Orders.EmployeeID = " & Emp & " AND randomizer()=0 " _
        & " ORDER BY Rnd(IsNull([Orders].[OrderID])*0+1);"

CurrentDb.Execute StrSql

End Sub

Thanks so much - Bob
 
Last edited:

ajetrumpet

Banned
Local time
Today, 14:19
Joined
Jun 22, 2007
Messages
5,638
i guess i'm just wondering now Bob, do you want me to do anything here? :) You have it working already...
 

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
Hi -

My original thought was to create a query like this:

Code:
SELECT  DISTINCT
    t.EmployeeID
  , Orders.Freight
FROM
   Orders AS t 
LEFT JOIN
   Orders 
ON
   t.EmployeeID = Orders.EmployeeID
WHERE
   (((Orders.Freight) In (
SELECT
    Top 2 [Freight] 
FROM
    Orders 
WHERE
    [EmployeeID]= t.[EmployeeID] 
ORDER BY
    [Freight] DESC)));

...which works, into the same basic query but with the random business--thus eliminating the VBA looping process.

However, seeing how fast your code processes (and RandomOrders() too, for that matter) and how slow the above code processes, believe I need to rethink the whole thing.

So, unless you've got a trick to speedup the above code and convert it so that it's returning random records, think we've gone as far as we're gonna go.

Thanks again - Bob
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
Bob what about this

Code:
SELECT [LastName] & ", " & [FirstName] AS Name, 
Orders.Freight
FROM Employees 
LEFT JOIN Orders ON Employees.EmployeeID = Orders.EmployeeID
WHERE (((Orders.Freight) 
In (SELECT Top 2 [Freight] FROM Orders 
WHERE [EmployeeID]= Employees.[EmployeeID] 
ORDER BY 
rnd*([OrderId]) desc)));

I didnt use the table alias as you used (t) in your sql instead I picked the employees directly from employees table this makes the query much efficient

Tell me if this works for you
 
Last edited:

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
Khawar,

Wow! That's getting closer. It's fast and it returns a random result. Only thing, it returns the same result each time. I'm going to have to play with this one.

Many thanks -- Bob
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
To get different records each time vba is to be used I think so
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
Copy Below Function in a new module

Code:
Option Compare Database
Dim RandNum As Long

Function GetRandom()

If RandNum > 100 Then

        RandNum = 0

    Else

        RandNum = RandNum + (Int((100 - 1 + 1) * Rnd + 1))

    End If

    GetRandom = RandNum

End Function

and then try this sql

Code:
SELECT [LastName] & ", " & [FirstName] AS Name, 
Orders.Freight
FROM Employees 
LEFT JOIN Orders ON Employees.EmployeeID = Orders.EmployeeID
WHERE (((Orders.Freight) In (SELECT Top 2 [Freight] FROM Orders 
WHERE [EmployeeID]= Employees.[EmployeeID] 
ORDER BY 
Getrandom()*([OrderId]) desc)));
 

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
Hi -

Sorry for the delay getting back.

This is very strange. Running the query, most times, returns the same 18 records each time. And then, trying it again, it returns all 828 Orders records. Haven't got that figured out.

Bob
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
Bob
For me the above works fine
Will try to find out some alternatives today in the evening when I will be free
If you get some other solution do share with me also

Regards
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
Finally got it to work download the attached file now it returns two different records each time I think this is what you want
 

Attachments

  • 2 Random Per Group.zip
    67.9 KB · Views: 497

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
This has got me absolutely baffled. Obviously you've got it working at your end, but when I download and run it, I'm getting the same 18 records each time.

Have loaded it in both A2003 and A97, thinking there might be some sort of corruption in one or the other, but continue to get the same responses, regardless of version.

Best wishes - Bob
 

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
I am also getting confused what do you want 2 per group different each time
or
different records per group and also different records each time

My first database was about different number of records each time and this one shows two per group but 18 different records each time
 

raskew

AWF VIP
Local time
Today, 14:19
Joined
Jun 2, 2001
Messages
2,734
I'm looking to return two randomly selected records for Steven Buchanan, two for Laura Callahan, etc., total of 18 different records each time.

Bob

Example, as it now functions, it returns these same two records for Steven Buchanan

Name Freight
Buchanan, Steven $22.98
Buchanan, Steven $890.78

There no variance in run to run.
 
Last edited:

khawar

AWF VIP
Local time
Today, 23:19
Joined
Oct 28, 2006
Messages
870
Thats what my last sample is doing I am attaching some screen shots

you can see I am getting 18 records and different results each time
 

Attachments

  • Main Screen.JPG
    Main Screen.JPG
    59.6 KB · Views: 214
  • Result1.JPG
    Result1.JPG
    90.9 KB · Views: 184
  • Result2.JPG
    Result2.JPG
    94.8 KB · Views: 179
  • Result3.JPG
    Result3.JPG
    93.8 KB · Views: 158
  • Result4.JPG
    Result4.JPG
    94.2 KB · Views: 187

Users who are viewing this thread

Top Bottom