The documentation clearly states that, if you open a Recordset
that has no records:
BOF
will be true
EOF
will be true
RecordCount
will be 0
For a non-empty Recordset
, neither BOF
and EOF
are true until you move beyond the first or last record.
Could it be that, from time to time, someone else could have added/deleted a record to one of the tables in the recordset you're just opening and change the resultset?
It could be the result of a race condition.
Rather than use BOF
or EOF
, you can test on Recordcount
: it's always 0
if the recordset is empty.
If the recordset is not empty, it will usually return 1
right after the recordset has been open; Recordcount
isn't an expensive operation in that case.
The only way to really return the actual number of records is to issue a MoveLast
before calling Recordcount
to force all records to be loaded.
Usually, if I need to iterate through a resultset in read-only fashion:
Dim db as DAO.Database
Dim rs as DAO.RecordSet
Set db = CurrentDB()
Set rs = db.OpenRecordSet("...", dbOpenForwardOnly)
If Not (rs Is Nothing) Then
With rs
Do While Not .EOF
' Do stuff '
.MoveNext
Loop
.Close
End With
Set rs = Nothing
End If
Set db = Nothing
If I don't need to iterate through records but just test if anything was returned:
Set rs = db.OpenRecordSet("...", dbOpenForwardOnly)
If Not (rs Is Nothing) Then
With rs
If .RecordCount > 0 Then
' We have a result '
Else
' Empty resultset '
End If
.Close
End With
Set rs = Nothing
End If
Set db = Nothing
It's pretty defensive and you have to adapt to your circumstances, but it works correctly every time.
Regarding your 2nd question, testing (BOF
Or EOF
) after opening the recordset should be more foolproof than the And
version, although I'd use Recordcount
myself.
Edit following your revised question:
From the bit of code you added to your question, I see a couple of issues, the main one being that your SQL Statement is missing and ORDER BY
clause.
The problem is that you are expecting the resultset to be in the Begin Order
followed by End Order
sequence but your SQL Statement doesn't guarantee you that.
In most cases, since you're using an autoincrement as ID, the database engine will return the data in that natural order, but there is no guarantee that:
- It's always going to happen that way
- That the original data was saved in the expected sequence, resulting in IDs that are in the 'wrong' order.
So, whenever you have expectations about the sequence of the resultset, you must explicitly order it.
I would also refactor this bit of code:
' ids are autoincrement long integers '
SQLString = "select * from Orders where type = OrderBegin or type = OrderEnd"
Dim OrderOpen as Boolean
OrderOpen = False
Set rs = db.Openrecordset(SQLString)
If rs.bof <> True And rs.eof <> True Then
myrec.movelast
If rs.fields("type").value = BeginOrder Then
OrderOpen = True
End If
End If
Into a separate function similar to:
' Returns true if the given CustID has a Open Order, '
' false if they are all closed.'
Public Function IsOrderOpen(CustID as Long) As Boolean
Dim result as Boolean
result = False
Dim sql as String
' Here I assume that the Orders table has a OrderDateTime field that '
' allows us to sort the order in the proper chronological sequence '
' To avoid loading the complete recordset, we sort the results in a way '
' that will return the last used order type as the first record.'
sql = sql & "SELECT Type "
sql = sql & "FROM Orders "
sql = sql & "WHERE ((type = OrderBegin) OR (type = OrderEnd)) "
sql = sql & " AND (CustID=" & CustID & ")"
sql = sql & "ORDER BY OrderDateTime DESC, Type DESC;"
Dim db as DAO.Database
Dim rs as DAO.Recordset
Set db = CurrentDB()
Set rs = db.Openrecordset(sql, dbOpenForwardOnly)
If Not (rs Is Nothing) Then
If rs.RecordCount > 0 Then
result = (rs!type = BeginOrder)
End If
rs.Close
End If
Set rs = Nothing
Set db = Nothing
IsOrderOpen = result
End Function
This would make the whole thing a bit more robust.