web-dev-qa-db-fra.com

Obtenir l'erreur 3340 La requête '' est corrompue lors de l'exécution des requêtes DoCmd.RunSQL

Depuis l'installation de la mise à jour Windows pour Office 2010 résolvant KB 4484127 j'obtiens une erreur lors de l'exécution des requêtes contenant une clause WHERE.

Par exemple, exécuter cette requête:

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

Résultats dans cette erreur:

Numéro d'erreur = 3340 La requête '' est corrompue

mise à jour en question est actuellement encore installé:

Screenshot showing Microsoft Office 2010 Service Pack 2 update 448127

Comment puis-je exécuter avec succès mes requêtes? Dois-je simplement désinstaller cette mise à jour?

83
Zvi Redler

Résumé

Il s'agit d'un bogue conn provoqué par les mises à jour Office publiées le 12 novembre 2019. Le bogue affecte toutes les versions d'Access actuellement prises en charge par Microsoft (d'Access 2010 à 365).

Ce bug a été corrigé.

  • Si vous utilisez une version C2R (Click-to-Run) d'Office, tilisez "Mettre à jour maintenant" :
    • Access 2010 C2R: corrigé dans la version 7243.5000
    • Access 2013 C2R: corrigé dans la version 5197.1000
    • Access 2016 C2R: corrigé dans la version 12130.20390
    • Access 2019 (v1910): corrigé dans la version 12130.20390
    • Access 2019 (licence en volume): corrigé dans la version 10353.20037
    • Canal mensuel Office 365: corrigé dans la version 12130.20390
    • Office 365 semi-annuel: corrigé dans la version 11328.20480
    • Office 365 semi-annuel étendu: corrigé dans la version 10730.20422
    • Office 365 semi-annuel ciblé: corrigé dans la version 11929.20494
  • Si vous utilisez une version MSI d'Office, installez la mise à jour correspondant à votre version d'Office. Tous ces correctifs ont été publiés sur Microsoft Update, donc installation de toutes les mises à jour Windows en attente devrait suffire:

Exemple

Voici un exemple de repro minimal:

  1. Créez une nouvelle base de données Access.
  2. Créez une nouvelle table vide "Table1" avec le champ ID par défaut et un champ entier long "myint".
  3. Exécutez le code suivant dans la fenêtre immédiate de l'éditeur VBA:

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

Résultat attendu : l'instruction se termine avec succès.

Résultat réel avec l'une des mises à jour de bogue installée: Une erreur d'exécution 3340 se produit ("La requête" est corrompue ").


Liens connexes:

92
Heinzi

Solution la plus simple

Pour mes utilisateurs, attendre près d'un mois jusqu'au 10 décembre pour une version de correctif de Microsoft n'est pas une option. La désinstallation de la mise à jour Microsoft incriminée n'est pas non plus effectuée sur plusieurs postes de travail verrouillés du gouvernement.

Je dois appliquer une solution de contournement, mais je ne suis pas exactement ravi de ce que Microsoft a suggéré - créer et remplacer une requête pour chaque table.

La solution consiste à remplacer le nom de la table par un simple (SELECT * FROM Table) requête directement dans la commande UPDATE. Cela ne nécessite pas de créer et d'enregistrer une tonne de requêtes, de tables ou de fonctions supplémentaires.

EXEMPLE:

Avant:

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

Après:

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

Cela devrait être beaucoup plus facile à implémenter sur plusieurs bases de données et applications (et restauration ultérieure).

33
Joe Marinucci

Ce n'est pas un problème de mise à jour Windows, mais un problème qui a été introduit avec la version de novembre du Patch Tuesday d'Office. Une modification apportée pour corriger une vulnérabilité de sécurité entraîne la déclaration de certaines requêtes légitimes comme corrompues. Étant donné que le changement était un correctif de sécurité, il affecte TOUTES les versions d'Office, y compris 2010, 2013, 2016, 2019 et O365.

Le bug a été corrigé dans tous les canaux, mais le moment de la livraison dépendra du canal sur lequel vous vous trouvez.

Pour les versions 2010, 2013 et 2016 de MSI et de licence en volume 2019, et le canal semi-annuel O365, le correctif sera dans la version du mardi de décembre, le 10 décembre. Pour O365, le canal mensuel et les initiés, cela sera corrigé lorsque la fourchette d'octobre sera publiée, actuellement prévue pour le 24 novembre.

Pour la chaîne semi-annuelle, le bogue a été introduit en 11328.20468, qui a été publié le 12 novembre, mais ne se diffuse pas à tout le monde en même temps. Si vous le pouvez, vous voudrez peut-être retarder la mise à jour jusqu'au 10 décembre.

Le problème se produit pour les requêtes de mise à jour sur une seule table avec des critères spécifiés (afin que les autres types de requêtes ne soient pas affectés, ni aucune requête qui met à jour toutes les lignes d'une table, ni une requête qui met à jour le jeu de résultats d'une autre requête). Dans ce cas, la solution de contournement la plus simple consiste dans la plupart des cas à modifier la requête de mise à jour pour mettre à jour une autre requête qui sélectionne tout dans la table, plutôt que de mettre à jour la requête directement.

C'est-à-dire, si vous avez une requête comme:

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

Ensuite, créez une nouvelle requête (Query1) définie comme:

Select * from Table1;

et mettez à jour votre requête d'origine pour:

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

Page officielle: Erreur d'accès: "La requête est corrompue"

20
Gustav

La résolution temporaire de ce problème dépend de la version d'Access utilisée:
Access 2010 Uninstall update KB4484127
Access 2013 Désinstaller la mise à jour KB4484119
Access 2016 Désinstaller la mise à jour KB4484113
Accéder à 2019 SI NÉCESSAIRE (à confirmer). Rétrogradation de la version 1808 (build 10352.20042) vers la version 1808 (build 10351.20054)
Rétrogradation d'Office 365 ProPlus de la version 1910 (build 12130.20344) vers une build précédente, voir https://support.Microsoft.com/en-gb/help/2770432/how-to-revert- vers-une-version-antérieure-de-office-2013-ou-office-2016-clic

15
Grant

Nous et nos clients avons eu du mal avec cela au cours des deux derniers jours et avons finalement écrit un document pour discuter du problème en détail avec quelques solutions: http://fmsinc.com/MicrosoftAccess/Errors/query_is_corrupt/

Il inclut nos constatations selon lesquelles il affecte les solutions Access lors de l'exécution de requêtes de mise à jour sur des tables locales, des tables Access liées et même des tables SQL Server liées.

Il affecte également les solutions non Microsoft Access utilisant le moteur de base de données Access (ACE) pour se connecter aux bases de données Access à l'aide d'ADO. Cela inclut les applications Visual Studio (WinForm), les applications VB6 et même les sites Web qui mettent à jour les bases de données Access sur des ordinateurs sur lesquels Access ou Office n'ont jamais été installés.

Ce plantage peut même avoir un impact sur les applications Microsoft qui utilisent ACE telles que PowerBI, Power Query, SSMA, etc. (non confirmé), et bien sûr, d'autres programmes tels qu'Excel, PowerPoint ou Word utilisant VBA pour modifier les bases de données Access.

En plus de la désinstallation évidente des mises à jour de sécurité incriminées, nous incluons également certaines options lorsqu'il n'est pas possible de désinstaller en raison d'autorisations ou de la distribution d'applications Access à des clients externes dont les PC sont hors de votre contrôle. Cela inclut la modification de toutes les requêtes de mise à jour et la distribution des applications Access à l'aide d'Access 2007 (vente au détail ou exécution), car cette version n'est pas affectée par les mises à jour de sécurité.

5
LukeChung-FMS

Utilisez le module suivant pour implémenter automatiquement la solution de contournement suggérée par Microsofts (en utilisant une requête au lieu d'une table). Par mesure de précaution, sauvegardez d'abord votre base de données.

Utilisez AddWorkaroundForCorruptedQueryIssue() pour ajouter la solution de contournement et RemoveWorkaroundForCorruptedQueryIssue() pour la supprimer à tout moment.

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

Vous pouvez trouver le dernier code sur mon référentiel GitHub .

AddWorkaroundForCorruptedQueryIssue() ajoutera le suffixe _Table à toutes les tables non système, par ex. la table IceCreams serait renommée en IceCreams_Table.

Il créera également une nouvelle requête en utilisant le nom de la table d'origine, qui sélectionnera toutes les colonnes de la table renommée. Dans notre exemple, la requête serait nommée IceCreams et exécuterait le SQL select * from [IceCreams_Table].

RemoveWorkaroundForCorruptedQueryIssue() effectue les actions inverses.

J'ai testé cela avec toutes sortes de tables, y compris des tables externes non MDB (comme SQL Server). Mais sachez que l'utilisation d'une requête au lieu d'une table peut entraîner l'exécution de requêtes non optimisées sur une base de données backend dans des cas spécifiques, surtout si vos requêtes d'origine qui ont utilisé les tables sont de mauvaise qualité ou très complexes.

(Et bien sûr, selon votre style de codage, il est également possible de casser des choses dans votre application. Donc, après avoir vérifié que le correctif fonctionne généralement pour vous, ce n'est jamais une mauvaise idée d'exporter tous vos objets sous forme de texte et d'utiliser certains find replace magique pour garantir que toutes les occurrences de noms de table seront exécutées sur les requêtes et non sur les tables.)

Dans mon cas, ce correctif fonctionne en grande partie sans aucun effet secondaire, je devais juste renommer manuellement USysRibbons_Table En USysRibbons, car je ne l'avais pas marqué comme table système lorsque je l'ai créé dans le passé.

4
lauxjpn

Pour ceux qui cherchent à automatiser ce processus via PowerShell , voici quelques-uns liens que j'ai trouvés qui peuvent être utiles:

Détecter et supprimer les mises à jour incriminées

Il existe un script PowerShell disponible ici https://www.arcath.net/2017/09/office-update-remover qui recherche dans le registre une mise à jour Office spécifique (transmis sous forme de numéro de ko) et le supprime à l'aide d'un appel à msiexec.exe. Ce script analyse les deux GUID des clés de registre pour générer la commande de suppression de la mise à jour appropriée.

Un changement que je suggérerais serait d'utiliser le /REBOOT=REALLYSUPPRESS comme décrit dans Comment désinstaller KB4011626 et autres mises à jour Office (Référence supplémentaire: https://docs.Microsoft.com/en-us/windows/win32/msi/uninstalling- correctifs ). La ligne de commande que vous créez ressemble à ceci:

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

La commande pour exécuter le script ressemblerait à ceci:

OfficeUpdateRemover.ps1 -kb 4484127

Empêcher l'installation des mises à jour

L'approche recommandée ici semble être de masquer la mise à jour . Évidemment, cela peut être fait manuellement, mais certains scripts PowerShell peuvent aider à l'automatisation. Ce lien: https://www.maketecheasier.com/hide-updates-in-windows-10/ décrit le processus en détail, mais je vais le résumer ici.

  1. Installez le module Windows Update PowerShell .
  2. Utilisez la commande suivante pour masquer une mise à jour par numéro de Ko:

    Hide-WUUpdate -KBArticleID KB4484127

J'espère que cela sera utile à quelqu'un d'autre.

3
AdamsTips

Script VBA pour MS-Contournement:

Il est recommandé de supprimer la mise à jour du buggy, si possible (sinon essayez mon code), au moins pour les versions MSI. Voir la réponse https://stackoverflow.com/a/58833831/94393 .

Pour les versions CTR (Click-To-Run), vous devez supprimer toutes les mises à jour Office de novembre, ce qui peut entraîner de graves problèmes de sécurité (vous ne savez pas si des correctifs critiques seront supprimés).

D'après les commentaires de @ Eric:

  • Si vous utilisez Table.Tablename Pour lier des formulaires, ils ne sont plus liés car l'ancien nom de table est maintenant un nom de requête !.
  • OpenRecordSet(FormerTableNowAQuery, dbOpenTable) échouera (car c'est une requête maintenant, plus une table)

Attention! Test rapide contre Northwind.accdb sur Office 2013 x86 CTR Aucune garantie !

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

Pour tester:

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
3
ComputerVersteher

J'ai remplacé le currentDb.Execute et Docmd.RunSQL avec une fonction d'assistance. Cela peut prétraiter et modifier l'instruction SQL si une instruction de mise à jour contient une seule table. J'ai déjà une table dual (une seule ligne, une seule colonne) alors j'ai opté pour une option fakeTable.

Remarque: cela ne changera pas vos objets de requête. Il n'aidera que les exécutions SQL via 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.

Ce n'est qu'un concept (If it's a single table update modify the sql before execution). Adaptez-le selon vos besoins. Cette méthode ne crée pas de requêtes de remplacement pour chaque table (ce qui peut être le moyen le plus simple mais a ses propres inconvénients. À savoir les problèmes de performances)

+ Points: Vous pouvez continuer pour utiliser cet assistant même après que MS a corrigé le bogue, cela ne changera rien. Dans le cas où l'avenir pose un autre problème, vous êtes prêt à pre-process votre SQL en un seul endroit. Je n'ai pas opté pour la méthode désinstallation des mises à jour car cela nécessite un accès administrateur + va prendre trop de temps pour obtenir tout le monde sur la bonne version + même si vous désinstallez, la stratégie de groupe de certains utilisateurs finaux installe à nouveau la dernière mise à jour. Vous revenez au même problème.

Si vous avez accès au code source, use this method et vous êtes sûr à 100% qu'aucun utilisateur final ne rencontre le problème.

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.
    'https://stackoverflow.com/questions/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

Maintenant juste CTRL+F

Rechercher et remplacer docmd.RunSQL avec helper.Execute

Rechercher et remplacer [currentdb|dbengine|or your dbobject].execute avec helper.execute

s'amuser!

2
krish KM

Ok je vais carillon ici aussi, parce que même si ce bogue a été corrigé, ce correctif n'a pas encore été entièrement rempli par diverses entreprises où les utilisateurs finaux peuvent ne pas être en mesure de mettre à jour (comme mon employeur ...)

Voici ma solution pour DoCmd.RunSQL "UPDATE users SET uname= 'bob' WHERE usercode=1". Il suffit de commenter la requête incriminée et de déposer le code ci-dessous.

    '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

Je ne peux pas dire que c'est joli, mais ça fait le travail.

0
Chaosbydesign