1

我有一张Tbl如下表:

+----+------+----------+
| ID | Item | ItemDate |
+----+------+----------+
|  1 | xv   | 7/23     |
|  2 | drc  | 3/15     |
|  3 | fna  | 3/15     |
|  4 | fna  | 1/19     |
+----+------+----------+

用户请求了一个TblForm基于此表的表单,其中包含一个列,该列maxDate提供ItemDate每个Item. 表单必须允许用户编辑Tbl数据,所以我不能仅仅基于联接查询构建表单,因为 Access 不允许您编辑联接的结果。此外,表单必须可以基于maxDate列进行排序。

我构建了一个单独的maxDate聚合查询,然后添加了一个控件TblForm并将其 ControlSource 设置为:

=DLookUp("maxDate","maxDate","Item=" & [Item])

但是在生成的数据表中,我无法根据此列进行排序;我认为那是因为它不是TblForm's 记录源的一部分。所以我尝试构建一个包含 DLookUp 的查询:

select *,=DLookUp("maxDate","maxDate","Item=" & [Item]) as maxDateField from tbl

基于此查询的表单非常慢。

关于如何构建我正在寻找的东西的任何想法?

4

1 回答 1

1

这可以通过使用临时表来实现。

您需要两个相等的临时表,我们称它们为 tmpTbl1 和 tmpTbl2。

要刷新这些表,您需要创建两个查询:vwMaxDate1 和 vwMaxDate2。

您还可以创建两个查询作为报告的来源:vwTbl1 和 vwTbl2。您将在它们之间切换,因为您无法在通过表单打开时更新表格。

这是视图的内容:

' CreateTmpTable1
SELECT Tbl.Item, Max(Tbl.ItemDate) AS maxdate INTO TmpTable1
FROM Tbl
GROUP BY Tbl.Item;

' CreateTmpTable2
SELECT Tbl.Item, Max(Tbl.ItemDate) AS maxdate INTO TmpTable2
FROM Tbl
GROUP BY Tbl.Item;

' vwMaxDate1
INSERT INTO tmpTbl1 ( Item, maxdate )
SELECT Tbl.Item, Max(Tbl.ItemDate) AS maxdate
FROM Tbl
GROUP BY Tbl.Item;

' vwMaxDate2
INSERT INTO tmpTbl2 ( Item, maxdate )
SELECT Tbl.Item, Max(Tbl.ItemDate) AS maxdate
FROM Tbl
GROUP BY Tbl.Item;

' vwTbl1
SELECT Tbl.*, T.maxdate
FROM Tbl LEFT JOIN tmpTbl1 AS T ON Tbl.Item = T.Item;

' vwTbl2
SELECT Tbl.*, T.maxdate
FROM Tbl LEFT JOIN tmpTbl2 AS T ON Tbl.Item = T.Item;

现在,执行 CreateTmpTable1 和 CreateTmpTable2。您将不再需要这些查询。

在设计模式下输入两个临时表并将项目设置为主键。

然后,执行 vwMaxDate1 并根据视图 vwTbl1 设计您的表单。完成后,删除注册表的来源。我还将锁定 maxdate 字段的控件,因为这是一个计算字段,不应手动修改。

现在,在表单中输入以下代码,并将表单的“更新前”、“更新后”和“打开”事件更改为“[事件过程]”。

Option Compare Database
Option Explicit

Private dataChanged As Boolean      ' True whenever Item or ItemDate are changed
Private FirstTable As Boolean       ' If true the origin of the registry is vwTbl1

Private Sub Form_AfterUpdate()

    If dataChanged Then

        DoCmd.SetWarnings False
        Select Case FirstTable

            Case True:  ' vwTbl1 is open: Switch to vwTbl2
                DoCmd.RunSQL "DELETE * FROM TmpTbl2"    ' Delete actual data from tmp table 2
                DoCmd.OpenQuery "vwMaxDate2"            ' Create new data for tmp table 2
                Me.RecordSource = "vwTbl2"              ' Switch to vwTbl2
                FirstTable = False

            Case False: ' vwTbl2 is open: Switch to vwTbl1
                DoCmd.RunSQL "DELETE * FROM TmpTbl1"    ' Delete actual data from tmp table 1
                DoCmd.OpenQuery "vwMaxDate1"            ' Create new data for tmp table 1
                Me.RecordSource = "vwTbl1"              ' Switch to vwTbl1
                FirstTable = True

        End Select

    DoCmd.SetWarnings True
    End If

End Sub

Private Sub Form_BeforeUpdate(Cancel As Integer)

    ' Examine the Item and ItemDate controls to determine whether they've changed or not
    dataChanged = (Me.Item.OldValue <> Me.Item.Value Or Me.ItemDate.OldValue <> Me.ItemDate.Value)

End Sub

Private Sub Form_Open(Cancel As Integer)

    ' Initialize variables and form registry source

    FirstTable = True
    dataChanged = False

    DoCmd.SetWarnings False
    DoCmd.OpenQuery "vwMaxDate1"
    Me.RecordSource = "vwTbl1"
    DoCmd.SetWarnings True

End Sub

此查询对于查询数据和更新与 Item 和 ItemDate 不同的任何字段将是快速的。

如果您需要更新这些特定字段,您会注意到可变延迟,具体取决于 Tbl 表中的寄存器数量。

为了加速临时表的创建,强烈建议基于“Item”和“ItemDate”字段在 Tbl 表上创建索引。

如果您需要它在多用户环境中工作,那么您应该使用表记录来保存变量 FirstTable。这意味着您必须使用一些指令来查询和更新此表,例如

FirstTable = DLookup("FirstTable","CtrlTable","ID=1")

DoCmd.RunSQL "UPDATE CtrlTable SET FirstTable=True WHERE ID=1"

而不是简单地用“=”将 True 或 False 分配给 FirstTable。

就这样。希望对你有效。

问候,

于 2013-07-29T09:22:00.263 回答