类初始化时设置Recordset为类变量,访问时触发‘Record Deleted’错误
问题描述
重构旧MS Access数据库时,将分散在多窗体和模块中、被调用三次的学生实习岗位分配逻辑,整合到名为Distribution的类后,出现了与Recordset初始化位置相关的**"Record Deleted"(记录已删除)**错误,具体情况如下:
原代码运行逻辑
原代码在单个过程中初始化3个Recordset并完成分配逻辑:
Public Sub distribute() Dim students As Recordset Dim openings As Recordset Dim distribution As Recordset Set students = Currentdb.OpenRecordset("Einschreibung_studenten_liste_verteilung", db_open_dynaset) Set openings = Currentdb.OpenRecordset("Select*From pzeitraum_temp Order by Rnd(-Timer()*[id]", db_open_dynaset) Set distribution = Currentdb.OpenRecordset("praxiseinteilung_tmp", db_open_dynaset) ' 对比学生与岗位,满足条件则写入distribution表(约200行嵌套6-7层的逻辑) End Sub
重构后的类结构
将200行逻辑拆分为多个子过程,把Recordset设为类变量并在Class_Initialize中初始化:
Dim students As Recordset Dim openings As Recordset Dim distribution As Recordset Private Sub Class_Initialize() Set students = Currentdb.OpenRecordset("Einschreibung_studenten_liste_verteilung", db_open_dynaset) Set openings = Currentdb.OpenRecordset("Select*From pzeitraum_temp Order by Rnd(-Timer()*[id]", db_open_dynaset) Set distribution = Currentdb.OpenRecordset("praxiseinteilung_tmp", db_open_dynaset) End Sub Public Sub Distribute() ' 执行分配逻辑 routine1 routine2 ' 其他操作 End Sub Private Sub routine1() ' 操作Recordset routine3 ' 其他操作 End Sub Private Sub routine2() ' 操作Recordset End Sub Private Sub routine3() ' 操作Recordset End Sub
错误表现
- 调用
openings.FindFirst("Key =" & Searchvalue)后,openings.NoMatch = True,但该记录实际存在。 - 在
Distribute子过程开头插入遍历openings的循环时,第一条记录就触发**"Record Deleted"**运行时错误:
With openings Do While .EOF = False Debug.Print(!key) .MoveNext Loop End With
- 但将上述循环放在
Class_Initialize末尾时可正常打印所有key;若把Recordset初始化移回Distribute子过程,代码也能正常运行。
错误原因分析
- Recordset与数据库上下文的绑定失效:
CurrentDb每次调用都会返回新的数据库对象实例,在Class_Initialize中初始化Recordset时绑定的CurrentDb,和后续子过程中隐式依赖的CurrentDb可能不是同一个实例,导致Recordset失去有效绑定,访问时触发错误。 - 临时表数据的时效性问题:
代码中使用的pzeitraum_temp和praxiseinteilung_tmp都是临时表,若在类初始化后、调用Distribute前,有其他操作修改或清空了临时表数据,会导致Recordset指向的记录失效,触发"记录已删除"错误。而在Distribute中初始化Recordset,能保证读取的是最新数据。 - 动态排序的Recordset状态异常:
openings的SQL使用了Order by Rnd(-Timer()*[id])动态排序,该逻辑依赖Timer()的即时值。在Class_Initialize中初始化时排序已执行,后续若临时表数据变动,Recordset的游标无法正确定位,导致访问出错。
解决方案建议
- 延迟Recordset初始化到
Distribute方法:
保留类变量,但不在Class_Initialize中初始化,而是在Distribute开头统一初始化,确保Recordset使用最新的数据库上下文和临时表数据:Public Sub Distribute() Set students = Currentdb.OpenRecordset("Einschreibung_studenten_liste_verteilung", db_open_dynaset) Set openings = Currentdb.OpenRecordset("Select*From pzeitraum_temp Order by Rnd(-Timer()*[id]", db_open_dynaset) Set distribution = Currentdb.OpenRecordset("praxiseinteilung_tmp", db_open_dynaset) ' 后续分配逻辑 routine1 routine2 End Sub - 显式保存数据库对象引用:
若坚持在Class_Initialize中初始化,先将CurrentDb的引用保存到类变量,确保所有Recordset绑定同一个数据库实例:Dim db As DAO.Database Dim students As Recordset Dim openings As Recordset Dim distribution As Recordset Private Sub Class_Initialize() Set db = CurrentDb Set students = db.OpenRecordset("Einschreibung_studenten_liste_verteilung", db_open_dynaset) Set openings = db.OpenRecordset("Select*From pzeitraum_temp Order by Rnd(-Timer()*[id]", db_open_dynaset) Set distribution = db.OpenRecordset("praxiseinteilung_tmp", db_open_dynaset) End Sub - 锁定临时表避免数据篡改:
确认类实例存在期间,没有其他进程或代码对临时表执行删除、清空操作,必要时在初始化Recordset前添加临时表锁定逻辑,防止数据被意外修改。 - 优化动态排序逻辑:
若不需要每次初始化都随机排序,可先将排序结果写入临时表,再基于该表打开Recordset,减少动态排序带来的状态异常风险。
内容的提问来源于stack exchange,提问作者Steve Piller
相关产品推荐
相关产品推荐

