ฉันแทนที่currentDb.Execute
และDocmd.RunSQL
ด้วยฟังก์ชันตัวช่วย ที่สามารถประมวลผลล่วงหน้าและเปลี่ยนแปลงคำสั่ง SQL หากคำสั่งการปรับปรุงใด ๆ มีเพียงหนึ่งตาราง ฉันมีตารางdual
(แถวเดี่ยวคอลัมน์เดียว) อยู่แล้วดังนั้นฉันจึงไปกับตัวเลือก fakeTable
หมายเหตุ : สิ่งนี้จะไม่เปลี่ยนวัตถุแบบสอบถามของคุณ มันจะช่วยให้การประมวลผล SQL ผ่าน VBA เท่านั้นIf you would like to change your query objects, use FnQueryReplaceSingleTableUpdateStatements and update your sql in each of your querydefs. Shouldn't be a problem either.
(If it's a single table update modify the sql before execution)
นี่เป็นเพียงแนวคิด ปรับมันตามความต้องการของคุณ วิธีนี้ไม่ได้สร้างคิวรีการแทนที่สำหรับแต่ละตาราง (ซึ่งอาจเป็นวิธีที่ง่ายที่สุด แต่มีข้อเสียของตัวเองเช่นปัญหาประสิทธิภาพ)
+ คะแนน:
คุณสามารถใช้ตัวช่วยนี้ต่อไปได้แม้หลังจากที่ MS แก้ไขข้อผิดพลาดแล้วมันจะไม่เปลี่ยนแปลงอะไรเลย ในกรณีที่อนาคตนำปัญหาอื่นมาคุณพร้อมpre-process
SQL ของคุณในที่เดียว ฉันไม่ได้ไปถอนการติดตั้งวิธีการอัปเดตเนื่องจากต้องมีสิทธิ์การเข้าถึงระดับผู้ดูแลระบบ + จะใช้เวลานานเกินไปในการทำให้ทุกคนได้รับเวอร์ชันที่ถูกต้อง + แม้ว่าคุณจะถอนการติดตั้งก็ตามนโยบายกลุ่มของผู้ใช้ปลายทาง คุณกลับไปที่ปัญหาเดียวกัน
หากคุณสามารถเข้าถึงซอร์สโค้ดuse this method
และคุณมั่นใจได้ 100% ว่าผู้ใช้ปลายทางไม่มีปัญหา
Public Function Execute(Query As String, Optional Options As Variant)
'Direct replacement for currentDb.Execute
If IsBlank(Query) Then Exit Function
'invalid db options remove
If Not IsMissing(Options) Then
If (Options = True) Then
'DoCmd RunSql query,True ' True should fail so transactions can be reverted
'We are only doing this so DoCmd.RunSQL query, true can be directly replaced by helper.Execute query, true.
Options = dbFailOnError
End If
End If
'Preprocessing the sql command to remove single table updates
Query = FnQueryReplaceSingleTableUpdateStatements(Query)
'Execute the command
If ((Not IsMissing(Options)) And (CLng(Options) > 0)) Then
currentDb.Execute Query, Options
Else
currentDb.Execute Query
End If
End Function
Public Function FnQueryReplaceSingleTableUpdateStatements(Query As String) As String
' ON November 2019 Microsoft released a buggy security update that affected single table updates.
'/programming/58832269/getting-error-3340-query-is-corrupt-while-executing-queries-docmd-runsql
Dim singleTableUpdate As String
Dim tableName As String
Const updateWord As String = "update"
Const setWord As String = "set"
If IsBlank(Query) Then Exit Function
'Find the update statement between UPDATE ... SET
singleTableUpdate = FnQueryContainsSingleTableUpdate(Query)
'do we have any match? if any match found, that needs to be preprocessed
If Not (IsBlank(singleTableUpdate)) Then
'Remove UPDATe keyword
If (VBA.Left(singleTableUpdate, Len(updateWord)) = updateWord) Then
tableName = VBA.Right(singleTableUpdate, Len(singleTableUpdate) - Len(updateWord))
End If
'Remove SET keyword
If (VBA.Right(tableName, Len(setWord)) = setWord) Then
tableName = VBA.Left(tableName, Len(tableName) - Len(setWord))
End If
'Decide which method you want to go for. SingleRow table or Select?
'I'm going with a fake/dual table.
'If you are going with update (select * from T) as T, make sure table aliases are correctly assigned.
tableName = gDll.sFormat("UPDATE {0},{1} SET ", tableName, ModTableNames.FakeTableName)
'replace the query with the new statement
Query = vba.Replace(Query, singleTableUpdate, tableName, compare:=vbDatabaseCompare, Count:=1)
End If
FnQueryReplaceSingleTableUpdateStatements = Query
End Function
Public Function FnQueryContainsSingleTableUpdate(Query As String) As String
'Returns the update ... SET statment if it contains only one table.
FnQueryContainsSingleTableUpdate = ""
If IsBlank(Query) Then Exit Function
Dim pattern As String
Dim firstMatch As String
'Get the pattern from your settings repository or hardcode it.
pattern = "(update)+(\w|\s(?!join))*set"
FnQueryContainsSingleTableUpdate = FN_REGEX_GET_FIRST_MATCH(Query, pattern, isGlobal:=True, isMultiline:=True, doIgnoreCase:=True)
End Function
Public Function FN_REGEX_GET_FIRST_MATCH(iText As String, iPattern As String, Optional isGlobal As Boolean = True, Optional isMultiline As Boolean = True, Optional doIgnoreCase As Boolean = True) As String
'Returns first match or ""
If IsBlank(iText) Then Exit Function
If IsBlank(iPattern) Then Exit Function
Dim objRegex As Object
Dim allMatches As Variant
Dim I As Long
FN_REGEX_GET_FIRST_MATCH = ""
On Error GoTo FN_REGEX_GET_FIRST_MATCH_Error
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Multiline = isMultiline
.Global = isGlobal
.IgnoreCase = doIgnoreCase
.pattern = iPattern
If .test(iText) Then
Set allMatches = .Execute(iText)
If allMatches.Count > 0 Then
FN_REGEX_GET_FIRST_MATCH = allMatches.item(0)
End If
End If
End With
Set objRegex = Nothing
On Error GoTo 0
Exit Function
FN_REGEX_GET_FIRST_MATCH_Error:
FN_REGEX_GET_FIRST_MATCH = ""
End Function
ตอนนี้แค่CTRL+F
ค้นหาและแทนที่docmd.RunSQL
ด้วยhelper.Execute
ค้นหาและแทนที่[currentdb|dbengine|or your dbobject].execute
ด้วยhelper.execute
มีความสุข!