How to build an editable form with data equivalent to a join?

≯℡__Kan透↙ 提交于 2021-02-19 07:47:26

问题


I have a table Tbl as follows:

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

A user has requested a form TblForm based on this table that includes a column maxDate that gives the most recent ItemDate for each Item. The form must allow the user to edit Tbl data, so I can't just build a form based on a join query, as Access doesn't allow you to edit the results of a join. In addition, the form must be sortable based on the maxDate column.

I built a separate maxDate aggregate query, then added a control to TblForm and set its ControlSource as:

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

But in the resulting datasheet, I can't sort based on this column; I assume that's because it's not part of TblForm's record source. So I tried building a query that includes the DLookUp:

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

The form based on this query is extremely slow.

Any ideas on how I can build what I'm looking for?


回答1:


This can be achieved by using temporary tables.

You need two equal temp tables, let's call them tmpTbl1 and tmpTbl2.

To refresh these tables, you create two queries: vwMaxDate1 and vwMaxDate2.

You also create two queries as origins for your report: vwTbl1 and vwTbl2. You'll switch between them, because you cannot update a table while open by a form.

This is the content of the views:

' 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;

Now, execute CreateTmpTable1 and CreateTmpTable2. You wont need these queries again.

Enter both temp tables in design mode and set Item as the Primary Key.

Then, execute vwMaxDate1 and design your form based on the view vwTbl1. When you're finished, delete the origin of the registry. I'd also lock the control for the maxdate field, since this is a calculated field and shouldn't be manually modified.

Now, enter the following code into the form and change the "Before update", "After update" and "Open" events of the form to "[Event procedure]".

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

This query will be fast for querying data and for update of any fields different from Item and ItemDate.

If you need to update these particular fields, you'll notice a variable delay, depending on the number of registers in your Tbl table.

To accelerate the creation of the temporary table, it would be strongly advisable to create an index on your Tbl table based on both "Item" and "ItemDate" fields.

If you need this to work in a multiuser environment, then you should use a table record to hold the variable FirstTable. This means you have to query and update this table with some instructions like

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

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

instead of simply assigning True or False to FirstTable with "=".

And that's all. Hope it works for you.

Regards,



来源:https://stackoverflow.com/questions/17823194/how-to-build-an-editable-form-with-data-equivalent-to-a-join

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!