Спасибо braX, kri sh KM и SunKnight за ваши комментарии выше.
Я разработал процесс, который соответствует моим собственным потребностям, но я кратко объясню свои рассуждения:
Ограничения других методов:
- Решение Allenbrowne (1) требует, чтобы пользователь использовал форму, а (2) - ключи быть AutoNumbers (мое должно быть строковым текстом)
- Решение Scottgem исключает использование форм. Я предлагаю своим пользователям форму для быстрого обновления полей, с которыми они обычно взаимодействуют, но в некоторых таблицах есть более 20 полей, и любая форма, которую я им дам (12+ таблиц с более чем 20 уникальными полями), будет сложнее, чем просто они взаимодействуют с самими таблицами (хотя, как правило, это осуждается, в этом случае не возникает проблем с отображением таблиц, пока у нас есть некоторое отслеживание).
- Сгенерированные вручную DataMacros негибкие - я не могу скопируйте и вставьте любую их часть в новую таблицу, чтобы быстро обновить поля, которые я заинтересован в отслеживании (добавить, удалить, изменить и т. д. c.). Эта база данных является новой, поэтому я ожидаю, что приоритеты и фокус изменятся по мере добавления новых полей с течением времени.
Решение:
В конце моей публикации вопрос, я упомянул, что я мог бы передать обновленные значения в функцию с дополнительными входами, и сделать так, чтобы функция выполняла внутреннее сравнение до / после, так что я так и сделал.
Ограничения:
DataMacros выдает ошибки, если вы не обрабатываете нулевые значения в выражении
DataMacros имеют ограничение в 255 символов
Из-за # 1 выше, мне пришлось обернуть входные данные в функции Nz (). Моя схема также включает подачу входных данных в функцию «after» в формате ключ / значение, для чего требуется подробное объявление. С ограничением в 255 символов я решил отслеживать только 6-10 полей на таблицу за раз (чтобы сделать больше, я просто повторяю процесс во втором DataMacro).
Public TableChangedName As String
Public TableChangedKey As String
Public SuppressLog As Boolean
Public Function SetupLogEvent(ByVal TableName As String, ByVal KeyName As String)
'This function reads the recordset as-is to build a 'before' dictionary
'SuppressLog is True when updating via user form
'The form will make its own submissions to the history log to specify which form did the update
If SuppressLog = False Then
Set BeforeFields = New Scripting.Dictionary
Set rs = CurrentDb.OpenRecordset("SELECT * FROM [" & TableName & "] WHERE KeyName='" & KeyName & "'")
Dim i As Long
Do While Not rs.EOF
rs.MoveLast
rs.MoveFirst
For i = 0 To rs.Fields.Count - 1
fName = rs.Fields(i).Name
fVal = rs.Fields(i).Value
BeforeFields.Add fName, CStr(Nz(fVal, ""))
Next i
rs.MoveNext
Loop
rs.Close
TableChangedName = TableName
TableChangedKey = KeyName
SetupLogEvent = True
End If
End Function
Public Function SubmitLogEvent(Optional ByVal Input1 As String = "None", Optional ByVal Input2 As String = "None", Optional ByVal Input3 As String = "None", Optional ByVal Input4 As String = "None", Optional ByVal Input5 As String = "None", Optional ByVal Input6 As String = "None", Optional ByVal Input7 As String = "None", Optional ByVal Input8 As String = "None", Optional ByVal Input9 As String = "None", Optional ByVal Input10 As String = "None", Optional ByVal Input11 As String = "None", Optional ByVal Input12 As String = "None")
'This function submits an entry to the _History table for each field that was changed, marking the change as "Manually updated" (i.e. no form used)
'SuppressLog is True when updating via user form
'The form will make its own submissions to the history log to specify which form did the update
If SuppressLog = False Then
Dim InputArray As Variant
InputArray = Array(Input1, Input2, Input3, Input4, Input5, Input6, Input7, Input8, Input9, Input10, Input11, Input12)
Set AfterFields = New Scripting.Dictionary
For i = LBound(InputArray) To UBound(InputArray) Step 2
If InputArray(i) = "None" Then
'End of used input fields
Exit For
Else
AfterFields.Add InputArray(i), InputArray(i + 1)
End If
Next i
DoCmd.SetWarnings False
For Each fName In AfterFields.Keys
If BeforeFields(fName) <> AfterFields(fName) Then
strSQL = "INSERT INTO _History ([TableModified],[KeyName],[Field],[From],[To],[User],[TimeStamp],[Method]) VALUES ('" & TableChangedName & "','" & TableChangedKey & "','" & fName & "','" & BeforeFields(fName) & "','" & AfterFields(fName) & "','" & Environ("username") & "','" & Now() & "','Manually updated')"
Debug.Print ("strSQL = """ & strSQL & """")
DoCmd.RunSQL strSQL
End If
Next fName
DoCmd.SetWarnings True
End If
SubmitLogEvent = True
End Function
С этими двумя функции выше, единственное, что мне нужно обновить для каждой таблицы - это выражения.
До SetLocalVar выражение:
= SetupLogEvent ("Table01", [KeyName])
После SetLocalVar выражение:
= SubmitLogEvent ('Field1', Nz ([Field1]), 'Field2', Nz ([Field2]), 'Field3', Nz ([Field3]), 'Field4', Nz ([Field4]))
Если новое поле добавляется в одну или несколько таблиц, которые необходимо отслеживать, я обновляю переменную FieldList и перезапустите следующую функцию:
Private Function PrintExpression()
'Enter the list of fields you want to track, separated by commas
FieldList = "Field_1,Field_2,Field_3,Field_4,Field_5"
exprString = "=SubmitLogEvent("
For Each Entry In Split(FieldList, ",")
exprString = exprString & "'" & Entry & "',Nz([" & Entry & "]),"
Next
exprString = RxReplace(exprString, ",$", ")")
If Len(exprString) > 255 Then
Call MsgBox("This result is > 255 characters (" & Len(exprString) & ") and will be rejected.", vbExclamation + vbOKOnly, "Input Too Long")
Else
Debug.Print ("=SetupLogEvent(""<<< Table Name >>>"",[KeyName])")
Debug.Print (exprString)
End If
End Function
Вывод:
= SetupLogEvent ("<<< Имя таблицы >>>", [KeyName])
= SubmitLogEvent ( 'Field_1', Nz ([Field_1]), 'Field_2', Nz ([Field_2]), 'Field_3', Nz ([Field_3]), 'Field_4', Nz ([Field_4]), 'Field_5 ', Nz ([Field_5]))
Я копирую / вставляю 2-е выражение в любой из таблиц, которые используют эти поля, и я готов. Если мне нужно отслеживать более 6-10 полей в таблице, я перезапускаю вышеупомянутую функцию со вторым FieldList и добавляю ее во второй DataMacro для таблицы. Промойте и повторите при необходимости.