在Access/VBA中构建SQL字符串

在Access/VBA中构建SQL字符串,sql,ms-access,vba,Sql,Ms Access,Vba,有时,我不得不在VBA中构建一个SQL字符串,并使用Docmd.RunSql()执行它。我总是通过将变量连接到字符串中来构建这些字符串,例如: Dim mysqlstring as String mysqlstring = "INSERT INTO MyTable (Field1, Field2, Field3 ...) VALUES (" mysqlstring = mysqlstring + Me.TextMyField1 + ", " 'parameter comments mysqlst

有时,我不得不在VBA中构建一个SQL字符串,并使用
Docmd.RunSql()
执行它。我总是通过将变量连接到字符串中来构建这些字符串,例如:

Dim mysqlstring as String
mysqlstring = "INSERT INTO MyTable (Field1, Field2, Field3 ...) VALUES ("
mysqlstring = mysqlstring + Me.TextMyField1 + ", " 'parameter comments
mysqlstring = mysqlstring + Me.TextMyField2 + ", " 
mysqlstring = mysqlstring + Me.TextMyField3 + ", " 
...
mysqlstring = mysqlstring + ");"
Docmd.RunSql mysqlstring
VBA似乎没有一元串联运算符(如+=),虽然这看起来并不理想,但至少我可以对每个参数进行注释并独立更改它们。它比一个怪物连接的字符串更容易阅读和更改。但构建SQL字符串似乎仍然是一种糟糕的方式。我有一个大约有50个参数在工作,所以有50行
mysqlstring=mysqlstring+…
。不可爱

顺便说一句,这排除了使用行连续体来格式化字符串的可能性,因为存在一个(提示:小于50)。另外,VBA不允许您在行继续之后添加注释,grr

直到最近,我还认为这是构建这些字符串的唯一方法。但最近我看到了一种不同的模式,在我发布答案的字符串(如)中注入参数,我想知道VBA是否有与
parameters.AddWithValue()
等效的方法,或者这是否比字符串串联方法更好。所以我认为这应该有自己的问题。也许我在这里遗漏了什么


请一些Access专家澄清在Access/VBA中构建SQL字符串的最佳实践是什么。

我将使用上述方法,每个参数都在单独的一行上,很好调试和添加

    Private Sub Command0_Click()
Dim rec As Recordset2
Dim sql As String
Dim queryD As QueryDef

    'create a temp query def.
    Set queryD = CurrentDb.CreateQueryDef("", "SELECT * FROM [Table] WHERE Val = @Val")
    'set param vals
    queryD.Parameters("@Val").Value = "T"
    'execute query def
    Set rec = queryD.OpenRecordset
End Sub
然而,如果您确实不喜欢这种方式,那么您可以查看参数查询。灵活性稍差,但在某些情况下稍快

或者另一种方法是定义一个公共函数,用于插入到该表中,并将值作为参数传递给它


然而,我会坚持你所得到的,但是如果VBA能够理解=+

那就太好了。除了@astander所说的,你可以创建一个querydef(带参数)并将其保存为数据库的一部分

e、 g

假设您使用名称
myTableInsert
保存了它,您可以编写如下代码

dim qd as QueryDef
set qd = CurrentDB.QueryDefs("myTableInsert")
qd.Parameters("dtBegin").Value = myTextFieldHavingBeginDate
qd.Parameters("dtEnd").Value = myTextFieldHavingEndDate    
qd.Execute
注意:我没有测试这段代码。但是,我猜应该是这样。

希望这能为您提供足够的信息以开始使用。

我过去做过的一件事是创建一个系统,用于解析SQL代码以查找参数并将参数存储在表中。我会在Access之外编写MySQL查询。然后,我所要做的就是从Access打开文件,每次我想运行它时,它都可以随时更新


这是一个非常复杂的过程,但如果你感兴趣的话,我很乐意在下周回去工作时挖掘代码。

我有一个时间表应用程序,带有一个相当复杂的未绑定劳动交易输入表单。有很多数据验证、速率计算等代码。我决定使用以下内容创建SQL插入/更新字段

变量strSQLInsert、strSQLValues、strSQLUpdate是表单级字符串

以下的许多行:

Call CreateSQLString("[transJobCategoryBillingTypesID]", lngJobCategoryBillingTypesID)
其次是:

If lngTransID = 0 Then
    strSQL = "INSERT into Transactions (" & Mid(strSQLInsert, 3) & ") VALUES (" & Mid(strSQLValues, 3) & ")"
Else
    strSQL = "UPDATE Transactions SET " & Mid(strSQLUpdate, 3) & " WHERE transID=" & lngTransID & ";"
End If

conn.Open
conn.Execute strSQL, lngRecordsAffected, adCmdText
请注意,中线删除了前导“,”。lngTrans是自动编号primamy kay的值

Sub CreateSQLString(strFieldName As String, varFieldValue As Variant, Optional blnZeroAsNull As Boolean)
'    Call CreateSQLString("[<fieldName>]", <fieldValue>)

Dim strFieldValue As String, OutputValue As Variant

    On Error GoTo tagError

    ' if 0 (zero) is supposed to be null
    If Not IsMissing(blnZeroAsNull) And blnZeroAsNull = True And varFieldValue = 0 Then
        OutputValue = "Null"
    ' if field is null, zero length or ''
    ElseIf IsNull(varFieldValue) Or Len(varFieldValue) = 0 Or varFieldValue = "''" Then
        OutputValue = "Null"
    Else
        OutputValue = varFieldValue
    End If

    ' Note that both Insert and update strings are updated as we may need the insert logic for inserting
    '    missing auto generated transactions when updating the main transaction
    ' This is an insert
    strSQLInsert = strSQLInsert & ", " & strFieldName
    strSQLValues = strSQLValues & ", " & OutputValue
    ' This is an update
    strSQLUpdate = strSQLUpdate & ", " & strFieldName & " = " & OutputValue

    On Error GoTo 0
    Exit Sub

tagError:

    MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure CreateSQLString of VBA Document Form_LabourEntry"
    Exit Sub
End Sub
Sub-CreateSQLString(strFieldName作为字符串,varFieldValue作为变量,可选blnZeroAsNull作为布尔值)
'调用CreateSQLString(“[]”,)
Dim strFieldValue作为字符串,OutputValue作为变量
关于错误转到tagError
'如果0(零)应该为null
如果不IsMissing(blnZeroAsNull)且blnZeroAsNull=True且VARFELDVALUE=0,则
OutputValue=“Null”
'如果字段为空,则长度为零或“”
ElseIf IsNull(varFieldValue)或Len(varFieldValue)=0或varFieldValue=“””,则
OutputValue=“Null”
其他的
OutputValue=varFieldValue
如果结束
'请注意,Insert和update字符串都会更新,因为我们可能需要Insert逻辑来进行插入
'更新主事务时缺少自动生成的事务
“这是一个插页
strSQLInsert=strSQLInsert&“,”和strFieldName
strSQLValues=strSQLValues&“,”和OutputValue
这是一个更新
strSQLUpdate=strSQLUpdate&“,”&strFieldName&“=”&OutputValue
错误转到0
出口接头
标记错误:
MsgBox“Error”和Err.Number&(“&Err.Description&”)在VBA文档表单的CreateSQLString过程中
出口接头
端接头
我看到其他海报都在使用Execute方法。DoCmd.RunSQL的问题是它可以忽略错误。以下任一项都将显示查询收到的任何错误消息。如果使用DAO,则使用Currentdb。执行strSQL,dbfailonerror。。对于ADO,请使用CurrentProject.Connection.Execute strCommand、LNGRECORDSAFInfected、adCmdText,然后可以删除docmd.setwarnings行

如果要使用docmd.setwarnings,请确保在任何错误处理代码中也使用True语句。否则,以后可能会发生奇怪的事情,尤其是在您使用应用程序时。例如,如果关闭对象,您将不再收到“是否希望保存更改”消息。这可能意味着不需要的更改、删除或添加将保存到MDB中


此外,两种方法的性能也可能有显著差异。一次发布声明currentdb.execute花费了两秒钟,而docmd.runsql花费了八秒钟。正如其他人所说的那样,首先使用参数可能更好。然而

一、 同样,我们也错过了一个连接操作符,因为我们已经习惯了PHP中的。在一些情况下,我编写了一个函数来实现这一点,尽管不是专门用于连接SQL字符串的。下面是我用于为HTTP GET创建查询字符串的代码:

  Public Sub AppendQueryString(strInput As String, _
       ByVal strAppend As String, Optional ByVal strOperator As String = "&")
    strAppend = StringReplace(strAppend, "&", "&amp;")
    strInput = strInput & strOperator & strAppend
  End Sub
还有一个我称之为的例子:

  AppendQueryString strOutput, "InventoryID=" & frm!InventoryID, vbNullstring
  AppendQueryString strOutput, "Author=" & URLEncode(frm!Author)
…等等

现在,为了构造SQLWHERE子句
  AppendQueryString strOutput, "InventoryID=" & frm!InventoryID, vbNullstring
  AppendQueryString strOutput, "Author=" & URLEncode(frm!Author)
  Public Sub ConcatenateWhere(ByRef strWhere As String, _
      strField As String, intDataType As Integer, ByVal varValue As Variant)
    If Len(strWhere) > 0 Then
       strWhere = strWhere & " AND "
    End If
    strWhere = strWhere & Application.BuildCriteria(strField, _
       intDataType, varValue)
  End Sub
  Dim strWhere As String

  ConcatenateWhere strWhere,"tblInventory.InventoryID", dbLong, 10036
  ConcatenateWhere strWhere,"tblInventory.OtherAuthors", dbText, "*Einstein*"
  Debug.Print strWhere
  strSQL = "SELECT tblInventory.* FROM tblInventory"
  strSQL = strSQL & " WHERE " & strWhere
  tblInventory.InventoryID=10036 AND tblInventory.OtherAuthors Like "*Einstein*"
  Public Sub ConcatenateValues(ByRef strValues As String, _
      intDatatype As Integer, varValue As Variant)
    Dim strValue As String

    If Len(strValues) > 0 Then
       strValues = strValues & ", "
    End If
    Select Case intDatatype
      Case dbChar, dbMemo, dbText
        ' you might want to change this to escape internal double/single quotes
        strValue = Chr(34) & varValue & Chr(34)
      Case dbDate, dbTime
        strValue = "#" & varValue & "#"
      Case dbGUID
        ' this is only a guess
        strValues = Chr(34) & StringFromGUID(varValue) & Chr(34)
      Case dbBinary, dbLongBinary, dbVarBinary
        ' numeric?
      Case dbTimeStamp
        ' text? numeric?
      Case Else
        ' dbBigInt , dbBoolean, dbByte, dbCurrency, dbDecimal, 
        '   dbDouble, dbFloat, dbInteger, dbLong, dbNumeric, dbSingle
        strValue = varValue
    End Select
    strValues = strValues & strValue
  End Sub
Dim db as Database: Set db = Current Db
Dim sql$
sql= "INSERT INTO MyTable (Field1, Field2, Field3 ...Fieldn) " & _
     "VALUES (" & _
     Me.TextMyField1 & _
     "," & Me.TextMyField2 & _
     "," & Me.TextMyField3 & _
     ...
     "," & Me.TextMyFieldn & _
     ");"
db.Execute s
Set db = nothing