从 Excel VBA 运行嵌套的 Access SQL 查询

Posted

技术标签:

【中文标题】从 Excel VBA 运行嵌套的 Access SQL 查询【英文标题】:Run nested Access SQL Query from Excel VBA 【发布时间】:2013-08-30 16:21:27 【问题描述】:

很抱歉标题不够具体,但英语不是我的母语,我无法更好地解释。这不是关于如何从 Excel VBA 在 Access 数据库上运行查询的问题,我知道该怎么做。我请求帮助是因为我在 Access 中内置了一个用于测试的有效 SQL 查询,我需要从启用宏的 Excel 电子表格中启动它。 比赛:我正在构建一个由 Access 数据库(它是“被动的”,它只存储数据)和一些与之交互的 Excel 电子表格组成的工具。我需要这种方式,因为用户必须使用它,所以我无法更改它。我有一些函数可以让我与预先构建我需要的字符串的数据库进行通信。在这种情况下,我想读取查询的记录集结果。实现这一点的 VBA 函数如下:

Public Function Read_Recordset(ByVal stSQL1 As String) As ADODB.Recordset
Dim cnt As ADODB.Connection
Dim stDB As String
Dim stConn As String
Dim wbBook As Workbook
Dim wsSheet1 As Worksheet

 'Instantiate the ADO-objects.
Set cnt = New ADODB.Connection
Set Read_Recordset = New ADODB.Recordset

 'Path to the database.
stDB = Foglio1.Cells(1, 2)

 'Create the connectionstring.
stConn = "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source=" & stDB & ";"

With cnt
    .Open (stConn) 'Open the connection.
    .CursorLocation = adUseClient 'Necessary to disconnect the recordset.
End With
Debug.Print stSQL1

With Read_Recordset
    .Open stSQL1, cnt 'Create the recordset.
    Set .ActiveConnection = Nothing 'Disconnect the recordset.
End With

'Release objects from the memory.
cnt.Close
Set cnt = Nothing
End Function

现在,我已经在 Access DB 中构建了我需要的查询,这非常复杂,但工作完美无瑕:

SELECT TOP 2 *
FROM (
SELECT C.Name, format(O.Freight,"#0.00") as Freight, format((O.Forwarding+
C.FixedFee + O.Freight*C.MgmtSurcharge/100 + O.Freight*C.FixedFuelSurcharge/100+(
Switch
(
1.85<C.FuelReferencePrice,
C.FuelReferencePrice,1.85>C.FuelReferencePrice,1.85
)
- C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight),"#0.00") as
AdditionalCosts,format((O.Freight+(O.Forwarding + C.FixedFee +
O.Freight*C.MgmtSurcharge/100 + O.Freight*C.FixedFuelSurcharge/100+(
Switch
(
1.85<C.FuelReferencePrice,
C.FuelReferencePrice,1.85>C.FuelReferencePrice,1.85
)
- C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight)),"#0.00") as TotalCost
FROM Temp_TaxableWeights AS T INNER JOIN (Weight_Ranges AS W INNER JOIN (Carriers AS C
INNER JOIN [OBPT_Groupage&LorryOwner] AS O ON C.[ID] = O.[CarrierID]) ON W.ID =
O.WeightRangeID) ON T.CarrierID = C.ID
WHERE (((W.WeightMin)< T.TaxableWeight) AND ((W.WeightMax)>= T.TaxableWeight) AND
((O.DistrictID)=35)) AND O.RateTypeID=4
UNION SELECT C.Name, format(O.Freight*T.TaxableWeight,"#0.00") as Freight,
format((O.Forwarding + C.FixedFee + O.Freight*T.TaxableWeight*C.MgmtSurcharge/100 +
O.Freight*T.TaxableWeight*C.FixedFuelSurcharge/100+(
Switch
(
1.85<C.FuelReferencePrice,
C.FuelReferencePrice,1.85>C.FuelReferencePrice,1.85
)
-C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight*T.TaxableWeight),"#0.00")
as AdditionalCosts,format((O.Freight*T.TaxableWeight +O.Forwarding + C.FixedFee +
O.Freight*T.TaxableWeight*C.MgmtSurcharge/100 +
O.Freight*T.TaxableWeight*C.FixedFuelSurcharge/100+(
Switch
(
1.85<C.FuelReferencePrice,
C.FuelReferencePrice,1.85>C.FuelReferencePrice,1.85
)
-C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight*T.TaxableWeight),"#0.00")
as TotalCost
FROM Temp_TaxableWeights AS T INNER JOIN (Weight_Ranges AS W INNER JOIN (Carriers AS C
INNER JOIN [OBPT_Groupage&LorryOwner] AS O ON C.[ID] = O.[CarrierID]) ON W.ID =
O.WeightRangeID) ON T.CarrierID = C.ID
WHERE (((W.WeightMin)< T.TaxableWeight) AND ((W.WeightMax)>= T.TaxableWeight) AND
((O.DistrictID)=35)) AND O.RateTypeID=8
ORDER BY TotalCost ASC
)  AS Best2Quotations;

这给了我想要的结果:

现在我的问题。我需要从 Excel 电子表格启动此查询,因为它不会是静态的,因为我在 Access 中编写它以进行测试:一些值是从工作表本身获取的。但是,我什至无法运行静态的。我正在尝试使用此代码:

Public Sub btnCalcQuotations_Click()
Dim stSQL As String
Dim rstTemp As ADODB.Recordset
Dim RealWeight As Double, Volume As Double

stSQL = "SELECT TOP 2 * FROM (SELECT C.Name, format(O.Freight," & Chr(34) & "#0.00" & Chr(34) & ") as Freight, format((O.Forwarding + C.FixedFee + O.Freight*C.MgmtSurcharge/100 + O.Freight*C.FixedFuelSurcharge/100+(Switch(1.85<C.FuelReferencePrice,C.FuelReferencePrice , 1.85 > C.FuelReferencePrice, 1.85)" & _
            "- C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight)," & Chr(34) & "#0.00" & Chr(34) & ") as AdditionalCosts,format((O.Freight+(O.Forwarding + C.FixedFee + O.Freight*C.MgmtSurcharge/100 + O.Freight*C.FixedFuelSurcharge/100+ (Switch(1.85<C.FuelReferencePrice,C.FuelReferencePrice , 1.85 > C.FuelReferencePrice, 1.85)" & _
            "- C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight))," & Chr(34) & "#0.00" & Chr(34) & ") as TotalCost,W.WeightMin, W.WeightMax, C.FuelReferencePrice,C.IndexedFuelSurcharge FROM Temp_TaxableWeights AS T INNER JOIN (Weight_Ranges AS W INNER JOIN (Carriers AS C INNER JOIN [OBPT_Groupage&LorryOwner] AS O ON C.[ID] = O.[CarrierID])" & _
            "ON W.ID = O.WeightRangeID) ON T.CarrierID = C.ID WHERE (((W.WeightMin) < T.TaxableWeight) And ((W.WeightMax) >= T.TaxableWeight) And ((O.DistrictID) = 35)) And O.RateTypeID = 4 UNION SELECT C.Name, format(O.Freight*T.TaxableWeight," & Chr(34) & "#0.00" & Chr(34) & ") as Freight, format((O.Forwarding + C.FixedFee + O.Freight*T.TaxableWeight*C.MgmtSurcharge/100 +" & _
            "O.Freight*T.TaxableWeight*C.FixedFuelSurcharge/100+ (Switch(1.85<C.FuelReferencePrice,C.FuelReferencePrice , 1.85 > C.FuelReferencePrice, 1.85) - C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight*T.TaxableWeight)," & Chr(34) & "#0.00" & Chr(34) & ") as AdditionalCosts,format((O.Freight*T.TaxableWeight +O.Forwarding + C.FixedFee + O.Freight*T.TaxableWeight*C.MgmtSurcharge/100 + O.Freight*T.TaxableWeight*C.FixedFuelSurcharge/100+" & _
            "(Switch(1.85<C.FuelReferencePrice,C.FuelReferencePrice, 1.85 > C.FuelReferencePrice, 1.85)- C.FuelReferencePrice)/1.85*C.IndexedFuelSurcharge*O.Freight*T.TaxableWeight)," & Chr(34) & "#0.00" & Chr(34) & ") as TotalCost,W.WeightMin, W.WeightMax, C.FuelReferencePrice, C.IndexedFuelSurcharge FROM Temp_TaxableWeights AS T INNER JOIN (Weight_Ranges AS W INNER JOIN (Carriers AS C INNER JOIN [OBPT_Groupage&LorryOwner] AS O ON C.[ID] = O.[CarrierID]) ON W.ID = O.WeightRangeID) ON T.CarrierID = C.ID" & _
            "WHERE (((W.WeightMin) < T.TaxableWeight) And ((W.WeightMax) >= T.TaxableWeight) And ((O.DistrictID) = 35)) And O.RateTypeID = 8 ORDER BY TotalCost ASC)"
Set rstTemp = Read_Recordset(stSQL)
With rstTemp
    If Not .EOF Then
        r = Application.WorksheetFunction.Match("Trasportatore", Columns(24), 0) + 2
        .MoveFirst
        While Not .EOF
            Cells(r, 24) = !Name
            Cells(r, 25) = !Freight
            Cells(r, 26) = !AdditionalCost
            Cells(r, 27) = !TotalCost
            .MoveNext
        Wend
    End If
End With
End Sub

我无法让它工作,在实际读取数据的那一刻,所以前面的Read_Recordset VBA函数的这一行:

 .Open stSQL1, cnt 'Create the recordset.

它返回给我一个运行时错误,上面写着:

“不支持 JOIN 表达式”(或类似的,我的是意大利语)

我很挣扎,在花费大量时间在 Access 中构建查询之后,我无法忍受无法从 Excel 启动它的想法。 有什么建议或替代解决方案吗? 任何事情都会非常感激。 问候,

马可

【问题讨论】:

使用 Access 特定函数的 SQL 不会通过 Excel 用于读取 Access 数据库的“普通”查询机制运行。我怀疑这是你的问题。 mmmm...谢谢答案。但是您所说的访问特定功能是什么?也许开关?我可以用一些标准 SQL 代码替换它吗? 我不认为 ADO 可以使用 Access 函数,所以我怀疑这是问题所在。我想我可能会通过尝试简化查询来解决这个问题。一个复杂的查询可能会让我晚上睡不着。 假设没有办法简化查询。我建立了数据库,期待进行这种查询,因为这是我想到的唯一方法。 “开关”功能是访问特定的?我可以用一个特定的案例来代替它吗? 我搞定了!!我尝试了一个更简单的查询但具有相同的结构(因此每个查询中都有 SWITCH 函数的 2 个不同查询的 UNION)并且正在工作,所以我在更大的查询中解决了问题:缺少“”(空格)字符,哈哈!无论如何谢谢! 【参考方案1】:

为了将来参考,可以从 ADO 激活 Access 中内置的查询,如下所示(注意:如果您的查询确实有参数,则只需要附加参数。)

    'Create Variables
    dim cmd as new adodb.command, cn as new adodb.connection

    'Establish db connection
    cn.connectionstring = "Data Source=MyDataSource.accdb;Provider=Microsoft.ACE.OLEDB.12.0;"
    cn.Open

    'Create and assign parameter values
    Set parStartDate = .CreateParameter("Enter Start Date", adDate, adParamInput, 10)
    Set parEndDate = .CreateParameter("Enter End Date", adVarChar, adParamInput, 10)
        parStartDate.Value = sStartDate
        parEndDate.Value = sEndDate

    'Set up command attribute, assign param objects to command object 
    'so that these are passed through as well

    With cmd
    .CommandType = adCmdStoredProc
    .Parameters.Append parStartDate
    .Parameters.Append parEndDate
    Set .ActiveConnection = cn
    .CommandText = "MyQueryName"
    .Execute
    End With

这将适用于访问数据库中的更改的操作查询,但如果您需要返回记录集,则只需像往常一样打开记录集,但使用命令对象而不是 sql 打开它字符串:

rs.Open(cmd)

希望这会有所帮助:)

【讨论】:

以上是关于从 Excel VBA 运行嵌套的 Access SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

使用 VBA 从 Access 运行后从任务管理器中删除 Excel 任务

Access(VBA)从Excel工作簿打开并运行宏

从 Excel VBA 运行工作参数化 Access SQL 查询 (INSERT INTO) 时出现“需要对象”错误

VBA - 从 Excel 更新 Access 文件的链接表

使用 VBA 从 Excel 打开和关闭 Access 数据库 - Access 宏错误

如果从 Excel vba 执行 vbs,则从 vbs 运行 Access ImportExport 失败 - 知道吗?