afterupdate 在主键中留下空白

Posted

技术标签:

【中文标题】afterupdate 在主键中留下空白【英文标题】:afterupdate leaves gaps in primary key 【发布时间】:2013-10-02 16:23:19 【问题描述】:

我正在开发一个访问数据库,其中包含一些仅用于数据输入的表单。问题是,当我打开这些表单中的任何一个时,访问会在基础表中创建一个新条目,包括增加基础表的自动编号主键,并且即使用户选择不创建记录,自动编号也会保持递增数据库。这意味着由于用户在不提交更改的情况下打开和关闭表单的所有时间,实际表中的自动编号序列中存在间隙。

我在 beforeupdate 方法中使用了如下代码让用户丢弃坏数据:

Private Sub Form_BeforeUpdate(Cancel As Integer)
    'Provide the user with the option to save/undo
    'changes made to the record in the form
    If MsgBox("Data has been entered into this form." _
        & vbCrLf & vbCrLf & "Do you want to save this data?" _
        , vbYesNo, "Changes Made...") = vbYes Then
            DoCmd.Save
    Else
        DoCmd.RunCommand acCmdUndo
    End If
End Sub  

我曾想过使用 beforeinsert 事件,但在使用 beforeinsert 时打开表单时遇到问题。

有人可以告诉我如何设置,以便这些间隙不会在自动编号值的序列中产生吗?该数据库可能会被 10 个并发用户使用。

【问题讨论】:

【参考方案1】:

您不应该对自动编号字段的值赋予任何意义。自动编号允许您为每一行定义唯一值,这样做不保证它们是连续的。我强烈建议您只使用自动编号字段来识别唯一记录,而不是作为某种计数器或人类可读的值。

现在,除此之外,如果您需要每条记录真正具有顺序值,那么您需要做的是

    创建一个单独的表,其中只有一行,一列保存序列中的下一个值。 每当从数据输入表单中保存记录时,您就会以独占方式锁定该计数器表,防止任何其他用户对其进行读取或写入。 从计数器表中检索当前值并将其用于您的新数据条目记录中。 更新计数器表中的值以递增到下一个值。

Microsoft 提供了一个可以轻松锁定表的功能:DAOOpenTableExclusive(),可以在 here 找到。

您可以使用找到的代码here 创建一个计数器表。

【讨论】:

【参考方案2】:

如果您有表单集的记录源,则任何绑定控件的条目都会导致填充任何AutoNumber 字段。如果用户放弃或没有输入所有必需的信息,则跳过该自动编号。

为避免这种情况,您可以自己保存记录。假设您定义了下表:

并创建了以下表单:

后面有以下代码:

Private Sub cmdClose_Click()
  DoCmd.Close
End Sub


Private Sub cmdSave_Click()
  DoCmd.SetWarnings False
  DoCmd.RunSQL "INSERT INTO z_TestTable (DueDate, " & _
                                        "DateReceived, " & _
                                        "Terms, " & _
                                        "ECOFee, " & _
                                        "Classification) " & _
                               "VALUES (#" & txtDueDate & "#, " & _
                                       "#" & txtDateReceived & "#, " & _
                                       "'" & txtTerms & "', " & _
                                       txtECOFee & ", " & _
                                       "'" & txtClassification & "')"
  MsgBox ("RECORD SAVED")
End Sub

Private Sub txtClassification_AfterUpdate()
  funCheckForRequiredFields
End Sub


Private Sub txtDateReceived_AfterUpdate()
  funCheckForRequiredFields
End Sub


Private Sub txtDueDate_AfterUpdate()
  funCheckForRequiredFields
End Sub


Private Sub txtECOFee_AfterUpdate()
  funCheckForRequiredFields
End Sub


Private Sub txtTerms_AfterUpdate()
  funCheckForRequiredFields
End Sub


Function funCheckForRequiredFields()
  Dim NotComplete As Boolean
  NotComplete = False

  If (IsNull(txtDueDate)) Then NotComplete = True
  If (IsNull(txtDateReceived)) Then NotComplete = True
  If (IsNull(txtECOFee)) Then NotComplete = True
  If (IsNull(txtClassification)) Then NotComplete = True
  If (IsNull(txtTerms)) Then NotComplete = True

  If (NotComplete) Then _
    cmdSave.Enabled = False Else _
    cmdSave.Enabled = True
End Function

表单打开时禁用了保存记录按钮。 保存记录按钮仅在输入所有字段后启用。当按下Save Record 时,记录通过DoCmd.RunSQL 命令保存。

我将表单上控件的格式设置为与所需数据相对应。这可确保检查输入的数据的有效性。

通过使用此方法,使用自动编号字段并且没有针对它保存记录的唯一方法是如果 INSERT SQL 无效。这不应该发生,因为它是事先检查的。

【讨论】:

您不必使用未绑定的表单来获取 OP 正在寻找的内容。是的,这是解决它的一种方法,但我认为这是最后的手段,而不是第一选择。 Constablebrew 的帖子以更好的方式解决了这个问题。不要担心自动编号中的空白,如果您确实关心它,请不要使用自动编号。【参考方案3】:

您可以使用 max 函数从表中的列中确定最大值,然后在保存之前添加一个。

编辑: 要让它在多用户环境中工作,请将字段设置为“索引为”是(无重复)。

【讨论】:

如果有多个用户或可能同时打开多个记录,则可能会出现重复。 这将是您回答中的一个重要疏忽。两者齐头并进。将其添加到您的答案中,我认为您的答案是最简单的解决方案。还需要提及的是,必须捕获引发的错误(那是什么 err#?)并循环直到保存成功。

以上是关于afterupdate 在主键中留下空白的主要内容,如果未能解决你的问题,请参考以下文章

仅在主表上作为复合主键

3sql

数据库 三个范式

在一个查询中从多个主键中提取条目

我不确定为啥会出现此错误。删除主键中的重复记录

从列而不是主键中选择最大值的正确方法是啥