Provided by Allen Browne, November 2003. Updated April 2010.
Extended DLookup()
The DLookup() function in Access retrieves a value from a table. For basic information on how to use DLookup(), see Getting a value from a table.
Why a replacement?
DLookup() has several shortcomings:
- It just returns the first match to finds. Since you cannot specify a sort order, the result is unpredictable. You may even get inconsistent results from the same data (e.g. after compacting a database, if the table contains no primary key).
- Its performance is poor.
- It does not clean up after itself (can result in Not enough databases/tables errors).
- It returns the wrong answer if the target field contains a zero-length string.
ELookup() addresses those limitations:
- An additional optional argument allows you to specify a sort order. That means you can specify which value to retrieve: the min or max value based on any sort order you wish to specify.
- It explicitly cleans up after itself.
- It runs about twice as fast as DLookup(). (Note that if you are retrieving a value for every row of a query, a subquery would provide much better performance.)
- It correctly differentiates a Null and a zero-length string.
Limitations of ELookup():
- If you ask ELookup() to concatenate several (not memo) fields, and more than 255 characters are returned, you strike this Access bug:
Concatenated fields yield garbage in recordset.
- DLookup() can call the expression service to resolve an argument such as:
DLookup("Surname", "Clients", "ClientID = [Forms].[Form1].[ClientID]")
You can resolve the last issue by concatenating the value into the string:
ELookup("Surname", "Clients", "ClientID = " & [Forms].[Form1].[ClientID])
Before using ELookup() in a query, you may want to modify it so it does not pop up a MsgBox for every row if you get the syntax wrong. Alternatively, if you don't mind a read-only result, a subquery would give you faster results than any function.
How does it work?
The function accepts exactly the same arguments as DLookup(), with an optional fourth argument. It builds a query string:
SELECT Expr FROM Domain WHERE Criteria ORDER BY OrderClause
This string opens a recordset. If the value returned is an object, the requested expression is a multi-value field, so we loop through the multiple values to return a delimited list. Otherwise it returns the first value found, or Null if there are no matches.
Note that ELookup() requires a reference to the DAO library. For information
on setting a reference, see References.
Public Function ELookup(Expr As String, Domain As String, Optional Criteria As Variant, _
Optional OrderClause As Variant) As Variant
On Error GoTo Err_ELookup
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim rsMVF As DAO.Recordset
Dim varResult As Variant
Dim strSql As String
Dim strOut As String
Dim lngLen As Long
Const strcSep = ","
varResult = Null
strSql = "SELECT TOP 1 " & Expr & " FROM " & Domain
If Not IsMissing(Criteria) Then
strSql = strSql & " WHERE " & Criteria
End If
If Not IsMissing(OrderClause) Then
strSql = strSql & " ORDER BY " & OrderClause
End If
strSql = strSql & ";"
Set db = DBEngine(0)(0)
Set rs = db.OpenRecordset(strSql, dbOpenForwardOnly)
If rs.RecordCount > 0 Then
If VarType(rs(0)) = vbObject Then
Set rsMVF = rs(0).Value
Do While Not rsMVF.EOF
If rs(0).Type = 101 Then
strOut = strOut & rsMVF!FileName & strcSep
Else
strOut = strOut & rsMVF![Value].Value & strcSep
End If
rsMVF.MoveNext
Loop
lngLen = Len(strOut) - Len(strcSep)
If lngLen > 0& Then
varResult = Left(strOut, lngLen)
End If
Set rsMVF = Nothing
Else
varResult = rs(0)
End If
End If
rs.Close
ELookup = varResult
Exit_ELookup:
Set rs = Nothing
Set db = Nothing
Exit Function
Err_ELookup:
MsgBox Err.Description, vbExclamation, "ELookup Error " & Err.number
Resume Exit_ELookup
End Function