VBA以编程方式根据用户输入更改查询SQL

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (83)

我正在尝试创建一个基于用户输入(在Excel工作表上)的宏将从我在Access中创建的查询中提取数据。为了使它只提取适用的数据行(行),需要相应地编辑WHERE语句。我已经调整了上一个问题中的以下代码,但是当我尝试替换SQL时,我遇到了问题。

Private Sub CommandButton4_Click()
Const DbLoc As String = "MYfilepath"
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim wb1 As Workbook, ws1 As Worksheet, ws2 As Worksheet, SQL As String, recCount As Long
Set wb1 = Workbooks("mytool.xlsm")
Set ws1 = wb1.Sheets("Inputs")
Set ws2 = wb1.Sheets("raw")
Set db = OpenDatabase(DbLoc)
Set userinput = ws1.Range("D6")


SQL = "SELECT Dock_Rec_Problems.Merch_Name, Dock_Rec_Problems.Vendor_Error_Code, Dock_Rec_Problems.DC, Dock_Rec_Problems.Vendor_ID_IP, Dock_Rec_Problems.Vendor_Name, Dock_Rec_Problems.PO_Number, Dock_Rec_Problems.SKU_No, Dock_Rec_Problems.Item_Description, Dock_Rec_Problems.Casepack, Dock_Rec_Problems.Retail, Dock_Rec_Problems.Num_Of_Cases, Dock_Rec_Problems.Dock_Rec_Problems_DGID"
SQL = SQL & "FROM Dock_Rec_Problems;"
SQL = SQL & "WHERE [Dock_Rec_Problems_DGID] =" & userinput

Set rs = db.OpenRecordset(SQL, dbOpenSnapshot)
If rs.RecordCount = 0 Then
    MsgBox "Not found in database", vbInformation + vbOKOnly, "No Data"
    GoTo SubExit
End If

ws2.Range("A1").CopyFromRecordset rs

SubExit:
On Error Resume Next
    Application.Cursor = xlDefault
    rs.Close
    Set rs = Nothing
Exit Sub

End Sub

如果有什么我可以清理的,请告诉我...谢谢!

原始查询SQL

SELECT Dock_Rec_Problems.Merch_Name, Dock_Rec_Problems.Vendor_Error_Code, 
Dock_Rec_Problems.DC, Dock_Rec_Problems.Vendor_ID_IP, 
Dock_Rec_Problems.Vendor_Name, Dock_Rec_Problems.PO_Number, 
Dock_Rec_Problems.SKU_No, Dock_Rec_Problems.Item_Description, 
Dock_Rec_Problems.Casepack, Dock_Rec_Problems.Retail, 
Dock_Rec_Problems.Num_Of_Cases, Dock_Rec_Problems.Dock_Rec_Problems_DGID
FROM Dock_Rec_Problems;

单输入SQL

SELECT Dock_Rec_Problems.Merch_Name, Dock_Rec_Problems.Vendor_Error_Code, Dock_Rec_Problems.DC, Dock_Rec_Problems.Vendor_ID_IP, Dock_Rec_Problems.Vendor_Name, Dock_Rec_Problems.PO_Number, Dock_Rec_Problems.SKU_No, Dock_Rec_Problems.Item_Description, Dock_Rec_Problems.Casepack, Dock_Rec_Problems.Retail, Dock_Rec_Problems.Num_Of_Cases, Dock_Rec_Problems.Dock_Rec_Problems_DGID
FROM Dock_Rec_Problems
WHERE (((Dock_Rec_Problems.Dock_Rec_Problems_DGID)="D040323000"));

双输入SQL

SELECT Dock_Rec_Problems.Merch_Name, Dock_Rec_Problems.Vendor_Error_Code, Dock_Rec_Problems.DC, Dock_Rec_Problems.Vendor_ID_IP, Dock_Rec_Problems.Vendor_Name, Dock_Rec_Problems.PO_Number, Dock_Rec_Problems.SKU_No, Dock_Rec_Problems.Item_Description, Dock_Rec_Problems.Casepack, Dock_Rec_Problems.Retail, Dock_Rec_Problems.Num_Of_Cases, Dock_Rec_Problems.Dock_Rec_Problems_DGID
FROM Dock_Rec_Problems
WHERE (((Dock_Rec_Problems.Dock_Rec_Problems_DGID)="D040323000")) OR (((Dock_Rec_Problems.Dock_Rec_Problems_DGID)="D040323012"));
提问于
用户回答回答于

由于用户输入的大小是开放式的,因此请考虑使用MS Access中保存的临时表,其结构与查询完全相同(可以使用:)构建SELECT * INTO temp_table FROM myquery。然后,每次调用Excel宏:

  1. 使用清洁临时表DELETE
  2. 通过用户输入Excel范围的单元格迭代,将所需的行追加到表中INSERT INTO...SELECT
  3. 从临时表创建记录集。

再一次,这是SQL参数化的主要用例,特别是因为查询接收用户输入。聪明的恶意用户可能会清理您的数据库!但至少,代码可以说更易于维护。因为您正在使用DAO,所以请考虑QueryDefs将参数值绑定到准备好的已保存查询,然后绑定到记录集。

SQL (另存为MS Access存储操作查询)

PARAMETERS [userparam] TEXT(255);
INSERT INTO Excel_Table (Merch_Name, Vendor_Error_Code, DC, Vendor_ID_IP,
                         Vendor_Name, PO_Number, SKU_No, Item_Description,
                         Casepack, Retail, Num_Of_Cases, Dock_Rec_Problems_DGID)
SELECT d.Merch_Name, d.Vendor_Error_Code, d.DC, d.Vendor_ID_IP, 
       d.Vendor_Name, d.PO_Number, d.SKU_No, d.Item_Description, 
       d.Casepack, d.Retail, d.Num_Of_Cases, d.Dock_Rec_Problems_DGID
FROM Dock_Rec_Problems d
WHERE d.[Dock_Rec_Problems_DGID] = [userparam];

VBA

...
Dim qdef As DAO.QueryDef
Dim cel As Range

Set qdef = db.QueryDefs("mySavedQuery")

' CLEAN OUT TEMP EXCEL TABLE
db.Execute "DELETE FROM Excel_Table"

' ITERATIVELY APPEND TO EXCEL TABLES
For Each cel In userinput.Cells
    qdef!userparam = cel.Value            ' BIND PARAM
    qdef.Execute dbFailOnError            ' EXECUTE ACTION
Next cel

' OPEN RECORDSET TO TABLE
Set rs = db.OpenRecordset("Excel_Table", dbOpenSnapshot)

If rs.RecordCount = 0 Then
    MsgBox "Recieving problem not found in database", vbInformation+vbOKOnly, "No Data"
    GoTo SubExit
End If

ws2.Range("A1").CopyFromRecordset rs
.......
用户回答回答于

您显示的代码存在一些问题。例如,在将strNewFields 变量设置为任何内容之前,试一试这个:

strNewSQL = strNewSQL & Replace(WHERE_FIELDS, "<INSERT FIELDS>", strNewFields)

此时strNewFields完全是空白,但您正在尝试替换。

我会建议:

  1. 改变你的WHERE_FIELDS Const Const WHERE_FIELDS As String = "WHERE " _ & "(((Dock_Rec_Problems.Dock_Rec_Problems_DGID) = <INSERT FIELDS>)); "Const WHERE_FIELDS As String = "WHERE " _ & " [Dock_Rec_Problems].[Dock_Rec_Problems_DGID] IN (<INSERT FIELDS>); " 我发现这比所有嵌套括号更容易阅读,它删除了IN()语句优先的等号。
  2. 现在你想用他们给你的任何输入填充strNewFields变量。可能使用a Do While Loop来迭代INPUTS。每个输入都添加到strNewFields变量中。 Dim rs as Recordset Set RS = currentdb.mydataset ' You need to modify this line rs.Open strNewFields = strNewFields & "'" & rs("InputFieldName") & "'" rs.MoveNext Do While rs.EOF = False strNewFields = strNewFields & ",'" & rs("InputFieldName") & "'" Loop strNewFields = StrNewFields & ")"
  3. 现在你已经填充了strNewFields,你可以简单地运行你的replace() Replace(WHERE_FIELDS, "<INSERT FIELDS>", strNewFields)

您需要查看设置变量的顺序,如上所述,您有一些事件顺序问题。

扫码关注云+社区

领取腾讯云代金券