Bắt lỗi 3340 Truy vấn '' bị hỏng trong khi thực hiện truy vấn DoCmd.RunQuery


83

Kể từ khi cài đặt bản cập nhật windows cho Office 2010 giải quyết KB 4484127, tôi gặp lỗi trong khi thực hiện các truy vấn có chứa mệnh đề WHERE.

Ví dụ: thực hiện truy vấn này:

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

Kết quả là lỗi này:

Số lỗi = 3340 Truy vấn '' bị hỏng

Bản cập nhật trong câu hỏi hiện vẫn đang được cài đặt:

Ảnh chụp màn hình hiển thị Microsoft Office 2010 Service Pack 2 cập nhật 448127

Làm thế nào tôi có thể chạy thành công các truy vấn của tôi? Tôi có nên gỡ cài đặt bản cập nhật này không?

Câu trả lời:


92

Tóm lược

Đây là một lỗi đã biết do các bản cập nhật Office được phát hành vào ngày 12 tháng 11 năm 2019. Lỗi này ảnh hưởng đến tất cả các phiên bản Access hiện được Microsoft hỗ trợ (từ Access 2010 đến 365).

Lỗi này đã được cố định.

  • Nếu bạn sử dụng phiên bản Office của C2R (Nhấp để chạy), hãy sử dụng "Cập nhật ngay" :
    • Truy cập 2010 C2R: Đã sửa trong Build 7243.5000
    • Truy cập 2013 C2R: Đã sửa trong Build 5197.1000
    • Truy cập 2016 C2R: Đã sửa trong Bản dựng 12130.20390
    • Truy cập 2019 (v1910): Đã sửa trong Bản dựng 12130.20390
    • Truy cập 2019 (Giấy phép số lượng lớn): Đã sửa trong Bản dựng 10353.20037
    • Kênh Office 365 hàng tháng: Đã sửa trong Bản dựng 12130.20390
    • Office 365 Bán niên: Đã sửa trong Bản dựng 11328.20480
    • Office 365 được mở rộng nửa năm: Đã sửa trong bản dựng 10730.20422
    • Office 365 được nhắm mục tiêu bán hàng năm: Đã sửa trong bản dựng 11929.20494
  • Nếu bạn sử dụng phiên bản Office của MSI, hãy cài đặt bản cập nhật phù hợp với phiên bản Office của bạn. Tất cả các bản vá này đã được phát hành trên Microsoft Update, vì vậy việc cài đặt tất cả các Bản cập nhật Windows đang chờ xử lý là đủ:

Thí dụ

Dưới đây là một ví dụ repro tối thiểu:

  1. Tạo cơ sở dữ liệu Access mới.
  2. Tạo một bảng mới, trống "Bảng1" với trường ID mặc định và trường Số nguyên dài "myint".
  3. Thực thi đoạn mã sau trong Cửa sổ ngay lập tức của trình soạn thảo VBA:

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

Kết quả dự kiến : Tuyên bố kết thúc thành công.

Kết quả thực tế với một trong các bản cập nhật lỗi được cài đặt: Xảy ra lỗi thời gian chạy 3340 ("Truy vấn '' bị hỏng").


Liên kết liên quan:


9
Bài đăng này dường như gặp phải lỗi tương tự khi sử dụng thời gian chạy Access 64-bit và OLEDB. Thứ đáng sợ, điều này sẽ làm cho nhiều ứng dụng sử dụng Access để lưu trữ dữ liệu không thể nhận được.
Erik A

4
Tôi vừa kiểm tra một hệ thống với Office 2013 32 bit và trên máy cụ thể đó, UUID cho bản cập nhật là 90150000-006E-0409-0000-0000000FF1CE... -0409-không phải vậy -0407-.
Gord Thompson

4
Tôi vừa kiểm tra một máy khác trong văn phòng có Office 2013 64-bit và UUID -006E-0409-cũng vậy. Cả hai máy đều có Gói dịch vụ 1 cho Microsoft Office 2013 (KB2850036) được cài đặt.
Gord Thompson

4
Đối với Office 2010 Pro Plus (SP2), chúng tôi cần sử dụng {90140000-0011-0000-0000-0000000FF1CE}trong tập lệnh bó. Lưu ý {9014...không{9114..}
AdamsTips

2
Tôi đã vá với bản cập nhật chính thức để giải quyết vấn đề nhưng tôi vẫn nhận được lỗi. Bất cứ ai khác có vấn đề đó?
dùng218076

33

Giải pháp đơn giản nhất

Đối với người dùng của tôi, chờ gần một tháng cho đến ngày 10 tháng 12 để phát hành bản sửa lỗi từ Microsoft không phải là một lựa chọn. Cũng không gỡ cài đặt bản cập nhật Microsoft vi phạm trên một số máy trạm bị khóa.

Tôi cần áp dụng một cách giải quyết, nhưng không thực sự hồi hộp với những gì Microsoft đề xuất - tạo và thay thế một truy vấn cho mỗi bảng.

Giải pháp là thay thế tên Bảng bằng một (SELECT * FROM Table)truy vấn đơn giản trực tiếp trong UPDATElệnh. Điều này không yêu cầu tạo và lưu một tấn truy vấn, bảng hoặc hàm bổ sung.

THÍ DỤ:

Trước:

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

Sau:

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

Điều đó sẽ dễ dàng hơn nhiều để thực hiện trên một số cơ sở dữ liệu và ứng dụng (và sau đó là rollback).


20

Đây không phải là sự cố cập nhật Windows, mà là sự cố được giới thiệu với bản phát hành Văn phòng Patch thứ ba tháng 11. Một thay đổi để khắc phục lỗ hổng bảo mật khiến một số truy vấn hợp pháp được báo cáo là bị hỏng. Bởi vì thay đổi là một sửa chữa bảo mật, nó ảnh hưởng đến TẤT CẢ các bản dựng của Office, bao gồm 2010, 2013, 2016, 2019 và O365.

Lỗi đã được sửa trong tất cả các kênh, nhưng thời gian phân phối sẽ phụ thuộc vào kênh bạn đang truy cập.

Đối với các bản dựng Cấp phép Khối lượng 2010, 2013 và 2016 MSI và 2019 và kênh Bán niên O365, bản sửa lỗi sẽ nằm trong bản dựng Thứ ba Bản vá tháng 12, ngày 10 tháng 12 Đối với O365, Kênh hàng tháng và Người trong cuộc, điều này sẽ được sửa khi ngã ba tháng 10 được phát hành, hiện đang lên kế hoạch cho ngày 24 tháng 11.

Đối với kênh Semi-Year, lỗi được giới thiệu vào 11328.20468, được phát hành vào ngày 12 tháng 11, nhưng không được tung ra cho tất cả mọi người cùng một lúc. Nếu bạn có thể, bạn có thể muốn giữ cập nhật cho đến ngày 10 tháng 12.

Sự cố xảy ra đối với các truy vấn cập nhật đối với một bảng có một tiêu chí được chỉ định (vì vậy các loại truy vấn khác không nên bị ảnh hưởng, cũng không có bất kỳ truy vấn nào cập nhật tất cả các hàng của bảng, cũng không phải là truy vấn cập nhật tập kết quả của truy vấn khác). Do đó, cách giải quyết đơn giản nhất trong hầu hết các trường hợp là thay đổi truy vấn cập nhật để cập nhật một truy vấn khác chọn mọi thứ từ bảng, thay vì cập nhật truy vấn trực tiếp.

Tức là, nếu bạn có một truy vấn như:

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

Sau đó, tạo một truy vấn mới (Query1) được xác định là:

Select * from Table1;

và cập nhật truy vấn ban đầu của bạn thành:

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

Trang chính thức: Lỗi truy cập: "Truy vấn bị hỏng"


13
Bạn có thực sự nói thẳng rằng chúng tôi có tới hàng trăm ngàn dòng mã được triển khai trên nhiều ứng dụng và sửa tất cả các bản cập nhật sql đơn giản cập nhật một hàng dữ liệu? Tôi cho rằng nếu bạn viết một truy vấn mới ngày hôm nay và ngay bây giờ, thì cách giải quyết như vậy là có thể. Nhưng đối với các mã và ứng dụng hiện có, ý tưởng rằng các cập nhật sql sẽ được thay đổi tất nhiên không phải là một cách tiếp cận thực tế để giải quyết vấn đề theo bất kỳ cách nào.
Albert D. Kallal

5
@ AlbertD.Kallal, bạn nên biết từ danh sách MVP, rằng tôi chỉ đề cập đến lời giải thích cho nguồn gốc của vấn đề. Làm thế nào để giải quyết vấn đề thực sự tùy thuộc vào bạn và những gì có thể phù hợp với kịch bản của bạn. Phương pháp được mô tả ở đây chỉ là một trong số nhiều.
Gustav

1
@ AlbertD.Kallal Không nên đổi tên bảng và tạo QueryDefs với sửa tên bảng cũ đó? (Tôi sẽ kiểm tra điều đó và đăng kịch bản nếu nó hoạt động)
ComputerVersteher

Bạn có thể làm điều đó mà không cần lập trình, ví dụ đổi tên bảng "users" thành "userst" và sau đó tạo tên truy vấn "users" - và sau đó nó sẽ hoạt động mà không có chương trình lập trình ....
Zvi Redler

9
@ AlbertD.Kallal: Tôi chia sẻ nỗi đau của bạn - nếu đây là một lỗi ảnh hưởng đến thư viện thời gian chạy của VC, tôi không nghĩ rằng MS sẽ trì hoãn việc sửa chữa trong một tháng và đề xuất cách khắc phục "viết lại, biên dịch lại và triển khai lại". . Hãy hy vọng họ xem xét lại và phát hành một bản vá sớm hơn; Rốt cuộc, nó cũng ảnh hưởng đến các ứng dụng được viết bằng các ngôn ngữ khác tình cờ sử dụng công cụ Access DB .
Heinzi

15

Để tạm thời giải quyết vấn đề này tùy thuộc vào phiên bản Access đang sử dụng:
Access 2010 Gỡ cài đặt bản cập nhật KB4484127
Access 2013 Gỡ cài đặt bản cập nhật KB4484119
Access 2016 Gỡ cài đặt bản cập nhật KB4484113
Access 2019 NẾU YÊU CẦU (tbc). Hạ cấp từ Phiên bản 1808 (Bản dựng 10352.20042) xuống Phiên bản 1808 (Bản dựng 10351.20054)
Office 365 ProPlus Hạ cấp từ Phiên bản 1910 (Bản dựng 12130.20344) xuống bản dựng trước đó, xem https://support.microsoft.com/en-gb/help/2770432/ how-to-Revert-to-an-sớm-phiên bản của văn phòng-2013-hoặc-office-2016-clic


Tôi đã gỡ cài đặt nó, nhưng nó được cài đặt lại vào lần tiếp theo tôi khởi động Windows. Làm thế nào để bạn ngăn chặn nó cài đặt lại?
dsteele

5
@dsteele Nếu phiên bản MSI và không có WSUS, hãy sử dụng support.microsoft.com/en-us/help/3073930/ công cụ khắc phục sự cố. Trên TLB, vô hiệu hóa các cập nhật trong Cài đặt tài khoản Office ..
ComputerVersteher

5

Chúng tôi và khách hàng của chúng tôi đã vật lộn với điều này trong hai ngày qua và cuối cùng đã viết một bài báo để thảo luận chi tiết về vấn đề này cùng với một số giải pháp: http://fmsinc.com/MicrosoftAccess/Errors/query_is_corrupt/

Nó bao gồm các phát hiện của chúng tôi rằng nó tác động đến các giải pháp Access khi chạy các truy vấn cập nhật trên các bảng cục bộ, các bảng Access được liên kết và thậm chí các bảng SQL Server được liên kết.

Nó cũng tác động đến các giải pháp không phải của Microsoft bằng cách sử dụng Công cụ cơ sở dữ liệu truy cập (ACE) để kết nối với cơ sở dữ liệu Access bằng ADO. Điều đó bao gồm các ứng dụng Visual Studio (WinForm), ứng dụng VB6 và thậm chí các trang web cập nhật cơ sở dữ liệu Access trên các máy chưa từng cài đặt Access hoặc Office trên chúng.

Sự cố này thậm chí có thể ảnh hưởng đến các ứng dụng của Microsoft sử dụng ACE như PowerBI, Power Query, SSMA, v.v. (không được xác nhận) và tất nhiên, các chương trình khác như Excel, PowerPoint hoặc Word sử dụng VBA để sửa đổi cơ sở dữ liệu Access.

Ngoài việc gỡ cài đặt rõ ràng các Cập nhật bảo mật vi phạm, chúng tôi cũng bao gồm một số tùy chọn khi không thể gỡ cài đặt do quyền hoặc phân phối ứng dụng Access cho khách hàng bên ngoài có PC nằm ngoài tầm kiểm soát của bạn. Điều đó bao gồm thay đổi tất cả các truy vấn Cập nhật và phân phối các ứng dụng Access bằng Access 2007 (bán lẻ hoặc thời gian chạy) vì phiên bản đó không bị ảnh hưởng bởi các bản cập nhật bảo mật.


4

Sử dụng mô-đun sau để tự động triển khai giải pháp được đề xuất của microsofts (sử dụng truy vấn thay vì bảng). Để phòng ngừa, hãy sao lưu cơ sở dữ liệu của bạn trước.

Sử dụng AddWorkaroundForCorruptedQueryIssue()để thêm cách giải quyết và RemoveWorkaroundForCorruptedQueryIssue()loại bỏ nó bất cứ lúc nào.

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

Bạn có thể tìm thấy mã mới nhất trên kho GitHub của tôi .

AddWorkaroundForCorruptedQueryIssue()sẽ thêm hậu tố _Tablevào tất cả các bảng không thuộc hệ thống, ví dụ: bảng IceCreamssẽ được đổi tên thành IceCreams_Table.

Nó cũng sẽ tạo một truy vấn mới bằng cách sử dụng tên bảng gốc, sẽ chọn tất cả các cột của bảng được đổi tên. Trong ví dụ của chúng tôi, truy vấn sẽ được đặt tên IceCreamsvà sẽ thực thi SQL select * from [IceCreams_Table].

RemoveWorkaroundForCorruptedQueryIssue() không hành động ngược lại.

Tôi đã thử nghiệm điều này với tất cả các loại bảng, bao gồm các bảng không MDB bên ngoài (như SQL Server). Nhưng lưu ý rằng việc sử dụng truy vấn thay vì bảng có thể dẫn đến các truy vấn không được tối ưu hóa được thực thi đối với cơ sở dữ liệu phụ trợ trong các trường hợp cụ thể, đặc biệt nếu các truy vấn ban đầu sử dụng các bảng có chất lượng kém hoặc rất phức tạp.

(Và tất nhiên, tùy thuộc vào phong cách mã hóa của bạn, bạn cũng có thể phá vỡ mọi thứ trong ứng dụng của mình. Vì vậy, sau khi xác minh rằng bản sửa lỗi thường hoạt động với bạn, không bao giờ nên xuất tất cả các đối tượng của bạn dưới dạng văn bản và sử dụng một số thay thế ma thuật để đảm bảo rằng bất kỳ sự xuất hiện nào của tên bảng sẽ được chạy với các truy vấn chứ không phải các bảng.)

Trong trường hợp của tôi, sửa lỗi này hoạt động chủ yếu mà không có bất kỳ tác dụng phụ nào, tôi chỉ cần đổi tên thủ công USysRibbons_Tabletrở lại USysRibbons, vì tôi đã không đánh dấu nó là bảng hệ thống khi tôi tạo nó trước đây.


Tôi thích việc bạn xác định một hệ thống có thể TableDef.Attributessao chép và sao chép câu trả lời của tôi;) và một chức năng hoàn tác là một ý tưởng tốt (nhưng tên cũ và mới nên được lưu trữ trong một bảng tùy thuộc vào không có bảng nào có hậu tố trước khi đổi tên). Một số phần khác bị lỗi (ví dụ: các bảng có thể kết thúc bằng hậu tố hoặc tên mới đã sẵn sàng sử dụng hoặc On Error Resume Nextkhông xử lý lỗi sau này). Bạn có biết RubberduckVBA ? Addin này có thể kiểm tra mã của bạn và đưa ra các đề xuất hay cho ngẫu hứng, bên cạnh tất cả các tính năng khác.
ComputerVersteher

Và bạn nên chỉ ra các lỗi mà cách tiếp cận của chúng tôi có thể gây ra (Xem bình luận @Erics về câu trả lời của tôi)
ComputerVersteher

Ah, tôi không thấy rằng đã có một câu trả lời tương tự ở đây, vì vậy cảm ơn bạn đã xem xét! Hậu tố được định nghĩa trong hằng số riêng của nó, vì vậy nó có thể dễ dàng thay đổi trong trường hợp có một đối tượng đã có từ trước được xác định đã sử dụng hậu tố. Mặt khác, kịch bản hoạt động như bình thường nhưng bất kỳ ai cũng nên cảm thấy được khuyến khích sửa đổi nó theo nhu cầu cá nhân của họ. Kịch bản đã được thử nghiệm trên các dự án khá lớn (hơn 400 bảng) bao gồm các bảng bên ngoài / được liên kết với các nguồn cơ sở dữ liệu bên ngoài khác nhau. Tôi không biết về Rubberduck (chỉ về MZ-Tools). Tôi chắc chắn sẽ kiểm tra chúng ra!
lauxjpn

3

Đối với những người đang tìm cách tự động hóa quy trình này thông qua PowerShell , đây là một vài liên kết tôi thấy có thể hữu ích:

Phát hiện và xóa các cập nhật vi phạm

Có một tập lệnh PowerShell có sẵn tại đây https://www.arcath.net/2017/09/office-update-remover tìm kiếm sổ đăng ký cho một bản cập nhật Office cụ thể (được chuyển qua dưới dạng số kb) và xóa nó bằng cách sử dụng lệnh gọi đến msiexec.exe. Kịch bản lệnh này phân tích cả GUID từ các khóa đăng ký để xây dựng lệnh xóa bản cập nhật thích hợp.

Một thay đổi mà tôi muốn đề xuất là sử dụng /REBOOT=REALLYSUPPRESSnhư được mô tả trong Cách gỡ cài đặt KB4011626 và các bản cập nhật Office khác (Tham khảo bổ sung: https://docs.microsoft.com/en-us/windows/win32/msi/uninstalling-patches ). Dòng lệnh bạn đang xây dựng trông như thế này:

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

Lệnh để chạy tập lệnh sẽ trông giống như thế này:

OfficeUpdateRemover.ps1 -kb 4484127

Ngăn chặn các bản cập nhật cài đặt

Cách tiếp cận được đề nghị ở đây dường như đang ẩn bản cập nhật . Rõ ràng điều này có thể được thực hiện thủ công, nhưng có một số tập lệnh PowerShell có thể giúp tự động hóa. Liên kết này: https://www.maketecheasier.com/ leather-updates-in-windows-10 / mô tả quá trình một cách chi tiết, nhưng tôi sẽ tóm tắt nó ở đây.

  1. Cài đặt Mô-đun PowerShell của Windows Update .
  2. Sử dụng lệnh sau để ẩn bản cập nhật theo số KB:

    Ẩn-WUUpdate -KBArticleID KB4484127

Hy vọng rằng đây sẽ là một sự giúp đỡ cho người khác ngoài kia.


3

VBA-Script cho MS-Workaround:

Bạn nên xóa bản cập nhật lỗi, nếu có thể (nếu không thử mã của tôi), ít nhất là cho các Phiên bản MSI. Xem câu trả lời https://stackoverflow.com/a/58833831/9439330 .

Đối với các Phiên bản TLB (Nhấp để chạy), bạn phải xóa tất cả các Bản cập nhật tháng 11 của Office, điều gì có thể gây ra sự cố bảo mật nghiêm trọng (không chắc chắn nếu có bất kỳ sửa lỗi quan trọng nào sẽ bị xóa).

Từ ý kiến ​​của @ Eric:

  • Nếu bạn sử dụng Table.Tablenameđể liên kết các biểu mẫu, chúng sẽ không bị ràng buộc vì tên bảng trước đây bây giờ là tên truy vấn!.
  • OpenRecordSet(FormerTableNowAQuery, dbOpenTable) sẽ thất bại (như một truy vấn bây giờ, không phải là một bảng nữa)

Chú ý! Chỉ cần thử nghiệm nhanh với Northwind.accdb trên Office 2013 x86 TLB Không bảo hành!

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

Để thử nghiệm:

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

4
Lưu ý rằng cách giải quyết này sẽ phá hỏng các biểu mẫu con được liên kết với các bảng (sẽ cần được phục hồi cho các truy vấn) và mã làm việc với các tabledefs với tên bảng được mã hóa cứng. Sử dụng hết sức thận trọng, tỷ lệ cược này chỉ sửa một lỗi để tạo hai lỗi mới tùy thuộc vào ứng dụng của bạn đang làm gì.
Erik A

@ErikA Tất nhiên chỉ là một cách giải quyết, nhưng tôi có thể liên kết Inventory to reorder Subform for Homevới Inventorybảng Homedưới dạng, không có vấn đề. Thậm chí nó không được đề xuất để liên kết các biểu mẫu với các truy vấn thay vì các bảng (không ràng buộc với bảng như thế Select * From tablenào?).
ComputerVersteher

2
Nếu tôi liên kết một biểu mẫu con với một bảng, tôi thường làm điều đó bằng cách sử dụng Table.TableNameký hiệu. Nếu bạn làm SELECT * FROM TableNamethay, bạn tất nhiên là tốt. Nhưng nếu bạn sử dụng Table.TableName, biểu mẫu con của bạn sẽ không bị ràng buộc nếu bạn đổi tên bảng.
Erik A

@ErikA: Đúng vậy. Bất kỳ lợi ích bằng cách làm điều đó?
ComputerVersteher

3
Không xa như tôi biết, ngoại trừ việc nó ngắn gọn hơn. Tuy nhiên, có một lợi thế đáng kể TableDefs!MyTableName.OpenRecordset(dbOpenTable)(hỗ trợ tìm kiếm chỉ mục), mà tôi cũng có xu hướng sử dụng và cũng sẽ gây ra lỗi với cách tiếp cận của bạn
Erik A

2

Tôi đã thay thế currentDb.ExecuteDocmd.RunSQLvới một chức năng trợ giúp. Điều đó có thể xử lý trước và thay đổi Câu lệnh SQL nếu bất kỳ câu lệnh cập nhật nào chỉ chứa một bảng. Tôi đã có một bảng dual(một hàng, một cột) vì vậy tôi đã sử dụng tùy chọn fakeTable.

Lưu ý : Điều này sẽ không thay đổi các đối tượng truy vấn của bạn. Nó sẽ chỉ giúp thực thi SQL thông qua 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.

Đây chỉ là một khái niệm (If it's a single table update modify the sql before execution). Điều chỉnh nó theo nhu cầu của bạn. Phương pháp này không tạo các truy vấn thay thế cho mỗi bảng (có thể là cách dễ nhất nhưng có nhược điểm riêng. Đó là vấn đề về hiệu suất)

+ Điểm: Bạn có thể tiếp tục sử dụng trình trợ giúp này ngay cả sau khi MS sửa lỗi, nó sẽ không thay đổi gì cả. Trong trường hợp, tương lai mang đến một vấn đề khác, bạn đã sẵn sàng để pre-processSQL của bạn ở một nơi. Tôi đã không gỡ cài đặt bản cập nhật phương pháp vì điều đó yêu cầu quyền truy cập của Quản trị viên + sẽ mất quá nhiều thời gian để đưa mọi người vào đúng phiên bản + ngay cả khi bạn gỡ cài đặt, một số chính sách nhóm của người dùng cuối sẽ cài đặt lại bản cập nhật mới nhất. Bạn đang trở lại cùng một vấn đề.

Nếu bạn có quyền truy cập vào mã nguồn use this methodvà bạn chắc chắn 100% rằng không có người xử lý nào gặp sự cố.

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

Bây giờ chỉ cần CTRL+F

Tìm kiếm và thay thế docmd.RunSQLbằnghelper.Execute

Tìm kiếm và thay thế [currentdb|dbengine|or your dbobject].executebằnghelper.execute

chúc vui vẻ!


0

Ok tôi cũng sẽ đồng ý ở đây, vì mặc dù lỗi này đã được sửa nhưng bản sửa lỗi đó vẫn chưa được đưa vào đầy đủ thông qua các doanh nghiệp khác nhau nơi người dùng cuối có thể không thể cập nhật (như chủ nhân của tôi ...)

Đây là cách giải quyết của tôi DoCmd.RunSQL "UPDATE users SET uname= 'bob' WHERE usercode=1". Chỉ cần nhận xét các truy vấn vi phạm và thả mã bên dưới.

    '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

Tôi không thể nói nó đẹp, nhưng nó hoàn thành công việc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.