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

83

Depuis l'installation de la mise à jour Windows pour Office 2010, résolution de la base de connaissances 4444127, 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

La mise à jour en question est actuellement toujours installée:

Capture d'écran montrant la mise à jour 448127 de Microsoft Office 2010 Service Pack 2

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

Zvi Redler
la source

Réponses:

92

Sommaire

Il s'agit d'un bogue connu provoqué par les mises à jour d'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, utilisez "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: résolu 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 l' 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 buggy installée: Une erreur d'exécution 3340 se produit ("La requête" est corrompue ").


Liens connexes:

Heinzi
la source
9
Ce message semble rencontrer la même erreur en utilisant le runtime Access 64 bits et OLEDB. Des choses effrayantes, cela rendra inutilisables de nombreuses applications qui utilisent Access pour stocker des données.
Erik A
4
Je viens de vérifier un système avec Office 2013 32 bits et sur cette machine particulière, l'UUID pour la mise à jour est 90150000-006E-0409-0000-0000000FF1CE... ce n'est -0409-pas le cas -0407-.
Gord Thompson
4
Je viens de vérifier une autre machine dans le bureau qui a Office 2013 64 bits et l'UUID l'est -006E-0409-également. Les deux machines ont le Service Pack 1 pour Microsoft Office 2013 (KB2850036) installé.
Gord Thompson,
4
Pour Office 2010 Pro Plus (SP2), nous devions l'utiliser {90140000-0011-0000-0000-0000000FF1CE}dans le script de commandes. Notez {9014...pas{9114..}
AdamsTips
2
J'ai corrigé la mise à jour officielle pour résoudre le problème, mais j'obtiens toujours l'erreur. Quelqu'un d'autre a eu ce problème?
user218076
33

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 par le 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 une simple (SELECT * FROM Table)requête directement dans la UPDATEcommande. 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).

Joe Marinucci
la source
20

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 le signalement 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 MSI 2010, 2013 et 2016, et les licences 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 déploie pas pour tout le monde à la fois. 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 un critère spécifié (de sorte que les autres types de requêtes ne doivent pas être 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"

Gustav
la source
13
Êtes-vous en train de dire sans détour que nous passons à des centaines de milliers de lignes de code déployées sur plusieurs applications et corrigeons toutes les mises à jour SQL qui mettent à jour simplement une ligne de données? Je suppose que si vous écrivez une nouvelle requête aujourd'hui, et maintenant, une telle solution de contournement est possible. Mais pour le code et les applications existants, l'idée que les mises à jour SQL doivent être modifiées n'est bien sûr pas une approche pratique pour résoudre les problèmes de quelque manière que ce soit.
Albert D.Kallal
5
@ AlbertD.Kallal, vous devez savoir à partir de la liste MVP, que je me réfère simplement à l'explication de la source du problème. La façon de traiter le problème dépend vraiment de vous et de ce qui peut convenir à votre scénario. La méthode décrite ici n'est qu'une parmi plusieurs.
Gustav
1
@ AlbertD.Kallal Ne devrait-on pas renommer les tables et créer des QueryDefs avec l'ancien nom de table pour résoudre ce problème? (Je vais tester cela et publier le script si cela fonctionne)
ComputerVersteher
Vous pouvez le faire sans programmation, par exemple renommer la table "users" en "userst" et ensuite créer un nom de requête "users" - et alors cela fonctionnera sans chane de programmation ....
Zvi Redler
9
@ AlbertD.Kallal: Je partage votre douleur - s'il s'agissait d'un bogue affectant la bibliothèque d'exécution VC, je ne pense pas que MS retarderait le correctif d'un mois et suggérerait une solution de contournement "réécrire, recompiler et redéployer". (Pour être honnête, ils ont corrigé et publié rapidement le problème VBA fin août.) Mais ne tirons pas sur le messager - Gustav ne semble pas être un employé de MS. Espérons qu'ils reconsidèrent et publient un correctif plus tôt; après tout, cela affecte également les applications écrites dans d'autres langues qui se trouvent simplement utiliser le moteur Access DB .
Heinzi
15

La résolution temporaire de ce problème dépend de la version d'Access utilisée:
Access 2010 Uninstall update KB4484127
Access 2013 Uninstall update KB4484119
Access 2016 Uninstall update KB4484113
Access 2019 IF REQUIRED (tbc). Rétrograder de la version 1808 (build 10352.20042) à la version 1808 (build 10351.20054)
Office 365 ProPlus Rétrograder de la version 1910 (build 12130.20344) à une build précédente, voir https://support.microsoft.com/en-gb/help/2770432/ comment-revenir-à-une-version-antérieure-de-office-2013-ou-office-2016-clic

Subvention
la source
Je l'ai désinstallé, mais il a été réinstallé la prochaine fois que j'ai démarré Windows. Comment l'empêchez-vous de réinstaller?
dsteele
5
@dsteele Si la version MSI et aucun WSUS, utilisez l' outil de dépannage support.microsoft.com/en-us/help/3073930/… . Le CTR désactiver les mises à jour dans Office-Account-Settings ..
ComputerVersteher
5

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 a un impact sur 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é.

LukeChung-FMS
la source
4

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 exemple la table IceCreamssera 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 IceCreamset exécuterait le SQL select * from [IceCreams_Table].

RemoveWorkaroundForCorruptedQueryIssue() fait 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_Tablerevenir à USysRibbons, comme je ne l' avais pas marqué comme une table système lorsque je l' ai créé dans le passé.

lauxjpn
la source
J'aime que vous déterminiez une table système avec TableDef.Attributeset copiez cela dans ma réponse;) et une fonction d'annulation est une bonne idée (mais l'ancien et le nouveau nom doivent être stockés dans une table car ne dépendant d'aucune table avec suffixe avant le renommage). Certaines autres parties sont défectueuses (par exemple, les tables peuvent se terminer par le suffixe ou le nouveau nom est déjà utilisé ou On Error Resume Nextsans gérer les erreurs plus tard). Connaissez-vous RubberduckVBA ? Cet add-in peut inspecter votre code et fait de belles suggestions d'amélioration, en plus de toutes les autres fonctionnalités.
ComputerVersteher
Et vous devez signaler les bugs que notre approche peut provoquer (Voir les commentaires @Erics sur ma réponse)
ComputerVersteher
Ah, je n'ai pas vu qu'il y avait déjà une réponse similaire ici, alors merci pour la revue! Le suffixe est défini dans sa propre constante, il peut donc être facilement modifié dans le cas où un objet déjà préexistant défini utilise déjà le suffixe. Sinon, le script fonctionne tel quel, mais tout le monde devrait se sentir encouragé à le modifier selon ses besoins individuels. Le script a été testé sur des projets assez importants (plus de 400 tables), y compris des tables externes / liées à différentes sources de bases de données externes. Je ne connaissais pas Rubberduck (seulement MZ-Tools). Je vais certainement les vérifier!
lauxjpn
3

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

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

Un script PowerShell est disponible ici https://www.arcath.net/2017/09/office-update-remover qui recherche dans le registre une mise à jour Office spécifique (transmise sous forme de numéro de ko) et la 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.

Une modification que je suggérerais serait d'utiliser le /REBOOT=REALLYSUPPRESScomme décrit dans Comment désinstaller KB4011626 et d'autres mises à jour Office (référence supplémentaire: https://docs.microsoft.com/en-us/windows/win32/msi/uninstalling-patches ). 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 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.

AdamsTips
la source
3

VBA-Script 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/9439330 .

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 tu utilisesTable.Tablename pour lier des formulaires, ils deviennent non 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)

Mise en garde! 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
ComputerVersteher
la source
4
Notez que cette solution de contournement ruinera les sous-formulaires liés aux tables (devra être rebondi aux requêtes) et le code fonctionnant avec les tabledefs avec un nom de table codé en dur. À utiliser avec une grande prudence, il y a de fortes chances que cela corrige un bogue uniquement pour en créer deux nouveaux en fonction de ce que fait votre application.
Erik A
@ErikA Bien sûr, seule une solution de contournement, mais je peux lier Inventory to reorder Subform for Homeà la Inventorytable sous Homeforme, sans problème. Même n'est-il pas recommandé de lier des formulaires à des requêtes au lieu de tables (n'est-ce pas une liaison à une table comme Select * From table?).
ComputerVersteher
2
Si je lie un sous-formulaire à une table, je le fais généralement en utilisant la Table.TableNamenotation. Si vous le faites à la SELECT * FROM TableNameplace, vous êtes bien sûr bien. Mais si vous utilisez Table.TableName, votre sous-formulaire deviendra indépendant si vous renommez la table.
Erik A
@ErikA: C'est vrai. Un avantage à faire ça?
ComputerVersteher
3
Pas autant que je sache, sauf que c'est plus bref. Cependant, il y a un avantage substantiel TableDefs!MyTableName.OpenRecordset(dbOpenTable)(support de la recherche d'index), que j'ai également tendance à utiliser et qui causera également des erreurs avec votre approche
Erik A
2

J'ai remplacé le currentDb.Executeet Docmd.RunSQLpar une fonction d'aide. Cela peut prétraiter et modifier l'instruction SQL si une instruction de mise à jour contient une seule table. J'ai déjà une dualtable ( une seule ligne, une seule colonne), donc je suis allé avec 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. Par exemple, les problèmes de performances)

+ Points: Vous pouvez continuer à utiliser cet assistant même après que MS a corrigé le bug, cela ne changera rien. Dans le cas où l'avenir apporte un autre problème, vous êtes prêt à pre-processvotre SQL en un seul endroit. Je n'ai pas opté pour la méthode de désinstallation des mises à jour car cela nécessite un accès administrateur + ça 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 réinstalle la dernière mise à jour. Vous revenez au même problème.

Si vous avez accès au code source use this methodet que 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.
    '/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

Maintenant juste CTRL+F

Rechercher et remplacer docmd.RunSQLparhelper.Execute

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

s'amuser!

Krish
la source
0

Ok je vais carillon ici aussi, car 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 de contournement 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.

Chaosbydesign
la source