Uzyskiwanie błędu 3340 Zapytanie '' jest uszkodzone podczas wykonywania zapytania DoCmd.RunSQL

83

Od czasu zainstalowania aktualizacji systemu Windows dla pakietu Office 2010 rozwiązującej KB 4484127 pojawia się błąd podczas wykonywania zapytań zawierających klauzulę WHERE.

Na przykład wykonanie tego zapytania:

DoCmd.RunSQL "update users set uname= 'bob' where usercode=1"

Wyniki tego błędu:

Numer błędu = 3340 Zapytanie '' jest uszkodzone

Ta aktualizacja jest nadal zainstalowana:

Zrzut ekranu przedstawiający aktualizację 448127 pakietu Microsoft Office 2010 z dodatkiem Service Pack 2

Jak mogę pomyślnie uruchamiać moje zapytania? Czy powinienem po prostu odinstalować tę aktualizację?

Zvi Redler
źródło

Odpowiedzi:

92

Podsumowanie

Jest to znany błąd spowodowany aktualizacjami pakietu Office wydanymi 12 listopada 2019 r. Błąd dotyczy wszystkich wersji Access obecnie obsługiwanych przez Microsoft (od Access 2010 do 365).

Ten błąd został naprawiony.

  • Jeśli używasz wersji pakietu Office C2R (Szybka instalacja), użyj opcji „Aktualizuj teraz” :
    • Access 2010 C2R: Naprawiono w kompilacji 7243.5000
    • Access 2013 C2R: Naprawiono w kompilacji 5197.1000
    • Access 2016 C2R: Naprawiono w kompilacji 12130.20390
    • Access 2019 (v1910): Naprawiono w kompilacji 12130.20390
    • Access 2019 (licencja zbiorowa): Naprawiono w kompilacji 10353.20037
    • Miesięczny kanał Office 365: Naprawiono w kompilacji 12130.20390
    • Półroczne Office 365: Naprawiono w kompilacji 11328.20480
    • Rozszerzone półroczne Office 365: Naprawiono w kompilacji 10730.20422
    • Cel półroczny Office 365: Naprawiono w kompilacji 11929.20494
  • Jeśli używasz wersji MSI pakietu Office, zainstaluj aktualizację zgodną z wersją pakietu Office. Wszystkie te poprawki zostały wydane w witrynie Microsoft Update, więc wystarczy zainstalować wszystkie oczekujące aktualizacje systemu Windows :

Przykład

Oto minimalny przykład repro:

  1. Utwórz nową bazę danych Access.
  2. Utwórz nową, pustą tabelę „Tabela 1” z domyślnym polem identyfikatora i polem Długa liczba całkowita „myint”.
  3. Wykonaj następujący kod w oknie natychmiastowym edytora VBA:

    CurrentDb.Execute "UPDATE Table1 SET myint = 1 WHERE myint = 1"

Oczekiwany wynik : Instrukcja zakończy się pomyślnie.

Rzeczywisty wynik po zainstalowaniu jednej z błędnych aktualizacji: pojawia się błąd 3340 w czasie wykonywania („Zapytanie” jest uszkodzone).


Powiązane linki:

Heinzi
źródło
9
Ten post wydaje się napotykać ten sam błąd przy użyciu 64-bitowego środowiska wykonawczego Access i OLEDB. Straszne rzeczy, sprawi to, że wiele aplikacji korzystających z Access do przechowywania danych będzie nieużytecznych.
Erik A
4
Właśnie sprawdziłem system z pakietem Office 2013 w wersji 32-bitowej i na tym konkretnym komputerze UUID dla aktualizacji to 90150000-006E-0409-0000-0000000FF1CE... to -0409-nie jest -0407-.
Gord Thompson,
4
Właśnie sprawdziłem inną maszynę w biurze, która ma pakiet Office 2013 64-bit, a także identyfikator UUID -006E-0409-. Oba komputery mają zainstalowany dodatek Service Pack 1 dla pakietu Microsoft Office 2013 (KB2850036).
Gord Thompson
4
W przypadku pakietu Office 2010 Pro Plus (SP2) musieliśmy użyć {90140000-0011-0000-0000-0000000FF1CE}skryptu wsadowego. Uwaga {9014...nie{9114..}
AdamsTips
2
Poprawiłem oficjalną aktualizację, aby rozwiązać problem, ale nadal pojawia się błąd. Czy ktoś jeszcze ma ten problem?
user218076,
33

Najprostsze rozwiązanie

Dla moich użytkowników czekanie prawie 10 miesięcy do 10 grudnia na wydanie poprawki od Microsoft nie wchodzi w grę. Odinstalowywanie nieuczciwej aktualizacji Microsoft na kilku rządowych stacjach roboczych jest również zablokowane.

Muszę zastosować obejście, ale nie do końca jestem podekscytowany tym, co Microsoft zasugerował - tworząc i zastępując zapytanie dla każdej tabeli.

Rozwiązaniem jest zastąpienie nazwy tabeli prostym (SELECT * FROM Table)zapytaniem bezpośrednio w UPDATEpoleceniu. Nie wymaga to tworzenia i zapisywania mnóstwa dodatkowych zapytań, tabel lub funkcji.

PRZYKŁAD:

Przed:

UPDATE Table1 SET Field1 = "x" WHERE (Field2=1);  

Po:

UPDATE (SELECT * FROM Table1) SET Field1 = "x" WHERE (Field2=1);  

Powinno to być o wiele łatwiejsze do wdrożenia w kilku bazach danych i aplikacjach (a później wycofaniu).

Joe Marinucci
źródło
20

To nie jest problem z aktualizacją systemu Windows, ale problem, który został wprowadzony w wersji Office Patch z listopada. Zmiana mająca na celu usunięcie luki w zabezpieczeniach powoduje, że niektóre uzasadnione zapytania są zgłaszane jako uszkodzone. Ponieważ zmiana była poprawką bezpieczeństwa, ma wpływ na WSZYSTKIE wersje pakietu Office, w tym 2010, 2013, 2016, 2019 i O365.

Błąd został naprawiony we wszystkich kanałach, ale czas dostarczenia zależy od tego, na którym kanale jesteś.

W przypadku wersji MSI 2010, 2013 i 2016 oraz licencji półrocznych O365, poprawka będzie dostępna we wtorek, 10 grudnia, 10 grudnia. W przypadku O365, kanału miesięcznego i osób mających dostęp do informacji poufnych zostanie to naprawione kiedy pojawi się październikowy widelec, obecnie planowany na 24 listopada.

W przypadku kanału półrocznego błąd został wprowadzony w 11328.20468, który został wydany 12 listopada, ale nie jest dostępny dla wszystkich naraz. Jeśli możesz, możesz wstrzymać się z aktualizacją do 10 grudnia.

Problem występuje w przypadku zapytań o aktualizację dla pojedynczej tabeli z określonymi kryteriami (więc nie należy wpływać na inne typy zapytań, ani na zapytania, które aktualizują wszystkie wiersze tabeli, ani zapytania, które aktualizuje zestaw wyników innego zapytania). Biorąc to pod uwagę, najprostszym obejściem w większości przypadków jest zmiana zapytania aktualizacyjnego, aby zaktualizować inne zapytanie, które wybiera wszystko z tabeli, zamiast aktualizować zapytanie bezpośrednio.

To znaczy, jeśli masz zapytanie takie jak:

UPDATE Table1 SET Table1.Field1 = "x" WHERE ([Table1].[Field2]=1);

Następnie utwórz nowe zapytanie (Query1) zdefiniowane jako:

Select * from Table1;

i zaktualizuj oryginalne zapytanie, aby:

UPDATE Query1 SET Query1.Field1 = "x" WHERE ([Query1].[Field2]=1);

Oficjalna strona: Błąd dostępu: „Zapytanie jest uszkodzone”

Gustav
źródło
13
Czy rzeczywiście mówisz z prostą miną, że przechodzimy do setek tysięcy wierszy kodu wdrożonych w wielu aplikacjach i naprawiamy wszystkie aktualizacje SQL, które w prosty sposób aktualizują rząd danych? Podejrzewam, że jeśli napiszesz nowe zapytanie dzisiaj i teraz, to takie obejście jest możliwe. Jednak w przypadku istniejącego kodu i aplikacji pomysł zmiany aktualizacji SQL nie jest oczywiście praktycznym podejściem do rozwiązywania problemów w jakikolwiek możliwy sposób.
Albert D. Kallal
5
@ AlbertD.Kallal, powinieneś wiedzieć z listy MVP, że po prostu odsyłam do wyjaśnienia źródła problemu. To, jak poradzić sobie z tym problemem, zależy od Ciebie i co może pasować do Twojego scenariusza. Opisana tutaj metoda jest tylko jedną z kilku.
Gustav
1
@ AlbertD.Kallal Czy zmiana nazw tabel i tworzenie QueryDefs ze starą nazwą tabeli to naprawić? (Przetestuję to i opublikuję skrypt, jeśli działa)
ComputerVersteher
Możesz to zrobić bez programowania, na przykład zmień nazwę tabeli „users” na „userst”, a następnie utwórz zapytanie o nazwie „users” - i wtedy będzie działać bez żadnego kanału programowania…
Zvi Redler,
9
@ AlbertD.Kallal: Dzielę twój ból - jeśli byłby to błąd mający wpływ na bibliotekę wykonawczą VC, nie sądzę, że stwardnienie rozsiane opóźniłoby poprawkę o miesiąc i sugeruje obejście „przepisania, rekompilacji i ponownego wdrożenia”. (Szczerze mówiąc, naprawili i szybko wydali problem VBA pod koniec sierpnia). Ale nie strzelajmy do posłańca - Gustav nie wydaje się pracownikiem stwardnienia rozsianego. Miejmy nadzieję, że ponownie rozważą i wydadzą łatkę wcześniej; w końcu wpływa to również na aplikacje napisane w innych językach, które akurat korzystają z silnika Access DB .
Heinzi
15

Aby tymczasowo rozwiązać ten problem, zależy od używanej wersji programu Access:
Access 2010 Odinstaluj aktualizację KB4484127
Access 2013 Odinstaluj aktualizację KB4484119
Access 2016 Odinstaluj aktualizację KB4484113
Access 2019 JEŚLI WYMAGANE (do potwierdzenia). Przejście z wersji 1808 (kompilacja 10352.20042) do wersji 1808 (kompilacja 10351.20054)
Office 365 ProPlus Przejście z wersji 1910 (kompilacja 12130.20344) na poprzednią kompilację, patrz https://support.microsoft.com/en-gb/help/2770432/ jak-przywrócić-do-wcześniejszej wersji-pakietu-2013-lub-pakietu-2016-clic

Dotacja
źródło
Odinstalowałem go, ale został ponownie zainstalowany przy następnym uruchomieniu systemu Windows. Jak zapobiec ponownej instalacji?
dsteele
5
@dsteele Jeśli wersja MSI i brak WSUS, użyj narzędzia do rozwiązywania problemów support.microsoft.com/en-us/help/3073930/... W CTR wyłącz aktualizacje w ustawieniach konta Office.
ComputerVersteher,
5

My i nasi klienci zmagaliśmy się z tym przez ostatnie dwa dni i ostatecznie napisaliśmy artykuł, aby szczegółowo omówić problem wraz z niektórymi rozwiązaniami: http://fmsinc.com/MicrosoftAccess/Errors/query_is_corrupt/

Zawiera nasze ustalenia, że ​​wpływa to na rozwiązania Access podczas uruchamiania zapytań o aktualizację na lokalnych tabelach, połączonych tabelach dostępu, a nawet połączonych tabelach SQL Server.

Wpływa także na rozwiązania inne niż Microsoft Access, korzystające z Access Database Engine (ACE) w celu łączenia się z bazami danych Access za pomocą ADO. Obejmuje to aplikacje Visual Studio (WinForm), aplikacje VB6, a nawet strony internetowe, które aktualizują bazy danych Access na komputerach, na których nigdy nie zainstalowano Access lub Office.

Ta awaria może nawet wpłynąć na aplikacje Microsoft korzystające z ACE, takie jak PowerBI, Power Query, SSMA itp. (Niepotwierdzone), i oczywiście inne programy, takie jak Excel, PowerPoint lub Word, używające VBA do modyfikowania baz danych Access.

Oprócz oczywistej dezinstalacji szkodliwych Aktualizacji Bezpieczeństwa, uwzględniamy również niektóre opcje, gdy nie można odinstalować z powodu uprawnień lub dystrybucji aplikacji Access do zewnętrznych klientów, których komputery są poza Twoją kontrolą. Obejmuje to zmianę wszystkich zapytań o aktualizację i dystrybucję aplikacji Access za pomocą Access 2007 (sprzedaż detaliczna lub środowisko wykonawcze), ponieważ aktualizacje zabezpieczeń nie mają wpływu na tę wersję.

LukeChung-FMS
źródło
4

Skorzystaj z następującego modułu, aby automatycznie wdrożyć obejście sugerowane przez Mikrofty (używając zapytania zamiast tabeli). W ramach ostrożności najpierw wykonaj kopię zapasową bazy danych.

Użyj, AddWorkaroundForCorruptedQueryIssue()aby dodać obejście i RemoveWorkaroundForCorruptedQueryIssue()usunąć je w dowolnym momencie.

Option Compare Database
Option Explicit

Private Const WorkaroundTableSuffix As String = "_Table"

Public Sub AddWorkaroundForCorruptedQueryIssue()
    On Error Resume Next

    With CurrentDb
        Dim tableDef As tableDef
        For Each tableDef In .tableDefs
            Dim isSystemTable As Boolean
            isSystemTable = tableDef.Attributes And dbSystemObject

            If Not EndsWith(tableDef.Name, WorkaroundTableSuffix) And Not isSystemTable Then
                Dim originalTableName As String
                originalTableName = tableDef.Name

                tableDef.Name = tableDef.Name & WorkaroundTableSuffix

                Call .CreateQueryDef(originalTableName, "select * from [" & tableDef.Name & "]")

                Debug.Print "OldTableName/NewQueryName" & vbTab & "[" & originalTableName & "]" & vbTab & _
                            "NewTableName" & vbTab & "[" & tableDef.Name & "]"
            End If
        Next
    End With
End Sub

Public Sub RemoveWorkaroundForCorruptedQueryIssue()
    On Error Resume Next

    With CurrentDb
        Dim tableDef As tableDef
        For Each tableDef In .tableDefs
            Dim isSystemTable As Boolean
            isSystemTable = tableDef.Attributes And dbSystemObject

            If EndsWith(tableDef.Name, WorkaroundTableSuffix) And Not isSystemTable Then
                Dim originalTableName As String
                originalTableName = Left(tableDef.Name, Len(tableDef.Name) - Len(WorkaroundTableSuffix))

                Dim workaroundTableName As String
                workaroundTableName = tableDef.Name

                Call .QueryDefs.Delete(originalTableName)
                tableDef.Name = originalTableName

                Debug.Print "OldTableName" & vbTab & "[" & workaroundTableName & "]" & vbTab & _
                            "NewTableName" & vbTab & "[" & tableDef.Name & "]" & vbTab & "(Query deleted)"
            End If
        Next
    End With
End Sub

'From https://excelrevisited.blogspot.com/2012/06/endswith.html
Private Function EndsWith(str As String, ending As String) As Boolean
     Dim endingLen As Integer
     endingLen = Len(ending)
     EndsWith = (Right(Trim(UCase(str)), endingLen) = UCase(ending))
End Function

Najnowszy kod można znaleźć w moim repozytorium GitHub .

AddWorkaroundForCorruptedQueryIssue()doda sufiks _Tabledo wszystkich tabel niesystemowych, np. IceCreamsnazwa tabeli zostanie zmieniona na IceCreams_Table.

Stworzy również nowe zapytanie przy użyciu oryginalnej nazwy tabeli, która wybierze wszystkie kolumny tabeli o zmienionej nazwie. W naszym przykładzie zapytanie zostanie nazwane IceCreamsi wykona SQL select * from [IceCreams_Table].

RemoveWorkaroundForCorruptedQueryIssue() robi odwrotne działania.

Przetestowałem to na wszystkich rodzajach tabel, w tym na zewnętrznych tabelach innych niż MDB (takich jak SQL Server). Należy jednak pamiętać, że użycie zapytania zamiast tabeli może prowadzić do wykonania niezoptymalizowanych zapytań w bazie danych zaplecza w określonych przypadkach, szczególnie jeśli oryginalne zapytania, które korzystały z tabel, są niskiej jakości lub bardzo złożone.

(I oczywiście, w zależności od stylu kodowania, możliwe jest również uszkodzenie aplikacji. Po sprawdzeniu, czy poprawka na ogół działa dla Ciebie, nigdy nie jest złym pomysłem, aby wyeksportować wszystkie obiekty jako tekst i użyć funkcji zastępowania znalezienia magia, aby upewnić się, że wszelkie wystąpienia użycia nazw tabel będą uruchamiane względem zapytań, a nie tabel).

W moim przypadku ta poprawka działa w dużej mierze bez żadnych skutków ubocznych, po prostu musiałem ręcznie zmienić nazwę z USysRibbons_Tablepowrotem na USysRibbons, ponieważ nie zaznaczyłem jej jako tabeli systemowej, kiedy ją tworzyłem w przeszłości.

lauxjpn
źródło
Podoba mi się, że określasz tabelę systemową TableDef.Attributesi kopiujesz ją do mojej odpowiedzi;) funkcja cofania jest dobrym pomysłem (ale stara i nowa nazwa powinna być przechowywana w tabeli, ponieważ zależy od braku tabel z sufiksem przed zmianą nazwy). Niektóre inne części są wadliwe (np. Tabele mogą kończyć się sufiksem lub nowa nazwa jest już w użyciu lub On Error Resume Nextbez późniejszych błędów obsługi). Czy znasz RubberduckVBA ? Ten dodatek może sprawdzać twój kod i zawiera fajne sugestie dotyczące ulepszeń, oprócz wszystkich innych funkcji.
ComputerVersteher,
I powinieneś wskazać błędy, które może powodować nasze podejście (patrz komentarze @Erics do mojej odpowiedzi)
ComputerVersteher
Ach, nie widziałem, że była już tutaj podobna odpowiedź, więc dziękuję za recenzję! Sufiks jest definiowany jako własna stała, więc można go łatwo zmienić w przypadku, gdy zdefiniowano już istniejący obiekt, który już korzysta z sufiksu. W przeciwnym razie skrypt działa tak, jak jest, ale każdy powinien zachęcać go do modyfikowania go do swoich indywidualnych potrzeb. Skrypt został przetestowany na dość dużych projektach (ponad 400 tabel), w tym tabelach zewnętrznych / połączonych z różnymi źródłami zewnętrznych baz danych. Nie wiedziałem o Rubberduck (tylko o MZ-Tools). Na pewno je sprawdzę!
lauxjpn
3

Dla tych, którzy chcą zautomatyzować ten proces za pomocą PowerShell , oto kilka linków, które mogą być pomocne:

Wykryj i usuń obraźliwe aktualizacje

Dostępny jest tutaj skrypt PowerShell https://www.arcath.net/2017/09/office-update-remover, który wyszukuje w rejestrze określoną aktualizację pakietu Office (przekazaną jako numer KB) i usuwa ją za pomocą wywołania msiexec.exe. Ten skrypt analizuje oba identyfikatory GUID z kluczy rejestru, aby zbudować polecenie usunięcia odpowiedniej aktualizacji.

Jedną zmianą, którą zasugerowałbym, byłoby użycie metody /REBOOT=REALLYSUPPRESSopisanej w Jak odinstalować KB4011626 i innych aktualizacji pakietu Office (dodatkowe informacje: https://docs.microsoft.com/en-us/windows/win32/msi/uninstalling-patches ). Linia poleceń, którą budujesz, wygląda następująco:

msiexec /i {90160000-0011-0000-0000-0000000FF1CE} MSIPATCHREMOVE={9894BF35-19C1-4C89-A683-D40E94D08C77} /qn REBOOT=REALLYSUPPRESS

Polecenie uruchomienia skryptu wyglądałoby mniej więcej tak:

OfficeUpdateRemover.ps1 -kb 4484127

Zapobiegaj instalowaniu aktualizacji

Zalecanym podejściem wydaje się tutaj ukrywanie aktualizacji . Oczywiście można to zrobić ręcznie, ale istnieją pewne skrypty PowerShell, które mogą pomóc w automatyzacji. Ten link: https://www.maketecheasier.com/hide-updates-in-windows-10/ szczegółowo opisuje ten proces, ale streszczę go tutaj.

  1. Zainstaluj moduł Windows PowerShell .
  2. Użyj następującego polecenia, aby ukryć aktualizację według numeru KB:

    Hide-WUUpdate -KBArticleID KB4484127

Mam nadzieję, że będzie to pomocne dla kogoś innego.

AdamsTips
źródło
3

Skrypt VBA dla obejścia MS:

Zaleca się usunięcie błędnej aktualizacji, jeśli to możliwe (jeśli nie, wypróbuj mój kod), przynajmniej dla wersji MSI. Zobacz odpowiedź https://stackoverflow.com/a/58833831/9439330 .

W przypadku wersji CTR (kliknij, aby uruchomić) musisz usunąć wszystkie listopadowe aktualizacje pakietu Office, co może powodować poważne problemy z bezpieczeństwem (nie jestem pewien, czy zostaną usunięte krytyczne poprawki).

Z komentarzy @ Erica:

  • Jeśli używasz Table.Tablenamedo wiązania formularzy, stają się one niezwiązane, ponieważ poprzednia nazwa tabeli jest teraz nazwą zapytania !.
  • OpenRecordSet(FormerTableNowAQuery, dbOpenTable) zawiedzie (ponieważ jest to teraz zapytanie, a nie tabela)

Uwaga! Wystarczy szybko przetestować w stosunku do Northwind.accdb na Office 2013 x86 CTR Brak gwarancji!

Private Sub RenameTablesAndCreateQueryDefs()
With CurrentDb
    Dim tdf As DAO.TableDef
    For Each tdf In .TableDefs

        Dim oldName As String
        oldName = tdf.Name

        If Not (tdf.Attributes And dbSystemObject) Then 'credit to @lauxjpn for better check for system-tables
            Dim AllFields As String
            AllFields = vbNullString

            Dim fld As DAO.Field

            For Each fld In tdf.Fields
                AllFields = AllFields & "[" & fld.Name & "], "
            Next fld

            AllFields = Left(AllFields, Len(AllFields) - 2)
            Dim newName As String
            newName = oldName

            On Error Resume Next
            Do
                Err.Clear
                newName = newName & "_"
                tdf.Name = newName
            Loop While Err.Number = 3012
            On Error GoTo 0

            Dim qdf As DAO.QueryDef

            Set qdf = .CreateQueryDef(oldName)
            qdf.SQL = "SELECT " & AllFields & " FROM [" & newName & "]"
        End If
    Next
    .TableDefs.Refresh

End With
End Sub

Dla testów:

Private Sub TestError()
With CurrentDb
    .Execute "Update customers Set City = 'a' Where 1=1", dbFailOnError 'works

    .Execute "Update customers_ Set City = 'b' Where 1=1", dbFailOnError 'fails
End With
End Sub
ComputerVersteher
źródło
4
Zauważ, że to obejście zrujnuje podformularze powiązane z tabelami (będzie musiało zostać powiązane z zapytaniami) i kodem działającym z tabelami o ustalonej nazwie tabeli. Używaj z dużą ostrożnością, szanse na to, że to naprawia tylko jeden błąd, aby utworzyć dwa nowe w zależności od tego, co robi twoja aplikacja.
Erik A
@ErikA Oczywiście tylko obejście, ale mogę powiązać Inventory to reorder Subform for Homedo Inventorystołu w Homeformie, bez problemów. Nawet nie zaleca się łączenia formularzy z zapytaniami zamiast z tabelami (czy nie wiąże się z tabelami jak Select * From table?).
ComputerVersteher,
2
Jeśli powiążę podformularz z tabelą, zazwyczaj robię to za pomocą Table.TableNamenotacji. Jeśli to zrobisz SELECT * FROM TableName, to oczywiście w porządku. Ale jeśli użyjesz Table.TableName, podformularz stanie się niezwiązany, jeśli zmienisz nazwę tabeli.
Erik A
@ErikA: To prawda. Jakieś korzyści z tego?
ComputerVersteher,
3
O ile mi wiadomo, poza tym, że jest krótszy. Istnieje jednak znaczna zaleta TableDefs!MyTableName.OpenRecordset(dbOpenTable)(obsługa wyszukiwania indeksów), z której również zwykle korzystam i również spowoduje błędy w twoim podejściu
Erik A
2

Wymieniłem currentDb.Executei Docmd.RunSQLz funkcji pomocnika. Może to wstępnie przetworzyć i zmienić instrukcję SQL, jeśli jakakolwiek instrukcja aktualizacji zawiera tylko jedną tabelę. Mam już dualtabelę (pojedynczy wiersz, jedna kolumna), więc wybrałem opcję fakeTable.

Uwaga : nie spowoduje to zmiany obiektów zapytania. Pomoże to jedynie w wykonywaniu SQL przez 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.

To tylko koncepcja (If it's a single table update modify the sql before execution). Dostosuj go do swoich potrzeb. Ta metoda nie tworzy zapytań zastępczych dla każdej tabeli (co może być najłatwiejszym sposobem, ale ma swoje wady, np. Problemy z wydajnością)

+ Punkty: Możesz nadal używać tego pomocnika nawet po tym, jak MS naprawi błąd, nic to nie zmieni. W przypadku, gdy przyszłość przyniesie kolejny problem, jesteś gotowy na pre-processSQL w jednym miejscu. Nie zdecydowałem się na metodę odinstalowywania aktualizacji , ponieważ wymaga to dostępu administratora + zajmie to zbyt długo, aby wszyscy mieli dostęp do poprawnej wersji + nawet jeśli odinstalujesz, zasady grupy niektórych użytkowników końcowych ponownie instalują najnowszą aktualizację. Wróciłeś do tego samego problemu.

Jeśli masz dostęp do kodu źródłowego use this methodi jesteś w 100% pewien, że żaden użytkownik nie ma problemu.

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

Teraz tylko CTRL+F

Wyszukiwania i zamiany docmd.RunSQLzhelper.Execute

Wyszukiwania i zamiany [currentdb|dbengine|or your dbobject].executezhelper.execute

baw się dobrze!

Krish
źródło
0

Ok, będę tu również dzwonił, ponieważ pomimo tego, że ten błąd został naprawiony, ta poprawka musi się jeszcze wypełnić w różnych przedsiębiorstwach, w których użytkownicy końcowi mogą nie być w stanie zaktualizować (np. Mój pracodawca ...)

Oto moje obejście DoCmd.RunSQL "UPDATE users SET uname= 'bob' WHERE usercode=1". Po prostu skomentuj obraźliwe zapytanie i wpisz poniższy kod.

    'DoCmd.RunSQL "UPDATE users SET uname= 'bob' WHERE usercode=1"
    Dim rst As DAO.Recordset
    Set rst = CurrentDb.OpenRecordset("users")
    rst.MoveLast
    rst.MoveFirst
    rst.FindFirst "[usercode] = 1" 'note: if field is text, use "[usercode] = '1'"
    rst.Edit
    rst![uname] = "bob"
    rst.Update
    rst.Close
    Set rst = Nothing

Nie mogę powiedzieć, że jest ładna, ale wykonuje zadanie.

Chaosbydesign
źródło