取消 MS Access 2007 绑定表单上单个字段的更新

发布于 2024-11-02 10:02:42 字数 2034 浏览 0 评论 0原文

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

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

我可以将 Serial 分解为一个单独的表,其中每一行都有一个 MasterId、EffectiveDate 和终止日期(Master 行的当前 Serial 将是具有 Null TerminationDate 的一个(也是唯一一个 - 代码强制执行的)行) 。到目前为止,一切顺利......

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

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

此时我的大脑出去吃午饭了,因为我仍然对未绑定的表单更满意保存按钮。我可以按照提议这样做吗?有什么技巧&我需要了解哪些陷阱?如果需要,我可以将表单上的“串行”字段设置为只读,并需要弹出窗口来编辑它,但如果可以的话,我宁愿避免这种情况。

**编辑

希望这会有所帮助:

之前

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. 选定Serial 中的行被 TermDt 填充 (Now())。此行上 Serial.Serial 的值无需更改。
  2. 在 Serial 中创建一个新行,并
  3. 在表单上显示新的 Serial、相同的 MasterId 和 EffDt = Now() New Serial,最好让用户看起来就像刚刚就地编辑过一样。
  4. 对表单上其他字段所做的任何更改都会被保存。

再次编辑

好的,HansUp 已经给出了一个答案,看起来它涵盖了我需要的大部分内容。我剩下的绊脚石甚至可能不是问题(尽管如果是这样,有人可以解释为什么吗?)。

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

I may be overthinking this, but I see all sorts of potential problems....

I have a table that I'll call Master. Among other items, it contains a field called Serial. I thought this would be a static item--i.e., that it would never change (once initially assigned), for any given row in Master. I now find out that it can change, and not only that, I need to store history of those changes.

I can break Serial out into a separate table, where each row will have a MasterId, EffectiveDate and Termination Date (the current Serial for a Master row would be the one (and only one--code-enforced) row with a Null TerminationDate). So far, so good....

Now the problem that I'm asking about: I have a bound form built on a Select from Master that includes Serial.

What I think I want to do, to make this changeability transparent to the user, is to short-circuit any changes that are made to the Serial text box, probably in the text box BeforeUpdate event. I'd then use code to Terminate the current row in the Serial table & insert a new row, which becomes current and is displayed in the text box. I would (I think) need to cancel the update in the form's recordset, without losing any changes the user has made to other fields....

It's at this point that my brain goes out to lunch, since I'm still much more comfortable with unbound forms & Save buttons. Can I do this, as proposed? What tricks & traps do I need to know about? If need be, I can make the Serial field on the form read-only, and require a pop-up to edit it, but I'd rather avoid that if I can.

**EDIT

Hope this helps:

Before

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

Current frmMaster RecordSource:

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

After

table Master
  Id             AutoNumber  PK
  --other fields

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

New 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

An "edit" on the form should result in:

  1. Selected row in Serial gets TermDt filled (Now()). NO CHANGE is to be made to the value of Serial.Serial on this row.
  2. A new row in Serial is created, with new Serial, same MasterId and EffDt = Now()
  3. New Serial displayed on form, preferably such that it looks to the user as if they'd just edited in place.
  4. Any changes made to other fields on the form get saved.

EDIT, again

Okay, so HansUp has given an answer that looks like it covers most of what I need. My remaining stumbling block may not even be an issue (though if that's the case, can someone explain why?).

The use case of adding a Master is pretty straightforward, but in the use case of an update that changes the Serial I'm still confused: if txtSerial is bound to Serial.Serial, and the user changes the contents of txtSerial, won't Access attempt to Update the contents of Serial.Serial to match? How does Adding a Serial row on change (which looks like it should work a treat), prevent the Update of the existing Serial row?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

对你再特殊 2024-11-09 10:02:42

您还没有向我们展示 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 表示的串行行时,您可以使用相关子查询。

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;

我意识到你的问题更广泛。目前,这件作品是我可以理解的。但它符合你的要求吗?

更新:我使用了我的串行表版本。我创建了一个包含自动编号 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

对于您想要完成的任务来说,这是一个有用的起点吗?

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

You haven't shown us sample data for the Serial table. I'm curious whether it could look like this:

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

Whenever you need the Serial rows expressed with EffDt and TermDt, you can use a correlated subquery.

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;

I realize your question is broader. For now, this piece is one I can wrap my head around. But does it fit your requirements?

Update: I used my version of the Serial table. And I created a Master table with autonumber Id, text Serial, plus 2 other text fields, Other_field1 and Other_field2. Then created a simple continuous form bound to "SELECT Id, Serial, Other_field1, Other_field2 FROM Master Order By Id;".

Here is the form's code:

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

Is that a useful starting point for what you want to accomplish?

Update2: You said txtSerial is bound to Serial.Serial; but my approach binds txtSerial directly to a Serial field in the Master table ... which is what I thought you originally proposed. What is wrong with that approach now?

南冥有猫 2024-11-09 10:02:42

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

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

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

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

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

Actually, I do not see why you want to prevent the change to the text box? If I am reading this correct, what you are saying is if the text box changes, then you need to save the previous current data and record before changes?

If above is the case, then simply in the after update event of the serial text box, run an append query that takes the current record from the table and copies (appends it somewhere else).

While you have a bound form, the changes to controls on the form are NOT YET committed to the actual underlying table. If your code or a process or a some append query or VBA code looks at or makes a copy of that row from the table, all of the old values will still be intact.

So a bound form does not mean each change in a text box is written to the table, but in fact the whole record is ONLY written to the table when the record is saved, and that is why the forms before update event has a cancel that can prevent the record write from occurring until your rules etc are met.

So you just need one or two lines in the serial box after update event (that event does not fire unless changes are made to the text box) to copy the current record from the table which will have all of the old values, including the old serial number.

淡写薰衣草的香 2024-11-09 10:02:42

如果 Master 更改得不太频繁,也不是太大,则在保存时,将添加 whoChanged 记录和时间字段附加到整个 Master 记录中。然后,当您想查看 Serial 中的更改时,只需对 Serial 执行 GroupBy 即可看到时间和更改者。

这对于一个领域来说有点矫枉过正,但总的来说提供了一种记录更改的好方法。

If Master doesn't change too often and isn't too large, then when saving, append the entire Master record with a whoChanged record added and a timefield. Then when you want to see changes in Serial, just do a GroupBy on Serial and you can see the times and who changed it.

This is overkill for one field, but in general provides a good way of recording changes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文