我在VBA中使用两个SQL查询,我相信它们可以在一个中完成,但是我无法让它工作。我想将VBA部分转换为VBA之外的查询,由于其处理的数据量,VBA不断破坏我的文件。 (通过休息我的意思是它给出一条消息,说“此文件不是一个有效的数据库”,使文件损坏)。我搜索该错误,但我发现的所有内容都与VBA代码无关。
无论如何,这是使用VBA运行的两个查询。
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, Tbl_VCodes.IsApd
HAVING Tbl_VCodes.IsApd = ""OFF"";
我循环这些结果来更新表。
Do While Not .EOF
SQL = "UPDATE Tbl_AttendanceByAgent SET EXC = " & recSet.Fields(1).Value & _
" WHERE VerintID = '" & recSet.Fields(0).Value & "'"
CurrentDb.Execute SQL
.MoveNext
Loop
我知道我可以将第一个查询的结果保存到表中而没有循环我可以使用另一个SQL查询更新主表,但我相信它可以在单个SQL上完成。我尝试使用UPDATE与第一个查询的SELECT,但它只是错误输出我的语法无效。
是的,这可以在一个查询中实现,如下所示
UPDATE Tbl_AttendanceByAgent
SET Tbl_AttendanceByAgent.EXC = t2.Exeptions
from Tbl_AttendanceByAgent t1
inner join (
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes as TV ON ET.Exception = TV.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, TV.IsApd
HAVING Tbl_VCodes.IsApd = 'OFF'
) AS t2 on t2.EID = t1.VerintID
注意:我想您将使用代码中的值替换sDate,eDate
这个问题是对所描述的错误和给定代码的回答,尽管它在技术上不回答对单个SQL语句的请求。我开始添加评论,但是当这个答案框允许一次有效地表达所有内容时,这太简单了。
首先,引用CurrentDb
实际上不是对单个对象实例的基本引用。相反,它更像是一个函数调用,它生成底层数据库对象的一个新的,唯一的“克隆”。众所周知,一次又一次地调用它会产生内存泄漏,至少效率非常低。有关详细信息,请参阅MS docs。
虽然给定的代码很短,但它并不甜蜜。它不仅重复创建新的数据库对象,而且每次都重复执行一个SQL语句来更新我假设的单行。这也需要每次都重新生成SQL字符串。
即使重复执行SQL语句是一种有效的选项,也有更好的方法,比如使用参数创建临时(内存中)QueryDef对象。然后,每个循环迭代只重置参数并执行相同的准备好的SQL语句。
但在这种情况下,将更新的表加载到DAO.Recordset实际上可能更有效,然后使用内存中的Recordset搜索匹配,然后使用记录集更新行。
我怀疑解决其中的几个问题会使你的VBA代码可行。
Dim db as Database
Set db = CurrentDb 'Get just a single instance and reuse
Dim qry as QueryDef
SQL = "PARAMETERS pEXC Text ( 255 ), pID Long; " & _
" UPDATE Tbl_AttendanceByAgent SET EXC = pEXC " & _
" WHERE VerintID = pID"
set qry = db.CreateQueryDef("", SQL)
'With recSet '???
Do While Not .EOF
qry.Parameters("pEXC") = recSet.Fields(1).Value
qry.Parameters("pID") = recSet.Fields(0).Value
qry.Execute
.MoveNext
Loop
'End With recSet '???
'OR an alternative
Dim recUpdate As DAO.Recordset2
Set recUpdate = db.OpenRecordset("Tbl_AttendanceByAgent", DB_OPEN_TABLE)
Do While Not .EOF
recUpdate.FindFirst "VerintID = " & recSet.Fields(0).Value
If Not recUpdate.NoMatch Then
recUpdate.Edit
recUpdate.Fields("EXC") = recSet.Fields(1).Value
recUpdate.Update
End If
.MoveNext
Loop
我在评论Gro的答案时意识到,原始查询的聚合子句将在EID上产生唯一值,但很明显,不需要对没有Tbl_VCodes.IsApd = 'OFF'
的值进行分组(和求和)。查询会更有效
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
AND Tbl_VCodes.IsApd = 'OFF'
GROUP BY ET.VerintEID;
顺便说一句,您可以考虑实现与上面显示的相同的临时QueryDef模式,然后您将第一个WHERE表达式更改为类似
PARAMETERS PsDate DateTime, PeDate DateTime;
...
WHERE (ET.ExceptDate Between [PsDate] And [PeDate])
...