【问题标题】:can't call update query from Excel VBA无法从 Excel VBA 调用更新查询
【发布时间】:2011-09-28 20:46:10
【问题描述】:

我在 Access 2007 数据库中存储了一个查询。我在这台机器上安装了 Access 2010。我正在尝试执行以下操作:

  1. 将 Excel 电子表格导出为数据库中的临时表。
  2. 在临时表中添加一列并用文件名填充它
  3. 使用导出表的内容更新链接表。

更新是 Access 前端中存储的查询。当我从 Access 运行更新查询时,它工作正常。但是当我使用代码从 VBA 运行它时:

sub test()

filename=thisworkbook.name
Set db_fe = OpenDatabase("C:\Data\myDB.mdb")
If TableExists(db_fe, "tempCorrection") Then
    DoCmd.RunSQL "drop table tempCorrection;"
End If
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, "tempCorrection", "C:\Data\corrections.xls", True


DoCmd.RunSQL "alter table tempCorrection add column newColumn text;"
DoCmd.RunSQL "update tempCorrection set newColumn='" & filename & "';", dbFailOnError
db_fe.Execute "updateCorrections", dbFailOnError
DoCmd.RunSQL "drop table tempCorrection;"

end sub

然后在“db_fe.execute”行上出现运行时错误“3078”:“Microsoft Access 数据库引擎找不到输入表或查询“tempCorrection”。确保它存在并且其名称拼写正确。”

查询 updateCorrections 如下所示:

UPDATE production AS p
INNER JOIN tempCorrection AS t
ON
(p.filename=t.filename)
AND
(p.a1=t.a1)
AND
(p.a2=t.a2)
set p.a3=t.a3

关于为什么我无法从 VBA 执行此查询有什么想法吗?

【问题讨论】:

  • 您正在使用两个不同的连接来执行 SQL。使用 DoCmd.RunSQL 时,您使用的是 CurrentDB(当前在 Access 用户界面中打开的数据库),而当您使用 .Execute 时,您使用的是打开到数据库“C:\Data\myDB”的连接.mdb”。如果DoCmd.RunSQL "drop table tempCorrection;" 运行正确,这意味着表tempCorrection 在当前打开的数据库中,而不是在另一个数据库中。这真的是草率的编码——你永远不应该把 DoCmd.RunSQL 和 .Execute 混在一起。事实上,你根本不应该使用 RunSQL……这太危险了。
  • @David-W-Fenton,好的,我将 .RunSQL 更新查询替换为 .Execute,并将 .RunSQL ALTER COLUMN 替换为 .tabledefs 然后 .createField。我仍然收到此错误。我应该在下面的答案中发布我更改的代码吗?
  • @David-W-Fenton,我已经把它贴在下面了。
  • 听起来你可能需要刷新缓存。在 ADO 中,这涉及创建一个 JRO.JetEngine 对象并使用连接对象调用其 RefreshCache 方法(例如,参见 this answer 中的 VBA)。不过,不知道如何使用 ye olde DAO 来做到这一点;)
  • VBE 对象浏览器是你的朋友:DBEngine.Idle dbRefreshCache

标签: sql excel ms-access vba


【解决方案1】:

如果您的代码在 Access 会话中正确运行,请考虑从您的 Excel 代码创建一个 Access 应用程序实例,然后从该 Access 应用程序实例运行其余部分。

Const cstrDbPath As String = "C:\Data\myDB.mdb"
Dim appAccess As Object
Set appAccess = CreateObject("Access.Application")
appAccess.OpenCurrentDatabase cstrDbPath, False
'then your code ... for example ... '
appAccess.DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, _
    "tempCorrection", "C:\Data\conrrections.xls", True
'before you exit the procedure ...'
appAccess.Quit
Set appAccess = Nothing

我希望这能为您指出一些有用的东西。但是,我怀疑这里涉及更多。我鼓励您在模块的声明会话中包含Option Explicit,然后从 VBE 主菜单调试->编译您的应用程序代码。似乎编译器可能会抱怨db_fe,因为您没有将其调暗……它是在其他地方声明的全局变量吗?不管是什么,请务必使用Option Explicit

编辑:在 TransferSpreadsheet 行上设置一个断点,然后逐行执行其余过程 (F8)。就在您到达db_fe.Execute 行之前,尝试这样的操作以查看是否找到了 tempCorrection:

Debug.Print DCount("*", "tempCorrection")

也不确定这会有多大用处……在这一点上,我基本上是在抓住稻草。我的直觉表明这可能与每次删除然后重新创建 tempCorrection 有关......我会围绕它编写代码。

Edit2:这部分代码让我感到困惑:

DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, _
    "tempCorrection", "C:\Data\corrections.xls", True
DoCmd.RunSQL "alter table tempCorrection add column newColumn text;"
DoCmd.RunSQL "update tempCorrection set newColumn='" & _
    filename & "';", dbFailOnError
db_fe.Execute "updateCorrections", dbFailOnError

您使用TransferSpreadsheet 创建表tempCorrection。稍后您在db_fe.Execute 上收到一个错误,表明数据库引擎找不到tempCorrection。但是在这两者之间,您执行了 2 个引用 tempCorrection 的 DDL 语句——我不明白为什么那些不会抛出关于找不到表的错误。也许这与 DoCmd.RunSQL (和/或您的 SetWarnings False)有关。我会将DoCmd.RunSQL 替换为db_fe.Execute 加上dbFailOnError

同样DoCmd.RunSQL的第二个参数是告诉数据库引擎在执行SQL时是否使用事务。使用dbFailOnError 作为DoCmd.RunSQL 的第二个参数似乎是错误的。

【讨论】:

  • DoCmd 有效,因为我包含了对 Access 14.0 对象库的引用。
  • 抱歉,我忘记包含“db_fe”的声明,它是在 VBE 中声明的。我尝试了“Option Explicit”并进行了编译,但仍然出现该错误。
  • 我刚刚在doCmd.transferSpreadsheet 之后添加了一行,得到了另一个错误。我添加了set tbl=db_fe.tabledefs("tempCorrection"),适当地声明了tbl。得到同样的错误。
  • 这是一个 Excel 代码模块。我正在为需要将内容提交到 Access 数据库但没有安装 Access 的用户进行开发,因此使用嵌入代码来运行 Access 操作查询的 Excel 工作表似乎是最好的。我试试 Edit2。
  • Edit2 代码给了我一个不同的错误:运行时错误 7866:“Microsoft Access 无法打开数据库,因为它丢失,或者被其他用户独占打开,或者它不是 ADP 文件。”
【解决方案2】:

根据@David-W-Fenton 的建议,我将在此处发布update() 的修改代码。我还包含了writeSheetTable() 的代码,该子过程从 Excel 中读取工作表值并将它们写入临时表 tempCorrection。我使用这个 sub 而不是 doCmd.transferspreadsheet 因为我认为在同一过程中使用 doCmd 和 database.execute 可能存在问题。

Sub update()

Dim db_fe As Database
Dim rs As Recordset
Dim tbl As TableDef
Dim fld As DAO.field
Dim tablestruct As String
dim filename as string


'open database'
Set db_fe = OpenDatabase("C:\Data\myDB.mdb")

'define SQL for creating temp table'
tablestruct = "create table tempCorrection " & _
"(a1 text,a2 text,a3 text,a4 text,a5 text,a6 text,a7 text," & _
"a8 text,a9 text,a10 text,a11 text,a12 text,a13 text,a14 text);"

'generate temp table from spreadsheet data'
writeSheetTable "my excel data", db_fe, "tempCorrection", tablestruct


'add field for userID and populate it, normally this is taken from filename'
Set tbl = db_fe.TableDefs("tempCorrection")
Set fld = tbl.CreateField("filename", dbText, 30)
tbl.Fields.Append fld
filename="TEST"
db_fe.Execute "update tempCorrection set filename='" & filename & "';", dbFailOnError
Debug.Print DCount("*", "tempCorrection")

'execute stored query updateCorrections, which I provided in my original question'
db_fe.Execute "updateCorrections"

'delete temp table'
db_fe.Execute "drop table tempCorrection;"

End Sub


Sub writeSheetTable(sheetname As String, db As Database, tablename As String, tablestruct As String)

Dim lastrow, lastcol, max As Long
Dim prodarray As Variant
Dim rs As Recordset
Dim ws As DAO.Workspace
Dim r, c As Long

'read in the sheet contents to prodArray'
With Sheets(sheetname)
    lastrow = .UsedRange.Rows.Count
    lastcol = .UsedRange.Columns.Count
    prodarray = .Range(.Cells(2, 1), .Cells(lastrow, lastcol))
End With
max = UBound(prodarray, 1)


'drop temp table if it already exists'
If TableExists(db, tablename) Then
    db.Execute "drop table " & tablename & ";"
End If


'create table using SQL defined in update()'
db.Execute tablestruct, dbFailOnError


'build table row by row as a recordset, using transaction to speed up appends'
Set rs = db.OpenRecordset(tablename)
Set ws = DBEngine.Workspaces(0)
ws.BeginTrans    
With rs
For r = 1 To UBound(prodarray, 1)
    .AddNew
    For c = 1 To UBound(prodarray, 2)
        .Fields(c - 1) = IIf(prodarray(r, c) = Empty, "", prodarray(r, c))
    Next
    .update
Next
End With
ws.CommitTrans

'destroy recordset object'
rs.Close
Set rs = Nothing

End Sub

即使我已将所有 DoCmd.RunSQl 语句替换为 database.execute,它仍然会出现错误。错误顺序如下:

  1. 我运行它一次并在Set tbl = db_fe.TableDefs("tempCorrection") 上收到“未在此集合中找到项目”错误
  2. 如果我保留 tempCorrection 存在,当我再次运行它时,它工作正常。如果我删除 tempCorrection 并再次运行它,它会给出相同的“未找到项目”错误。

【讨论】:

  • 在我看来,您需要删除任何现有版本的 tempCorrection 并在设置 TableDef 变量之前导入新版本。
  • @David-W-Fenton,该表在writeSheetTable() 中删除,行:if tableExists(db,tablename) then db.execute "drop table " & tablename & ";" end if
  • 刚刚又试了一次;现在我从 Access 中收到“...找不到对象 'tempCorrection'”错误,然后 VBA 在Debug.Print DCount("*", "tempCorrection") 上崩溃,并出现“运行时错误'0':保留错误”。评论debug.print 行让我回到上面描述的“未找到项目”错误序列。
  • 坦率地说,您的代码太复杂了,我无法弄清楚您要做什么。这不是我在生产前端要做的事情,因为它会搅动前端并使其膨胀(即删除和导入表)。但也许这一切都发生在临时数据库中,在这种情况下就可以了。即使这样,我也倾向于不导入整个表,而是简单地将其数据附加到现有表(在这样做之前清空)。
  • 我会看看是否能找到一种方法来完成此任务,而无需删除和创建表,而是清空并填充现有表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多