【问题标题】:"Operation not supported in transactions" While Copy/Paste Records in Access“事务中不支持操作”在 Access 中复制/粘贴记录时
【发布时间】:2013-10-31 17:06:25
【问题描述】:

当将单元格从 Excel 复制/粘贴到 Access 子表单中时,如果没有创建父记录,则会导致错误 - 正如预期的那样。

问题在于,发生此错误后,访问被锁定在所有后续输入数据导致事务中不支持的操作错误的状态。当您打开表格时,您可以看到新添加的数据还没有在表格中 - 因此看起来好像 Access 实际上正在进行某种事务。

我尝试点击“保存”..“刷新”..甚至添加 AfterInsert 事件以强制执行 commitTrans(),但没有运气 - Access 声称没有正在进行的事务

如果手动输入记录,则不会出错。该问题似乎仅在粘贴记录时出现。我猜 Access 正在创建一个事务来处理多个记录操作,并且没有从错误中正确恢复。

我可以删除“必需”标志,它会起作用 - 但是你有孤儿记录。我在想也许可以使用 After Insert Data Macro 添加一个具有默认批次名称的新批次,并将新的 BatchID 自动填充到 Items 表中。不过,我不确定如何做到这一点。

我确实尝试使用更改前数据宏删除“必需”标志并捕获错误 - 但虽然它减少了错误 - 它仍然产生相同的事务中不支持的操作 错误。

我在 Microsoft 知识库中查找了该错误,但没有找到任何特定于我的情况的内容。我在stackoverflow中搜索了错误消息,但一无所获。

我创建了一个新数据库并且能够复制该问题。

复制步骤

设置数据库

  1. 在 Access 2010 中创建一个新的 ACCDB 数据库
  2. 创建一个名为“Batches”的表,其中包含以下字段:
    • BatchID(自动编号)(主键)
    • 批次名称(文本)
  3. 创建一个名为“Items”的表,其中包含以下字段:
    • RecordID(自动编号)(主键)
    • BatchID(长整数)
      • 必需属性设置为True
    • 数据 - 文本
  4. 创建关系,将 Batches.BatchID 链接到 Items.BatchID
    • 包括批次中的所有记录,以及项目中的匹配记录
    • 实施参照完整性
    • 级联更新/删除
  5. 创建一个名为“表单”的表单
    • 将记录源设置为批次
    • 在 BatchID 和 Batch 名称文本框中添加
    • 添加子表单/子报表控件
      • 将源对象设置为“Table.Items”
      • 将链接主字段设置为“BatchID”
      • 将链接子字段设置为“BatchID”
      • 设置“在空母版上过滤”= 是
  6. 创建示例数据(使用表单)
    • 批量创建新记录。
      • 设置 BatchName = "Test"
    • 在项目中创建新记录。
      • 参考批次记录。
      • 设置数据 =“测试”

如您所见,手动操作很好。

从 Excel 复制和粘贴数据

  1. 在 Excel 中
    • 从 A1-A10 向下每个单元格输入一个字母:A,B,C,D,E,F,G,H,I,J
    • 突出显示单元格 A1-A10
    • 复制(Control+C)
  2. 在 Access 中,使用表单:
    • 添加新的批次记录。它应该在 BatchID TextBox 中显示“(新)”
    • 请勿输入批次名称
    • 在子表单中,单击新记录的记录选择器 (*) 以选择整行。键入 Control+V 进行粘贴。
    • 单击“确定”“您必须在“Data.BatchID”字段中输入一个值。 错误
      • 此错误可能会重复出现。继续点击确定
      • 如果它询问“是否要抑制更多错误消息...”,请回答是
    • 对于 “Microsoft Access 无法粘贴的记录已插入到名为“粘贴错误”的新表中。通知
    • 单击“确定”
    • 用“TestName”填写批次名称文本框
    • 尝试优雅地恢复。点击逃生。更改记录。

此时 - 您应该会看到输入的 BatchID、批次名称和新数据。一切似乎都按预期工作。如果您尝试刷新或导航到另一批记录 - 您将收到错误 Operation not supported in transactions。 Access 将继续显示此错误消息,直到我们关闭并重新打开表单。您粘贴的数据不会进入数据库。

通常,有点技术知识的人会意识到某些事情进展不顺利并关闭数据库并重新打开......但不幸的是,我有用户在任何弹出框上玩“打地鼠”然后尝试继续 - 所以我想让事情尽可能地防弹。

所需的解决方案

我想要解决这个问题的方法,最终不会导致访问、重复值等其他怪癖。

根据我自己的经验,使用 VBA 来“修复”密钥并不可靠。数据宏似乎更可靠 - 但设置起来可能很棘手 - 它们还不是很主流(我想说应该有一个 ms-access-data-macros 标签在stackoverflow上,但还没有)

【问题讨论】:

  • 能否附上或链接ACCDB和xls的示例文件
  • 您在游戏说明的哪一部分遇到了困难?我想澄清这个问题,以便可以遵循。至于附件 - Stackoverflow 不支持文件附件。当链接到另一个网站(即文件托管)时,原始问题往往比附件更有效 - 因此发现问题的人无法访问附件。
  • 我无法重新创建您的结果,即使您使用的是 .accdb 文件的副本。当我粘贴时,我得到“您必须在 'Items.BatchID' 字段中输入一个值。”信息。在我关闭它之后,子表单仍然很脏,但之后我所做的任何事情都会重新显示该消息,直到我点击 [Esc] 来撤消子表单更改。然后事情就恢复正常了。我在 32 位和 64 位 Access 2010 上都试过了,只是想看看它是否有任何区别(它没有)。
  • 我也无法重新创建。但我肯定知道在某些情况下会出现这样的错误,因为我在访问 2000 时遇到了这种错误。但我不记得我们是如何提升它以及如何解决它的。
  • 好的,我现在可以重新创建它了。建议改进说明:“在子表单中,单击新记录的记录选择器 (*) 以选择整行,然后键入 [Ctrl-V] 进行粘贴。” (之前我只是单击了新记录上的 Data 字段然后粘贴,因为这是我要粘贴数据的字段。)

标签: ms-access ms-access-2010


【解决方案1】:

建议的解决方法:

在 [Batches] 表中,将 [BatchName] 字段的Required 属性设置为Yes

将 [BatchID] 文本框的 Tab Stop 属性更改为“否”。这将使 [BatchName] 文本框在表单打开时成为默认焦点。

让表单的 On Current 事件为 [BatchName] 文本框提供新记录的焦点 (IsNull(Me.BatchID) = True)。

当 [BatchName] 文本框失去焦点时使表单变脏。

Option Compare Database
Option Explicit

Dim DeletePending As Boolean

Private Sub Form_Load()
    DeletePending = False
    Me.ItemSubForm.Enabled = False  ' Disable Subform by default
End Sub

Private Sub Form_Current()
    If IsNull(Me.BatchID) Then
        Me.BatchName.SetFocus
        ' Disable Subform if BatchID is NULL
        Me.ItemSubForm.Enabled = False
    Else
        ' Enable SubForm Otherwise
        Me.ItemSubForm.Enabled = False 
    End If
End Sub

Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)
    DeletePending = True
End Sub

Private Sub Form_AfterDelConfirm(Status As Integer)
    DeletePending = False
End Sub

Private Sub BatchName_LostFocus()
    If IsNull(Me.BatchID) And Not DeletePending Then
        Me.Dirty = True
    End If
End Sub

当用户点击子表单(并关闭 [BatchName] 文本框)时,他们会使表单变脏并且 BatchID 获得一个值。然后他们可以粘贴,但不会收到 [BatchID] 的“您必须输入值...”消息。如果他们没有输入 [BatchName] 值,他们现在会被提示输入 it(因为它现在是 Required),但至少他们可以正常地从中恢复。

2013-11-09 18:40 UTC 更新:

在玩这个多一点的过程中,我发现了两个怪癖:

  1. 如果您删除了最后一条父记录,Access 会执行此操作,然后立即在其位置创建另一个新记录。

  2. 如果您导航到“新”父记录,然后立即退出(例如,通过 PageDownPageUp 键)Access 将创建一个新的记录,然后把你留在那个记录上,表格很脏。按 Esc 然后移出“新”记录是有效的,它不会导致任何错误,但它肯定会让用户感到困惑。

我已更新答案以尝试解决这些问题。我添加了更多 VBA 代码来跟踪“DeletePending”状态。我还在 [Batches] 表中添加了将 [BatchName] 设置为 Required = Yes 的要求。如果用户移动到“新”记录然后立即再次移出,后者会使它稍微更直观(尽管更烦人)。

【讨论】:

  • 您所拥有的对于阻止用户立即粘贴很有用 - 但如果他们点击转义,他们仍然可以粘贴并导致错误。但是,如果您禁用子表单 - 您的解决方案似乎是防弹的。我会建议对您的问题进行编辑以添加到检查中。
  • 我的想法是在 Items 表上使用 After Insert 数据宏。如果 BatchID 为空,它会检查两个本地变量。第一个是时间戳 - 如果它在当前时间的一秒或两秒内,它会将 BatchID 设置为第二个本地变量中的值。如果没有时间戳,或者它太旧,它会在批次中创建一个新记录,获取新的 BatchID - 然后设置时间戳和批次ID。我喜欢这个解决方案的地方在于它可以在没有形式和不受信任的模式下工作。我不喜欢的是我不知道如何实现它:)
【解决方案2】:

我为此苦苦挣扎了很长时间,直到我终于明白是什么导致了这个错误。详细介绍需要一篇相当长的文章,而不是博客回复来解释这一切。如果有人有兴趣可以留下联系方式,我会联系他们并详细说明。 但是,对于那些想弄清楚这一点的人,我可以通过向您提供问题背后的想法来为您节省大量时间: 当您在绑定的子表单中执行数据事务时,您不能引用其他对象。交易过程的内部代码不允许这样做。例如,如果您在 Form_BeforeUpdate 事件中有代码尝试在子表单数据事务中打开另一个表单,您将收到错误 3246。您可以拥有创建变量、设置值、引用子表单的代码表单控件等,但您不能转到另一个对象。 如果您考虑一下,这是有道理的。一旦用户或代码进入另一个表单或对象,谁知道它会做什么。它可能永远不会回来或卷入导致交易挂起的其他错误。这就是交易必须首先完成的原因。 这意味着您必须捕获并消除错误 2115,该错误是由于用户在事务处理过程中尝试单击子表单之外的区域而导致的。这通常发生在大型复制和粘贴期间,其中用户在子表单事务中变得住院或开始继续到另一条记录。

【讨论】:

    【解决方案3】:

    我知道这是一个老故事,我也在为此苦苦挣扎。 我的解决方案是重新设计流程,以便用户关闭接收数据的表单以保存插入的记录。既不优雅但高效,让我免于猜测可能发生的每一个事件。

    【讨论】:

      【解决方案4】:

      避免不受欢迎的内部事务足以 使用以下代码对子表单错误事件进行编码:

      Private Sub Form_Error(DataErr As Integer, Response As Integer)
      
          Response = acDataErrContinue
      
      End Sub
      

      拦截子表单事件的一般方法是

      ' parent form code
      ' ---------------------------------------------------
      Private WithEvents subFormObj As [Form_Sottomaschera items]
      
      Private Sub Form_Open(Cancel As Integer)
      
          Set subFormObj = Me.Sottomaschera_items.Form
      
      End Subcode here
      
      ' asynchronous way
      Private Sub subFormObj_isInserting()
      
          Me.batchName = vbNullString         ' this resolves the new ID issue
      
      End Sub
      
      ' synchronous way
      Public Sub subFormInserting()
      
          Me.batchName = vbNullString
      
      End Sub
      
      ' sub-form code
      ' ---------------------------------------------------
      Public Event isInserting()              ' for asynchronous way
      
      Dim parentFormObj As Form_Maschera1     ' for synchronous way
      
      Private Sub Form_BeforeInsert(Cancel As Integer)
      
          parentFormObj.subFormInserting
          RaiseEvent isInserting
      '    Cancel = True
      
      End Sub
      
      Private Sub Form_Error(DataErr As Integer, Response As Integer)
      
          Response = acDataErrContinue
      
      End Sub
      
      Private Sub Form_Open(Cancel As Integer)
      
          Set parentFormObj = Me.Parent
      
      End Sub
      

      其中[Maschera1] 是主窗体,[Sottomaschera items] 是子窗体。

      很遗憾,它不能解决粘贴问题。

      要彻底解决问题,您需要保存父记录 + SetFocus 技巧,同步或异步:

      Private Sub subFormObj_isInserting()
      
          Me.batchName = vbNullString
          DoCmd.RunCommand acCmdSaveRecord
      '    DoEvents
          Me.batchName.SetFocus
      
      End Sub
      
      Public Sub subFormInserting()
      
          Me.batchName = vbNullString
          DoCmd.RunCommand acCmdSaveRecord
      '   DoEvents
          Me.batchName.SetFocus
      
      End Sub
      

      【讨论】:

      • 我已经试过了——正如你所指出的——它不会停止粘贴错误——而且表单错误事件似乎也没有捕获它。
      • 不幸的是,这在我的情况下不起作用,因为重点是将 Excel 中的记录粘贴到 Access 数据表中。
      • 我很抱歉想要坚持......这是一个赏金问题:)。我省略了数据表请求点以及您需要粘贴数据的方式。但是,如果您选择使用子表单并在运行时获取所需的视图,您将获得所需的粘贴操作,只需选择目标表列的标题即可。您将获得访问数据表的可能性和对子窗体的控制,只需在主窗体打开事件中设置 acCurViewDatasheet 视图模式。此外,对于针对粘贴数据并在数据表模式下查看的文本框,您必须将 TextBox.EnterKeyBehavior 设置为 False
      【解决方案5】:

      我不明白你到底想达到什么目标,所以这个答案可能不够充分。 你可以

      • Me.NewRecord = True 时设置子表单属性.Visible = False 以防止向其中输入数据
      • 在按Enter键触发的更新后事件中设置.Dirty = False,在添加批处理名称后强制将主窗体的记录保存到表中。它还允许避免在某些数据库中向主表单添加少量记录后不将子表单的记录保存到表中,至少使用动态子表单 .Recordsource。
      • 设置您的子表单属性.Visible = True

      下面的代码适用于表单视图,也许应该以某种方式扩展(开发)用于其他视图。

      将子窗体 Child 和所有其他要隐藏/显示的控件的 .Tag 设置为“a”。

      Private Sub Form_Current()
      
      If Me.CurrentView = 1 Then
      
          If Me.NewRecord = True Then
             ShowControls False
          ElseIf Me![Items subform Child].Visible = False Then
             ShowControls True
          End If
      
      End If
      
      End Sub
      
      Private Sub BatchName_Text_AfterUpdate()
      
      Dim NewRecordx As Boolean
      
      If Me![Items subform Child].Visible = False And Me.CurrentView = 1 Then ShowControls True
      
      NewRecordx = Me.NewRecord
      
      If Me.Dirty Then Me.Dirty = False 'save the new record to the table
      
      If Me.CurrentView = 1 And NewRecordx Then Me![Items subform Child].Form.Requery
      
      End Sub
      
      Private Sub ShowControls(bVisible As Boolean)
      
      Dim ctl As Control
      
      For Each ctl In Me.Controls
         If ctl.Tag = "a" Then ctl.Visible = bVisible
      Next ctl
      
      End Sub
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-16
        • 1970-01-01
        相关资源
        最近更新 更多