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é:
Comment puis-je exécuter avec succès mes requêtes? Dois-je simplement désinstaller cette mise à jour?
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é.
Voici un exemple de repro minimal:
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:
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).
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"
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
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é.
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é.
Pour ceux qui cherchent à automatiser ce processus via PowerShell , voici quelques-uns liens que j'ai trouvés qui peuvent être utiles:
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
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.
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.
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:
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
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!
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.