为什么对于新的非空记录集,bof/eof 之一偶尔会成立
set recordsetname = databasename.openrecordset(SQLString)
if recordsetname.bof <> true and recordsetname.eof <> true then
'do something
end if
2 个问题:
上述测试可能会错误地评估为 false,但这种情况极为罕见 (我的代码中潜伏着一个,今天失败了,我相信这是 5 年来的日常使用中的第一次 - 这就是我发现它的方式)。 为什么对于非空记录集,bof/eof 之一偶尔会成立。 这种情况似乎很少见,我想知道为什么会发生这种情况。
这是一个万无一失的替代品吗:
if 记录集名称.bof <> true 或 recordsetname.eof <> 那么是真的
编辑以添加代码详细信息:
客户有订单,每个订单以 BeginOrder
项目开始,并以EndOrder
项及其之间是订单中的项。
SQL 是:
' 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
If OrderOpen F False Then
'code here to add new BeginOrder Item to Orders table '
End If
ShowOrderHistory 'displays the customer's Order history '
在这种情况下看起来是这样
BeginOrder
Item a
Item b
...
Item n
EndOrder
BeginOrder
Item a
Item b
...
Item n
EndOrder
BeginOrder
Item a
item b
...
Item m
BeginOrder <----should not be there as previous order still open
set recordsetname = databasename.openrecordset(SQLString)
if recordsetname.bof <> true and recordsetname.eof <> true then
'do something
end if
2 questions :
the above test can evaluate to false incorrectly but only extremely rarely
(I've had one lurking in my code and it failed today, I believe for the first time in 5 years of daily use-that's how I found it). Why very occasionally will one of bof/eof be true for a non-empty recordset. It seems so rare that I wonder why it occurs at all.Is this a foolproof replacement:
if recordsetname.bof <> true or recordsetname.eof <> true then
Edit to add details of code :
Customers have orders, each order begins with a BeginOrder
item and end with an EndOrder
item and in between are the items in the order.
The SQL is:
' 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
If OrderOpen F False Then
'code here to add new BeginOrder Item to Orders table '
End If
ShowOrderHistory 'displays the customer's Order history '
In this case which looks this this
BeginOrder
Item a
Item b
...
Item n
EndOrder
BeginOrder
Item a
Item b
...
Item n
EndOrder
BeginOrder
Item a
item b
...
Item m
BeginOrder <----should not be there as previous order still open
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
该文档明确指出,如果您打开一个没有记录的
Recordset
:BOF
将为 trueEOF
将为 trueRecordCount< /code> 将是
0
对于非空
Recordset
,在移动之前BOF
和EOF
都不是 true超出第一个或最后一个记录。是否有时其他人可能会向您刚刚打开的记录集中的表之一添加/删除一条记录并更改结果集?
这可能是竞争条件的结果。
您可以在
Recordcount
上进行测试,而不是使用BOF
或EOF
:如果记录集为空,则它始终为0
。如果记录集不为空,通常会在记录集打开后立即返回
1
; 在这种情况下,Recordcount
并不是一个昂贵的操作。真正返回实际记录数的唯一方法是在调用
Recordcount
之前发出MoveLast
以强制加载所有记录。通常,如果我需要以只读方式迭代结果集:
如果我不需要迭代记录而只需测试是否返回任何内容:
它非常具有防御性,您必须适应您的情况,但它可以正常工作每次。
关于你的第二个问题,打开记录集后进行测试(
BOF
或EOF
)应该比And
版本更加万无一失,尽管我会使用Recordcount
我自己。根据修改后的问题进行编辑:
从您添加到问题中的代码中,我看到了几个问题,主要的一个是您的 SQL 语句丢失,并且
ORDER BY
条款。问题是您期望结果集位于
Begin Order
后跟End Order
序列,但您的 SQL 语句不能保证这一点。在大多数情况下,由于您使用自动增量作为 ID,数据库引擎将按自然顺序返回数据,但不能保证:
因此,只要您对结果集的顺序有期望,就必须明确对其进行排序。
我还会将这段代码重构
为一个单独的函数,类似于:
这将使整个事情变得更加健壮。
The documentation clearly states that, if you open a
Recordset
that has no records:BOF
will be trueEOF
will be trueRecordCount
will be0
For a non-empty
Recordset
, neitherBOF
andEOF
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
orEOF
, you can test onRecordcount
: it's always0
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 callingRecordcount
to force all records to be loaded.Usually, if I need to iterate through a resultset in read-only fashion:
If I don't need to iterate through records but just test if anything was returned:
It's pretty defensive and you have to adapt to your circumstances, but it works correctly every time.
Regarding your 2nd question, testing (
BOF
OrEOF
) after opening the recordset should be more foolproof than theAnd
version, although I'd useRecordcount
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 byEnd 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:
So, whenever you have expectations about the sequence of the resultset, you must explicitly order it.
I would also refactor this bit of code:
Into a separate function similar to:
This would make the whole thing a bit more robust.
我一直使用的模式是:
我从未见过这种失败(还!)。 此处对此进行了描述:如何:检测 DAO 记录集的限制
顺便说一句,Allen Browne 的 VBA 陷阱:使用记录集可能会引起兴趣。
The pattern I have always used is:
I have never seen this fail (yet!). This is described here: How to: Detect the Limits of a DAO Recordset
As an aside, Allen Browne's VBA Traps: Working with Recordsets might be of interest.
@Renaud Bompuis 的回答非常好。 让我强调一点,对于非空记录集,DAO Recordcount 永远不会为零,这是我在确定记录集是否已返回记录时测试的唯一内容。 我使用 .EOF 循环遍历记录,但在测试是否有记录返回之前不要开始逐步遍历记录。
@Renaud Bompuis's answer is quite good. Let me emphasize the point that the DAO Recordcount is never zero for a non-empty recordset, and that is the only thing I ever test in determining if a recordset has returned records. I use .EOF for looping through the records, but don't start stepping through the records until I've already tested if there are records returned.
这就是DAO,对吧? 我自己更喜欢 ADO,但 IIRC 在某些情况下(动态集?),您需要导航 EOF 才能评估最终的行数。 是否在这种状态下 EOF 为 true,BOF 为 false(因为尚未导航),但是一旦 BOF 被导航,它就是 true(显然)并且 EOF 仍然为 true。 据推测,预计零行时的初始状态应该是瞬时的,但是五年一次的反常计时事件意味着您在非常早期的初始状态中捕获了它?
This is DAO, right? I'm more an ADO man myself but IIRC there are circumstances (dynaset?) where you need to navigate EOF in order for the final number of rows to be assessed. Could it be in this state that EOF is true, BOF is false (because it hasn't been navigated yet) but as soon as BOF is navigated it is true (obviously) and EOF remains true. Presumably the initial state when zero rows are expected is supposed to be instantaneous but a once-in-five-years freak timing incident means you captured it in a really early initial state?
我偶尔会在 Access 中遇到完全相同的错误(今天在 Access 2007 中链接到 SQL Server 后端),其中语句
if rst.bof 和 rst.eof 的
计算结果为 false,尽管 rst 代表空记录集。 当它发生时,VBA 启动,并且立即窗格中的调试器显示,确实 rst.bof 为真,rst.eof 为真,因此它似乎发生了一毫秒,然后被纠正,但在测试了逻辑之后。
I occasionally come across the exact same bug in access (had it today in Access 2007 linked to a sql server back end) where the statement
if rst.bof and rst.eof
evaluates to false despite rst representing an empty recordset. When it happened, VBA started and the debugger in the immediate pane showed that, indeed rst.bof was true and rst.eof was true, so it seems to happen for a millisecond and then is corrected, but after one has tested the logic.
这是一个可能的解决方案
可能是这样您的表单或模块已损坏。 导出/导入受影响的模块或表单,或尝试 /decompile 选项。 在我的例子中,一个查询在不应该返回的时候返回了空,但我认为核心问题可能是类似的。
Here's a possible solution
It could be that your form or module has gotten corrupted. Export/Import the affected module or form, or try the /decompile option. In my case a query was coming back empty when it shouldn't have, but I think the core problem could be similar.