Option Compare Database
Option Explicit
Dim ws As DAO.Workspace
Dim rsWrite As DAO.Recordset
Dim stringArray() As String
Dim randomStringCount As Long
Dim sectionTimes(1 To 4) As Date
Private Sub Form_Load()
CreateStringArray
SetupSortedTable
ClearForm
End Sub
Private Sub btnCreateStrings_Click()
If rsWrite Is Nothing Then
SetupSortedTable ' if rerunning without closing
End If
ClearForm
GenerateRandomStrings
End Sub
Private Sub btnSortStrings_Click()
ClearForm
CreateStringArray
If rsWrite Is Nothing Then SetupSortedTable ' if rerunning without closing
Dim startRun As Date
Dim endRun As Date
startRun = Now
txtStartTime.SetFocus: txtStartTime.Text = Format(CDate(startRun), "mm/dd/yy hh:MM:ss")
sectionTimes(1) = Now
lblBusy.Caption = "Geting Strings": DoEvents
GetStrings
sectionTimes(2) = Now
lblBusy.Caption = "Sorting Strings": DoEvents
Quicksort stringArray, LBound(stringArray), UBound(stringArray)
sectionTimes(3) = Now
lblBusy.Caption = "Removing Duplicates": DoEvents
RemoveDuplicates
sectionTimes(4) = Now
lblBusy.Caption = "Writing Sorted": DoEvents
WriteTheStrings
endRun = Now
lblBusy.Caption = "Done"
txtEndTime.SetFocus: txtEndTime.Text = Format(CDate(endRun), "mm/dd/yy hh:MM:ss")
txtDuration.SetFocus: txtDuration.Text = DateDiff("s", startRun, endRun)
txtCount.SetFocus: Me.txtCount.Text = UBound(stringArray) - 1
Debug.Print "Get Strings start " & Format(sectionTimes(1), "h:mm:ss")
Debug.Print "Sort Strings start " & Format(sectionTimes(2), "h:mm:ss")
Debug.Print "Remove Duplicates start " & Format(sectionTimes(3), "h:mm:ss")
Debug.Print "Write Strings start " & Format(sectionTimes(4), "h:mm:ss")
End Sub
Private Sub CreateStringArray()
' create string array for sorting
On Error Resume Next
' if there is already a tblRandom table get the count of records in it
Dim rsCount As DAO.Recordset
Set rsCount = CurrentDb.OpenRecordset("select count(TextString) from tblRandom", dbOpenDynaset)
randomStringCount = rsCount(0)
lblCreatedStringsCount.Caption = "There are " & randomStringCount & " random strings"
rsCount.Close
Set rsCount = Nothing
ReDim stringArray((randomStringCount - 1))
On Error GoTo 0
End Sub
Private Sub ClearForm()
txtStartTime.SetFocus: txtStartTime.Text = ""
txtEndTime.SetFocus: txtEndTime.Text = ""
txtDuration.SetFocus: txtDuration.Text = ""
txtCount.SetFocus: Me.txtCount.Text = ""
End Sub
Private Sub SetupSortedTable()
' set up the table
Set ws = DBEngine.Workspaces(0)
On Error Resume Next
' create the tblPrimes table
CurrentDb.Execute "drop table tblSorted"
CurrentDb.Execute "Create Table tblSorted (ID AUTOINCREMENT PRIMARY KEY, SortedString TEXT(150));"
On Error GoTo 0
' create the recordset
Set rsWrite = CurrentDb.OpenRecordset("tblSorted", dbOpenTable) ' open the table to be written to
DoEvents
End Sub
Private Sub GetStrings()
' get the strings into an array
Dim rs As DAO.Recordset
Dim arrayIndex As Long
Set rs = CurrentDb.OpenRecordset("Select * from tblRandom", dbOpenForwardOnly)
arrayIndex = 0
Do While Not rs.EOF
stringArray(arrayIndex) = rs(1)
rs.MoveNext
arrayIndex = arrayIndex + 1
Loop
rs.Close
Set rs = Nothing
End Sub
Private Sub RemoveDuplicates()
Dim arrayIndex As Long
Dim removedCount As Long
removedCount = 0
For arrayIndex = UBound(stringArray) - 1 To 1 Step -1
If stringArray(arrayIndex) = stringArray(arrayIndex - 1) Then
' this is a duplicate of the one above it, make this blank
stringArray(arrayIndex) = ""
removedCount = removedCount + 1
End If
Next arrayIndex
If removedCount > 0 Then
' duplicates were found and blanked out - remove the blanks
Dim tempArray() As String
Dim tempArrayIndex As Long
' use a temp array to hold the new array with no duplicates
ReDim tempArray((UBound(stringArray) - removedCount))
For arrayIndex = 0 To UBound(stringArray) - 1
If Len(stringArray(arrayIndex)) > 0 Then
tempArray(tempArrayIndex) = stringArray(arrayIndex)
tempArrayIndex = tempArrayIndex + 1
End If
Next arrayIndex
' assign it back to the main array variable while condencing the array
ReDim stringArray((UBound(stringArray) - removedCount))
stringArray = tempArray
End If
End Sub
Sub Quicksort(vArray As Variant, arrLbound As Long, arrUbound As Long)
' Sorts a one-dimensional VBA array from smallest to largest
' using a very fast quicksort algorithm variant.
' from https://wellsr.com/vba/2018/excel/vba-quicksort-macro-to-sort-arrays-fast/
Dim pivotVal As Variant
Dim vSwap As Variant
Dim tmpLow As Long
Dim tmpHi As Long
tmpLow = arrLbound
tmpHi = arrUbound
pivotVal = vArray((arrLbound + arrUbound) \ 2)
While (tmpLow <= tmpHi)
While (vArray(tmpLow) < pivotVal And tmpLow < arrUbound)
tmpLow = tmpLow + 1
Wend
While (pivotVal < vArray(tmpHi) And tmpHi > arrLbound)
tmpHi = tmpHi - 1
Wend
If (tmpLow <= tmpHi) Then
vSwap = vArray(tmpLow)
vArray(tmpLow) = vArray(tmpHi)
vArray(tmpHi) = vSwap
tmpLow = tmpLow + 1
tmpHi = tmpHi - 1
End If
Wend
If (arrLbound < tmpHi) Then Quicksort vArray, arrLbound, tmpHi
If (tmpLow < arrUbound) Then Quicksort vArray, tmpLow, arrUbound
End Sub
Private Sub WriteTheStrings()
Dim arrayIndex As Long
ws.BeginTrans
' loop the sorted array and write it to a table
For arrayIndex = 0 To UBound(stringArray) - 1
rsWrite.AddNew
rsWrite("SortedString") = stringArray(arrayIndex)
rsWrite.Update
Next arrayIndex
ws.CommitTrans
rsWrite.Close
Set rsWrite = Nothing
ws.Close
Set ws = Nothing
End Sub
Public Sub GenerateRandomStrings()
' provided string generation code
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim rst As DAO.Recordset
Dim varListSize As Variant
Dim nListSize As Long
Dim nRecord As Long
Dim nChar As Integer
Dim sChar As String
Dim sText As String
Dim nRand As Integer
Set db = CurrentDb
' Delete tblRandom if it exists
On Error Resume Next
db.TableDefs.Delete "tblRandom"
On Error GoTo 0
DoEvents
' Re-create tblRandom and add the required fields
Set tdf = db.CreateTableDef("tblRandom")
DoEvents
With tdf
.Fields.Append .CreateField("ID", dbLong)
.Fields("ID").Attributes = dbAutoIncrField
.Fields.Append .CreateField("TextString", dbText, 255)
End With
db.TableDefs.Append tdf
Set rst = db.OpenRecordset("tblRandom", dbOpenDynaset)
' get the desired list size
varListSize = InputBox("Number of Elements", "Enter Number", 1000000) ' default 1,000,000 but can be changed
If varListSize = "" Or varListSize <= 0 Or Not IsNumeric(varListSize) Then
MsgBox "Exiting", vbOKOnly + vbInformation, "Invalid Selection"
Exit Sub
End If
nListSize = Fix(varListSize)
' generate the random list
Call Rnd(-1) ' required along with Randomize
Randomize 12345 ' set seed based on nRecord to ensure that the list of generated strings is always the same
Me.lblCreatedStringsCount.Caption = "Creating " & nListSize & " strings": DoEvents
ws.BeginTrans
' Outer loop
For nRecord = 1 To nListSize
sText = ""
' generate 8 random values between 0 and 26
For nChar = 1 To 8
nRand = Int(27 * Rnd()) ' Random number between 0 and 26
If nChar = 1 And nRand = 0 Then
nRand = (nRecord Mod 26) + 1 ' don't allow the first character to be a space
End If
' Convert to character
If nRand = 0 Then
sChar = " " ' if nRand = 0 then a space is generated
Exit For ' stop once a space is encountered
Else
sChar = Chr(96 + nRand) ' Chr(97) = "a", Chr(98) = "b", etc.
End If
' Build the string
sText = sText & sChar
Next nChar
' Write to the table
rst.AddNew
rst!TextString = sText
rst.Update
Next nRecord
ws.CommitTrans
rst.Close
Set rst = Nothing
Set tdf = Nothing
Set db = Nothing
Me.lblCreatedStringsCount.Caption = "Table of " & Format(nListSize, "#,##0") & " random strings created"
randomStringCount = nListSize ' update the array size required
End Sub