Is there a faster alternative to DLookup? - ms-access

I'm using DLookup to search for a field in a table. It runs correctly, but is slow. Is there anything I can do to speed it up?
Here's my existing code:
Private Sub cmdLogin_Click()
strUserLevel = ""
If IsNull(Me.cmbUserName) Or Me.cmbUserName = "" Then
MsgBox "You must enter a User Name.", vbOKOnly, "Required Data"
Me.cmbUserName.SetFocus
Exit Sub
End If
If IsNull(Me.txtPassword) Or Me.txtPassword = "" Then
MsgBox "You must enter a Password.", vbOKOnly, "Required Data"
Me.txtPassword.SetFocus
Exit Sub
End If
'strUserName = cmbUserName.Value
If Me.txtPassword.Value = DLookup("Password", "tableUser", "[lngEmpID]=" & Me.cmbUserName.Value) Then
lngMyEmpID = Me.cmbUserName.Value
strUserLevel = DLookup("Department", "tableUser", "[lngEmpID]=" & Me.cmbUserName.Value)
strUserName = DLookup("User_Name", "tableUser", "[lngEmpID]=" & Me.cmbUserName.Value)
boolInventoryMDL = DLookup("Inventory", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolDispositionMDL = DLookup("Disposition", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolReviewCloseMDL = DLookup("Review", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolAdministratorMDL = DLookup("Administrator", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolUserListMDL = DLookup("UserList", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolUserLevelMDL = DLookup("UserLevel", "tableDepartment", "[Department]=""" & strUserLevel & """")
If strUserLevel = "Superuser" Then
MsgBox "Welcome back Superuser! You can access all the modules here..", vbOKOnly, "Caution"
Else
MsgBox "Welcome! Login Success!", vbOKOnly, "Login Page"
End If
DoCmd.Close acForm, "frmLogin", acSaveNo
DoCmd.OpenForm "frmModule"
Else
MsgBox "Password Invalid. Please Try Again", vbOKOnly, "Invalid Entry!"
Me.txtPassword.Value = ""
Me.txtPassword.SetFocus
End If
End Sub

I don't believe the problem is due to inherent slowness of DLookup. Rather the problem is that the code uses so many of them.
Open one recordset based on a query of tableUser and take the values you need from that recordset. Then open a second recordset from a query of tableDepartment and get your remaining values.
Dim db As DAO.database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim strSelect As String
strSelect = "SELECT u.Password, u.Department, u.User_Name" & vbCrLf & _
"FROM tableUser AS u WHERE u.lngEmpID = [which_EmpId];"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, strSelect)
qdf.Parameters("which_EmpId") = Me.cmbUserName
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
If Not rs.EOF Then
If rs![Password] = Me.txtPassword Then
strUserLevel = rs!Department
strUserName = rs!User_Name
rs.Close
' open another recordset from a query of tableDepartment
' to retrieve your bool????? values
End If
End If
In that abbreviated sample, I used a temporary QueryDef for the parameterized SELECT query. However you would be better of to save that SQL as a named query, perhaps qryFetchUserData. Then at run time, instead of recreating the query each time, you could simply open the saved query.
Set qdf = db.QueryDefs("qryFetchUserData")
For optimum performance, you should add indexes on tableUser.lngEmpID and tableDepartment.Department if they're not already indexed.

Related

How to check if the table is empty in Access 2003?

I need only empty tables in access database. Additionally, it would be great if I can get empty tables from list of tables that I have (part of all tables). But listing all empty tables would work also.
You can use a small VBA function that checks this. Something like:
Function fIsTableEmpty(strTableName As String) As Boolean
On Error GoTo E_Handle
Dim db As DAO.Database
Dim rsData As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb
strSQL = "SELECT COUNT(*) FROM [" & strTableName & "];"
Set rsData = db.OpenRecordset(strSQL)
fIsTableEmpty = True ' start by assuming that there are records
If Not (rsData.BOF And rsData.EOF) Then
If rsData(0) > 0 Then fIsTableEmpty = False
End If
fExit:
On Error Resume Next
rsData.Close
Set rsData = Nothing
Set db = Nothing
Exit Function
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "fIsTableEmpty", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume fExit
End Function
You can use DCount:
Public Function ListEmptyTables()
Dim Table As DAO.TableDef
For Each Table In CurrentDb.TableDefs
If Table.SourceTableName = "" Then
If DCount("*", Table.Name) = 0 Then
Debug.Print Table.Name
End If
End If
Next
End Function

Searching function for textbox and letting my function still run when there are none entries in for the textbox and listbox

All I really need to know is how to make it where I can make selections in multiple multi-select listboxes, but leave any number of them blank and still have the macro/query work without having to put in an error message about it.
This also includes doing the same with the textboxes. The textboxes would function the same as the listboxes where they would search for anything in a data table to matches what I am looking for in the records and display what I am looking for in a table.
Here is my code
Private Sub Command62_Click()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim varItem As Variant
Dim District As String
Dim Circumstance As String
Dim Location As String
Dim Method As String
Dim Point As String
Dim Rank As String
Dim strSQL As String
Set db = CurrentDb()
Set qdf = db.QueryDefs("qryMultiselect")
For Each varItem In Me!District.ItemsSelected
District = District & ",'" & Me!District.ItemData(varItem) & "'"
Next varItem
If Len(District) = 0 Then
MsgBox "You did not select anything in the Distrcit field." _
, vbExclamation, "Nothing to find!"
Exit Sub
End If
District = Right(District, Len(District) - 1)
For Each varItem In Me!Circumstance.ItemsSelected
Circumstance = Circumstance & ",'" & Me!Circumstance.ItemData(varItem) &
"'"
Next varItem
If Len(Circumstance) = 0 Then
MsgBox "You did not select anything in the Circumstance field." _
, vbExclamation, "Nothing to find!"
Exit Sub
End If
Circumstance = Right(Circumstance, Len(Circumstance) - 1)
For Each varItem In Me!Location.ItemsSelected
Location = Location & ",'" & Me!Location.ItemData(varItem) & "'"
Next varItem
If Len(Location) = 0 Then
MsgBox "You did not select anything in the Location field." _
, vbExclamation, "Nothing to find!"
Exit Sub
End If
Location = Right(Location, Len(Location) - 1)
For Each varItem In Me!Method.ItemsSelected
Method = Method & ",'" & Me!Method.ItemData(varItem) & "'"
Next varItem
If Len(Method) = 0 Then
MsgBox "You did not select anything in the Method field." _
, vbExclamation, "Nothing to find!"
Exit Sub
End If
Method = Right(Method, Len(Method) - 1)
For Each varItem In Me!Point.ItemsSelected
Point = Point & ",'" & Me!Point.ItemData(varItem) & "'"
Next varItem
If Len(Point) = 0 Then
MsgBox "You did not select anything in the Point field." _
, vbExclamation, "Nothing to find!"
Exit Sub
End If
Point = Right(Point, Len(Point) - 1)
For Each varItem In Me!Rank.ItemsSelected
Rank = Rank & ",'" & Me!Rank.ItemData(varItem) & "'"
Next varItem
If Len(Rank) = 0 Then
MsgBox "You did not select anything in the Rank field." _
, vbExclamation, "Nothing to find!"
Exit Sub
End If
Rank = Right(Rank, Len(Rank) - 1)
strSQL = "SELECT * FROM tblDataEntry " & _"WHERE tblDataEntry.District
IN(" & District & ") AND tblDataEntry.Circumstance IN(" & Circumstance &
") AND tblDataEntry.Location IN(" & Location & ") AND tblDataEntry.Method
IN (" & Method & ") AND tblDataEntry.Point IN (" & Point & ") AND
tblDataEntry.Rank IN(" & Rank & ");"
qdf.SQL = strSQL
DoCmd.OpenQuery "qryMultiselect"
Set db = Nothing
Set qdf = Nothing
End Sub
I still need to add the textboxes, but I'm not sure where. (Please note that I'm still learning VBA).
Firstly, since you are repeatedly performing the same operation for each form control (in this case, constructing a comma-delimited string from the selected items), you can abstract this operation away into a function, and pass such function each List Box function.
For example, you could define a function such as:
Function SelectedItems(objBox As ListBox) As String
Dim strRtn As String, varItm
For Each varItm In objBox.ItemsSelected
strRtn = strRtn & ",'" & objBox.ItemData(varItm) & "'"
Next varItm
If strRtn <> vbNullString Then SelectedItems = Mid(strRtn, 2)
End Function
Which could then be evaluated with a List Box control argument, and would return either a null string ("") or a comma-delimited string of the selected items in the list box, e.g. something like:
?SelectedItems(Forms!Form1!List1)
'A','B'
Furthermore, since your form controls appear to be named consistently relative to the fields in your table, you could further condense your code to something along the following lines:
Private Sub Command62_Click()
Dim strSQL As String
Dim strArr As String
Dim varItm
For Each varItm In Array("District", "Circumstance", "Location", "Method", "Point", "Rank")
strArr = SelectedItems(Me.Controls(varItm))
If strArr <> vbNullString Then
strSQL = strSQL & "t." & varItm & " in (" & strArr & ") and "
End If
Next varItm
If strSQL <> vbNullString Then strSQL = "where " & Left(strSQL, Len(strSQL) - 5)
With CurrentDb.QueryDefs("qryMultiselect")
.SQL = "select * from tblDataEntry t " & strSQL
End With
DoCmd.OpenQuery "qryMultiselect"
End Sub
Note that the above is entirely untested.
Here, the main for each loop iterates over an array of strings corresponding to the names of your form controls and the names of your table fields.
For each form control in this array, the function obtains a comma-delimited string of the selected items in the control, and concatenates this with the existing SQL code only if one or more items have been selected.
As such, if not items are selected, the field will not feature in the SQL where clause.
If any filter has been selected, the trailing five characters (and) are trimmed from the end of the SQL string, and the where keyword is concatenated to the start of the SQL string - this ensures that if no filter has been selected, the resulting SQL code will not include a where clause.
Finally, the SQL for the query definition is updated and the query is opened, per your original code.
Where textboxes are concerned, the task merely need to skip the call to SelectedItems and obtain the value of the textbox directly.
Here is an example incorporating both listboxes & textboxes:
Private Sub Command62_Click()
Dim strSQL As String
Dim strArr As String
Dim varItm
For Each varItm In Array("District", "Circumstance", "Location", "Method", "Point", "Rank")
strArr = vbNullString
Select Case Me.Controls(varItm).ControlType
Case acListBox
strArr = SelectedItems(Me.Controls(varItm))
Case acTextBox
If Not IsNull(Me.Controls(varItm).Value) Then
strArr = "'" & Me.Controls(varItm).Value & "'"
End If
End Select
If strArr <> vbNullString Then
strSQL = strSQL & "t." & varItm & " in (" & strArr & ") and "
End If
Next varItm
If strSQL <> vbNullString Then strSQL = "where " & Left(strSQL, Len(strSQL) - 5)
With CurrentDb.QueryDefs("qryMultiselect")
.SQL = "select * from tblDataEntry t " & strSQL
End With
DoCmd.OpenQuery "qryMultiselect"
End Sub
I hope this helps, but please note that the above is untested and only theory.

Openrecordset With Multiple Tables, Access 2016

I am getting an "Object variable or With block variable not set (Error 91)" error with the following code:
Dim db As Database
Dim rs As Recordset
Private Sub Form_Load()
Dim mySQL As String
mySQL = "SELECT Tuteurs.ID_Tuteur, Tarifs_17_18.*, Paiements_17_18.* " & _
"FROM (Tuteurs INNER JOIN Tarifs_17_18 ON Tuteurs.ID_Tuteur = Tarifs_17_18.TuteurID_Trf) " & _
"INNER JOIN Paiements_17_18 ON Tuteurs.ID_Tuteur = Paiements_17_18.TuteurID_Pmt " & _
"WHERE ID_Tuteur =" & [Forms]![Eleves]![TuteurID_Elv]
Set db = CurrentDb
Set rs = db.OpenRecordset(mySQL, dbOpenDynaset, dbSeeChanges)
rs.MoveFirst
End Sub
Private Sub btn_Enregistrer_Click()
Dim totIns As Integer
totIns = DSum("Montant", "Paiements_17_18", "[Mois_Regle]='Inscription'")
If totIns = rs!Tarif_Inscription Then
MsgBox "Yes" & totIns & " = " & rs!Tarif_Inscription
Else
MsgBox "No" & totIns & " # " & rs!Tarif_Inscription
End If
End Sub
totIns is working very well but rs!Tarif_Inscription is the missing object variable.
[Tarif_Inscription] is a field in the [Tarifs_17_18] Table.
Any Help Please?
I Found the Solution,
My recordset is not in scope and My variables are declared outside My procedure.
Solution found by #Moke123.

Linking tables in Access

I have an access database that links to 6 tables. These tables are updated weekly and kept in a folder with the date. I would like for my access program to ask the user to select the location of the tables with out specifically using the Linked Table Manager.
The following code will prompt a user for the full path and file name of the database to be linked to. I decided to do this rather than just prompt for a folder. I strongly suggest you look at the connect string for one of your linked tables and make sure no other parameters are specified other than something like ';DATABASE=C:\Foldera\YYMMDD\MyAccessDB.mdb"
Private Function ReLinkTables()
Dim dbs As DAO.Database
Dim tdf As DAO.TableDef
Dim tdf2 As DAO.TableDef
Dim strConn As String
Dim strNewPath As String
Dim strTableName As String
On Error GoTo ERROR_HANDLER
' Prompt user for new path...
strNewPath = GetFolder
' Exit if none
If strNewPath = "" Then
Exit Function
End If
Set dbs = CurrentDb
dbs.TableDefs.Refresh
' Find all the linked tables...
For Each tdf In dbs.TableDefs
'Debug.Print tdf.Name & vbTab & tdf.Connect
If Len(tdf.Connect) > 0 Then
strTableName = tdf.Name
Debug.Print "Linked Table: " & tdf.Name & vbTab & tdf.Connect
dbs.TableDefs.Delete strTableName ' Delete the linked table
strConn = ";DATABASE=" & strNewPath
Set tdf2 = CurrentDb.CreateTableDef(strTableName, dbAttachSavePWD, strTableName, strConn)
CurrentDb.TableDefs.Append tdf2
Else ' Not a linked table
'Debug.Print "Keep: " & tdf.Name & vbTab & tdf.Connect
End If
Next tdf
Set tdf = Nothing
Set tdf2 = Nothing
dbs.TableDefs.Refresh
dbs.Close
Set dbs = Nothing
MsgBox "Finished Relinking Tables"
Proc_Exit:
Exit Function
ERROR_HANDLER:
Debug.Print Err.Number & vbTab & Err.Description
Err.Source = "Module_Load_SQLSERVER_DATABASE: ReLinkTables at Line: " & Erl
If Err.Number = 9999 Then
Resume Next
End If
MsgBox Err.Number & vbCrLf & Err.Description
Resume Proc_Exit
Resume Next
End Function
Function GetFolder() As String
Dim fldr As FileDialog
Dim sItem As String
Set fldr = Application.FileDialog(msoFileDialogFilePicker)
With fldr
.Title = "Select a Folder"
.AllowMultiSelect = False
'.InitialFileName = "Z:\xxxxxxxx" ' You can change to valid start path
If .Show <> -1 Then GoTo NextCode
sItem = .SelectedItems(1)
End With
NextCode:
Debug.Print "User selected path: >" & sItem & "<"
If sItem = "" Then MsgBox "User did not select a path.", vbOKOnly, "No Path"
GetFolder = sItem
Set fldr = Nothing
End Function

Invalid use of Me keyword

This code is a function and not a private subroutine. I'm suddenly getting this error with the Me.[field name here]. I'm not getting that error in my other code, just in this one. Here's my full code without the boring end part, but I'm getting the error starting from the line:
Me.assignedby.Column(1)
Public Function AssignNullProjects() As Long
Dim db As dao.Database
Dim rs As dao.Recordset
Dim strSQL As String
assignedby = TempVars("user").Value
Set db = CurrentDb
strSQL = "SELECT CFRRRID FROM CFRRR WHERE assignedto Is Null"
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
If Not rs.BOF And Not rs.EOF Then
While Not rs.EOF
strSQL = "UPDATE CFRRR SET assignedto = " & GetNextAssignee & ", assignedby = " & Me.assignedby.Column(1) & ", Me.Dateassigned = #" & Now & "#, Me.actiondate = #" & Now & "#, Me.Workername = " & _
Me.assignedto.Column(0) & ", Me.WorkerID = " & Me.assignedto.Column(0) & " WHERE CFRRRID = " & rs!CFRRRID
db.Execute strSQL, dbFailOnError
rs.MoveNext
Wend
End If
rs.Close
db.Close
Set rs = Nothing
Set db = Nothing
What could be the possible reason for the above-stated error, and how it could be removed?
Put that code in the form's code module. When you try to use Me in a standard module, you will always get that "Invalid use of Me keyword" complaint.
Check out the "Invalid use of Me keyword" and "Me <keyword>" topics in Access' help system for further details.