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

时间:2018-05-15 21:18:41

标签: vba ms-access

我正在尝试创建一个基于用户输入(在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"));

2 个答案:

答案 0 :(得分:1)

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

  1. 使用DELETE清除临时表。
  2. 通过用户输入Excel范围的单元格,使用INSERT INTO...SELECT将所需的行追加到表格中。
  3. 从临时表创建记录集。
  4. 再一次,这是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
    .......
    

答案 1 :(得分:0)

您显示的代码存在一些问题。例如,在将 strNewFields 变量设置为任何内容之前,尝试使用 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 变量。可能使用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)
    
  4. 您需要查看设置变量的顺序,如上所述,您有一些事件顺序。

    迈克尔