将 Excel 数据“日期范围”传递给 SQL 查询
Posted
技术标签:
【中文标题】将 Excel 数据“日期范围”传递给 SQL 查询【英文标题】:Pass Excel Data "Date Range" to SQL Query 【发布时间】:2021-05-19 20:22:00 【问题描述】:我有一个 115 行的大型 SQL 代码,我的任务是放入 Excel 中,并允许使用两个单元格的用户更改 SQL 代码的日期范围并显示结果。我已经尝试了其他几个主题,但没有成功。我正在使用 Microsoft 365。每个用户在其 PC 上都有一个名为“Reports55”的 ODBC 连接 在 excel 中,我有两个选项卡(摘要)和(SQLData)。在(摘要)选项卡上,单元格 A2 是 1StartDate,单元格 B2 是 2EndDate。 (SQLDate) 选项卡是显示 SQL 查询数据的地方。任何帮助将不胜感激。
技能水平
VBA:初学者(请为这个老家伙哑巴) SQL:中级 Excel:中级DECLARE @1StartDate AS DATE
DECLARE @2EndDate AS DATE
SET @1StartDate = '2020-04-27'
SET @2EndDate = '2021-05-27'
SELECT
'SHIP' AS RPT ,
SO_Detail_Ext.LateReason ,
(SO_Detail.ordnum_28 + SO_Detail.linnum_28 + SO_Detail.delnum_28) AS "Order",
SO_Detail.custid_28 AS "CustID",
SO_Detail.Prtnum_28 AS "Part",
CONVERT(VARCHAR(10), SO_Master.ORDDTE_27, 101) AS "OrdDte",
CONVERT(VARCHAR(10), SO_Detail.Shpdte_28, 101) AS "Shpdte",
ExactMAX.dbo.NumShopDays(SO_Master.ORDDTE_27,SO_Detail.shpdte_28) AS "LT_ToShip",
CONVERT(VARCHAR(10), SO_Detail.ORGDUE_28, 101) AS "OrgDue" ,
CONVERT(VARCHAR(10), SO_Detail.Curdue_28, 101) AS "Curdue_PDSL",
CONVERT(VARCHAR(10), SO_Detail.CUSDUE_28, 101) AS "Custdue_RDSL",
SO_Detail.Price_28 AS "Price",
SO_Detail.Shpqty_28 AS "ShpQty",
Customer_Master.udfkey_23 AS "UDFKey",
Customer_Master.Slster_23 AS "Territory",
Account_Types.DESCRPTN_104 AS "AccType",
Customer_Master.SLSTER_23 AS "WordArea" , --'Undefined World Area'
Customer_Master_Ext.CustomerClass ,
(SO_Detail.Price_28 * SO_Detail.Shpqty_28) AS "Extended",
CAST('1' AS INTEGER) AS "Shipped",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.Curdue_28 THEN 1 ELSE 0 END AS "PDSL_OnTime",
CASE
WHEN SO_Detail_Ext.LateReason = 'SSI' THEN '1' --Customer caused late order
WHEN SO_Detail.shpdte_28 <= SO_Detail.Cusdue_28 THEN 1 ELSE 0 END AS "RDSL_OnTime",
CASE WHEN Customer_Master_Ext.CustomerClass = 'TRADE'
THEN 1 ELSE 0 END AS "Trade_Shipped", --01 Total Trade Lines Shipped
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.Curdue_28
AND Customer_Master_Ext.CustomerClass = 'TRADE' THEN 1 ELSE 0 END AS "Trade_Ontime_PDSL",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.CUSDUE_28
AND Customer_Master_Ext.CustomerClass = 'TRADE' THEN 1 ELSE 0 END AS "Trade_Ontime_RDSL",
CASE WHEN Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) = '1' THEN 1 ELSE 0 END AS "Trade_Exp_Shipped",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.CUSDUE_28
AND Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) = '1' THEN 1 ELSE 0 END AS "Trade_EXP_Ontime_RDSL",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.Curdue_28
AND Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) = '1' THEN 1 ELSE 0 END AS "Trade_EXP_Ontime_PDSL",
CASE WHEN Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) <> '1' THEN 1 ELSE 0 END AS "Trade_MTO_Shipped",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.CUSDUE_28
AND Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) <> '1' THEN 1 ELSE 0 END AS "Trade_MTO_Ontime_RDSL",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.Curdue_28
AND Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) <> '1' THEN 1 ELSE 0 END AS "Trade_MTO_Ontime_PDSL",
CASE WHEN Customer_Master_Ext.CustomerClass IN ('INTERCO', 'INTRA-CO')
THEN 1 ELSE 0 END AS "Intraco_Shipped", -- 03 Intraco Lines Shipped
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.Curdue_28
AND Customer_Master_Ext.CustomerClass IN ('INTERCO', 'INTRA-CO')
THEN 1 ELSE 0 END AS "Intraco_OnTime_PDSL",
CASE
WHEN SO_Detail_Ext.LateReason = 'SSI' AND Customer_Master_Ext.CustomerClass IN ('INTERCO', 'INTRA-CO') THEN '1' --Customer caused late order
WHEN SO_Detail.Shpdte_28 <= SO_Detail.CUSDUE_28
AND Customer_Master_Ext.CustomerClass IN ('INTERCO', 'INTRA-CO')
THEN 1 ELSE 0 END AS "Intraco_Ontime_RDSL",
CASE WHEN isnull(Part_Master_Ext.ExpressShip, 0) = '1'
THEN 1 ELSE 0 END AS "EXP_Shipped",
CASE WHEN SO_Detail.Shpdte_28 <= SO_Detail.CURDUE_28
AND isnull(Part_Master_Ext.ExpressShip, 0) = '1' THEN 1 ELSE 0 END AS "EXP_Ontime_PDSL",
CASE
WHEN SO_Detail_Ext.LateReason = 'SSI' AND isnull(Part_Master_Ext.ExpressShip, 0) = '1' THEN '1'
WHEN SO_Detail.Shpdte_28 <= SO_Detail.CUSDUE_28
AND isnull(Part_Master_Ext.ExpressShip, 0) = '1' THEN 1 ELSE 0 END AS "EXP_Ontime_RDSL" ,
ExactMAX.dbo.NumShopDays(SO_Master.ORDDTE_27,SO_Detail.shpdte_28) AS "AVG_LT_Days" ,
ExactMAX.dbo.NumShopDays(SO_Master.ORDDTE_27,SO_Detail.Cusdue_28) AS "RDSL_AVG_LT_Days" ,
ExactMAX.dbo.NumShopDays(SO_Master.ORDDTE_27,SO_Detail.Curdue_28) AS "PDSL_AVG_LT_Days" ,
CASE WHEN Customer_Master_Ext.CustomerClass = 'TRADE'
AND isnull(Part_Master_Ext.ExpressShip, 0) <> '1'
THEN ExactMAX.dbo.NumShopDays(SO_Master.ORDDTE_27,SO_Detail.shpdte_28)
ELSE NULL END AS "Trade_MTO_AVGLT" ,
CASE WHEN Customer_Master_Ext.CustomerClass IN ('INTERCO', 'INTRA-CO')
THEN ExactMAX.dbo.NumShopDays(SO_Master.ORDDTE_27,SO_Detail.shpdte_28)
ELSE NULL END AS "Intraco_AVG_LT" ,
CAST('0' AS FLOAT) AS "Trade_Backlog_PDSL" , --09 Trade $$ Past Due Backlog by PDSL
CAST('0' AS FLOAT) AS "Trade_Backlog_RDSL" , --10 Trade $$ Past Due Backlog by RDSL
CASE WHEN Customer_Master_Ext.CustomerClass = 'TRADE'
THEN (SO_Detail.Price_28 * SO_Detail.Shpqty_28)
ELSE NULL
END AS "Trade_Sales$$" ,
NULL AS "Total_PastDue_Dollars_RDSL" ,
NULL AS "Total_PastDue_Lines_RDSL" ,
NULL AS "Total_PastDue_Dollars_PDSL" ,
NULL AS "Total_PastDue_Lines_PDSL" ,
SO_Detail.CreatedBy ,
SO_Detail.CreationDate ,
Part_Sales.CRTLTO_29
FROM
ExactMAX.dbo.SO_Detail LEFT JOIN
ExactMAX.dbo.Part_Sales ON
SO_Detail.PRTNUM_28 = Part_Sales.PRTNUM_29 LEFT JOIN
ExactMAX.dbo.SO_Master ON
SO_Detail.ORDNUM_28=SO_Master.ORDNUM_27 LEFT JOIN
ExactMAX.dbo.part_Master ON
SO_Detail.prtnum_28 = part_master.prtnum_01 LEFT JOIN
ExactMAX.dbo.Account_Types ON
Part_Master.ACTTYP_01=Account_Types.ACTTYP_104 LEFT JOIN
ExactMAX.dbo.CUSTOMER_MASTER ON
SO_Detail.CUSTID_28 = CUSTOMER_MASTER.CUSTID_23 LEFT JOIN
ExactMAX.dbo.Part_Master_Ext ON
SO_Detail.prtnum_28 = part_master_ext.prtnum_01 LEFT JOIN
ExactMAX.dbo.Customer_Master_Ext ON
Customer_Master.CUSTID_23 = Customer_Master_Ext.CUSTID_23 LEFT JOIN
ExactMAX.dbo.SO_Detail_Ext ON
SO_Detail_ext.ORDER_LIN_DEL = (SO_Detail.ORDNUM_28 + SO_Detail.LINNUM_28 + SO_Detail.DELNUM_28)
WHERE
SO_Detail.Shpdte_28 BETWEEN @1StartDate AND @2EndDate
AND SO_Detail.Status_28 IN ('4', '5')
AND Part_Master.Acttyp_01 <> ''
AND Part_Master.TYPE_01 IN ('A', 'C', 'P', 'S', 'X')
【问题讨论】:
This 文章应该可以帮助您。 【参考方案1】:考虑参数化查询的以下步骤:
SQL
将长而复杂的 SQL 保存在 .sql
文本文件中。以您需要的任何方式使用换行符和缩进进行格式化。保存在与工作簿相同的文件夹或集中位置,供所有用户在 VBA 中阅读。
删除 DECLARE
和 SET
行,仅保留单个 SELECT
命令。
在 SELECT
查询中,将每个 @
变量替换为 qmarks ?
,以便在 VBA 中进行参数化。
WHERE
SO_Detail.Shpdte_28 BETWEEN ? AND ?
VBA
在工作簿模块或独立模块中启动 VBA Sub
子例程。
设置与数据库的 ADO ODBC 连接。许多在线示例和教程。
将 SQL 查询读入字符串变量:
' READ SQL QUERY FROM FILE INTO STRING
With CreateObject("Scripting.FileSystemObject")
strSQL = .OpenTextFile("C:\path\to\my\SQL\Query.sql", 1).readall
End With
使用连接对象和查询打开一个 ADO 命令对象。从它们位于工作簿中的任何单元格创建并绑定两个日期参数。然后执行命令渲染一个 ADO 记录集。
' DEFINE COMMAND OBJECT
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn ' CONNECTION OBJECT
.CommandType = adCmdText
.CommandText = strSQL ' SQL QUERY
' BIND DATE PARAMETERS FOR ? IN SQL, ASSUMING startDate AND endDate ARE VBA DATES
.Parameters.Append .CreateParameter("dtparam1", adDate, adParamInput, , startDate)
.Parameters.Append .CreateParameter("dtparam2", adDate, adParamInput, , endDate)
' BIND OUTPUT TO RECORDSET
Set rs = .Execute
End With
使用Range.CopyFromRecordset 将记录集输出到从最左侧列开始的工作表。请注意:不填充列。循环通过记录集Fields
获取此类列名。
ThisWorkbook.Worksheets("SQLData").Range("A2").CopyFromRecordset rs
(在整个 VBA 代码中,请务必在最顶部使用 Option Explicit
指定 Dim
变量,并结合适当的错误处理,通过将对象设置为 Nothing
(有或没有运行时错误)来关闭记录集和连接并释放 ASO 资源.)
【讨论】:
以上是关于将 Excel 数据“日期范围”传递给 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章