【问题标题】:Cancel update of single field on MS Access 2007 bound form取消更新 MS Access 2007 绑定表单上的单个字段
【发布时间】:2011-04-20 18:46:41
【问题描述】:

我可能想多了,但我看到了各种潜在的问题......

我有一张桌子,我会叫主人。在其他项目中,它包含一个名为 Serial 的字段。我认为这将是一个静态项目 - 即,对于 Master 中的任何给定行,它永远不会改变(一旦最初分配)。我现在发现它可以更改,不仅如此,我还需要存储这些更改的历史记录。

我可以将 Serial 分成一个单独的表,其中每一行都有一个 MasterId、EffectiveDate 和 Termination Date(Master 行的当前 Serial 将是具有空终止日期)。到目前为止,一切都很好......

现在我要问的问题是:我有一个基于 Select from Master 的绑定表单,其中包括 Serial。

认为我想要做的是使这种可变性对用户透明,是短路对 Serial 文本框所做的任何更改,可能在文本框 BeforeUpdate 事件中.然后,我将使用代码终止 Serial 表中的当前行并插入一个新行,该行变为当前行并显示在文本框中。我会(我认为)需要取消表单记录集中的更新,不会丢失用户对其他字段所做的任何更改......

此时我的大脑开始吃午饭了,因为我仍然对未绑定的表单和保存按钮感到更加自在。我可以按照建议这样做吗?我需要了解哪些技巧和陷阱?如果需要,我可以将表单上的 Serial 字段设为只读,并要求弹出窗口进行编辑,但如果可以的话,我宁愿避免这样做。

**编辑

希望这会有所帮助:

之前

table Master
  Id             AutoNumber  PK
  Serial         Text(20)
  --other fields

当前frmMaster RecordSource:

SELECT Id, Serial, yada, yada FROM Master WHERE blahBlah

之后

table Master
  Id             AutoNumber  PK
  --other fields

table Serial
  Id             AutoNumber  PK
  MasterId       Long
  Serial         Text(20)
  EffDt          Date/Time
  TermDt         Date/Time

新的 frmMaster RecordSource:

SELECT Master.Id, Serial.Serial, yada, yada 
FROM Master LEFT JOIN Serial ON Master.Id = Serial.MasterId 
WHERE Serial.TermDt is Null AND blahBlah

表单上的“编辑”应导致:

  1. 序列中的选定行填充了 TermDt (Now())。不会更改此行上的 Serial.Serial 的值。
  2. 在 Serial 中创建了一个新行,具有新的 Serial、相同的 MasterId 和 EffDt = Now()
  3. 新序列显示在表单上,​​最好让用户看起来好像他们刚刚在适当的位置编辑过一样。
  4. 对表单上其他字段所做的任何更改都会保存。

再次编辑

好的,HansUp 给出的答案似乎涵盖了我需要的大部分内容。我剩下的绊脚石甚至可能不是问题(但如果是这样,有人可以解释原因吗?)。

添加 Master 的用例非常简单,但在更改 Serial 的更新用例中,我仍然感到困惑:如果 txtSerial 绑定到 Serial.Serial,并且用户更改了txtSerial,Access 不会尝试更新 Serial.Serial 的内容以匹配吗?如何在更改时添加序列行(看起来应该是一种享受),如何防止现有序列行的更新?

【问题讨论】:

  • 这很难想象,IMO。向我们展示您的 Master 和 SerialHistory 表中包含相关字段的示例。
  • 在您的示例中,串行字段是否成为主表和串行表的一部分?
  • 什么是 EffDt 以及如何发音?
  • 在我看来 Master 应该有一个字段 Serial_ID 作为 Serial.ID 的外键。
  • EffDt 是 EffectiveDate--我有一个习惯,就是在我与 DBA 合作时留下的需要它。

标签: ms-access ms-access-2007


【解决方案1】:

您尚未向我们展示 Serial 表的示例数据。我很好奇它是否可能是这样的:

Id MasterId Serial  date_added
 1        1 foo      4/21/2011 7:00:00 AM
 2        1 bar      4/21/2011 9:00:00 AM
 3        1 foo      4/21/2011 11:00:00 AM
 4        2 asldkjf  4/4/2011 1:00:00 PM

当您需要用 EffDt 和 TermDt 表示的 Serial 行时,您可以使用相关子查询。

SELECT
    s.Id,
    s.MasterId,
    s.Serial,
    s.date_added AS EffDt,
    (SELECT TOP 1 sub.date_added
    FROM Serial As Sub
    WHERE
        sub.MasterId = s.MasterId
        And sub.date_added > s.date_added
    ORDER BY sub.date_added) AS TermDt
FROM Serial AS s
ORDER BY
    s.MasterId,
    s.date_added;

我知道您的问题范围更广。现在,这件作品是我可以环绕的。但它符合您的要求吗?

更新:我使用了我的 Serial 表版本。我创建了一个主表,其中包含自动编号 ID、文本序列号以及 2 个其他文本字段,Other_field1 和 Other_field2。然后创建一个简单的连续表单,绑定到“SELECT Id, Serial, Other_field1, Other_field2 FROM Master Order By Id;”。

这是表单的代码:

Option Compare Database
Option Explicit
Dim varSerialOldvalue As Variant

Private Sub Form_AfterInsert()
    addSerialRow
End Sub

Private Sub Form_AfterUpdate()
    Dim strSql As String
    If Me.txtSerial.value <> varSerialOldvalue Then
        addSerialRow
    End If
End Sub

Private Sub Form_BeforeUpdate(Cancel As Integer)
    varSerialOldvalue = Me.txtSerial.OldValue
End Sub

Private Sub addSerialRow()
    Dim strSql As String
    Dim strMsg As String

On Error GoTo ErrorHandler

    strSql = "INSERT INTO Serial(MasterId, Serial, date_added)" & _
        vbNewLine & "VALUES(" & Me.txtid & ",'" & _
        Replace(Me.txtSerial, "'", "''") & "', #" & _
        Format(Now(), "yyyy/mm/dd hh:nn:ss") & "#);"

    CurrentDb.Execute strSql, dbFailOnError

ExitHere:
    On Error GoTo 0
    Exit Sub

ErrorHandler:
    strMsg = "Error " & Err.Number & " (" & Err.description _
        & ") in procedure addSerialRow" & vbNewLine & _
        "SQL: " & strSql
    MsgBox strMsg
    GoTo ExitHere
End Sub

这是您想要完成的有用的起点吗?

Update2:你说txtSerial 绑定到Serial.Serial;但我的方法将 txtSerial 直接绑定到 Master 表中的 Serial 字段......这就是我认为你最初提出的。现在这种方法有什么问题?

【讨论】:

  • 这确实向我展示了如何只使用一个日期。它没有回答我的主要问题——我将不得不对另一个编辑进行更多思考,看看我是否可以更清楚地说明这一点。
  • 再读一遍,它确实完成了我需要的大部分。请参阅我最近的编辑,了解我仍然坚持的一件事......
  • 好。不确定 Nulls for Serial;认为如果需要你可以解决这个问题。现在的一个症结是什么?我从昨天阅读了您的编辑,但我猜我没有看到您希望我看到的内容。
  • 我所说的编辑直到现在才出现,因为我先写了评论。我想在我引用它之前我应该​​知道足够的知识来创建“对象”(编辑)......
  • 啊哈!是的,我刷新了页面以检查是否有更新,但一定是太快了。
【解决方案2】:

实际上,我不明白为什么要阻止对文本框的更改?如果我没看错,你的意思是如果文本框发生变化,那么你需要保存之前的当前数据并记录在变化之前?

如果是上述情况,那么只需在串行文本框的更新后事件中,运行一个追加查询,从表中获取当前记录并复制(将其追加到其他地方)。

虽然您有一个绑定表单,但对表单上的控件所做的更改尚未提交到实际的基础表。如果您的代码或进程或某个附加查询或 VBA 代码从表中查看或复制该行,则所有旧值仍将保持不变。

所以绑定表单并不意味着文本框中的每个更改都写入表格,但实际上整个记录仅在记录保存时写入表格,这就是为什么更新事件之前的表格有在满足您的规则等之前,可以防止记录写入发生的取消。

因此,您只需要在更新事件之后的串行框中的一两行(除非对文本框进行更改,否则该事件不会触发)从表中复制当前记录,该表将包含所有旧值,包括旧的序列号。

【讨论】:

  • 我不确定我是否明白你在说什么——但如果我明白了,我认为它不会按我想要的方式工作。请参阅编辑。
  • 嗯...更多我想一想,也许我可以插入一个(复制的)串行行并更新 that 行的 TermDt,然后让保存继续进行,然后更新更改后的 Serial 行的 EffDt .... 但这似乎很复杂且容易出错。也许吧。
  • 不清楚您是否只想进行审核。在更新事件之前的表单串行文本框中,您可以使用 textbox.old 值,并设置表单级别变量,从而获得新旧值。此时,用户可能想要点击撤消而不是保存整个记录。因此,您不应更新该串行(审计)表。如果要保存记录,则在更新后形成旧序列当前然后简单地用新值+日期+时间等写出新记录。如果不是新的序列值,您仍然可以更新现有的序列行时间/日期更新值。
【解决方案3】:

如果 Master 不会经常更改且不会太大,则在保存时,附加整个 Master 记录,添加 whoChanged 记录和时间字段。然后,当您想查看 Serial 的变化时,只需在 Serial 上执行 GroupBy,您就可以看到时间和谁更改了它。

这对于一个领域来说是多余的,但通常提供了一种记录更改的好方法。

【讨论】:

    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 2012-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 1970-01-01
    相关资源
    最近更新 更多