我正在尝试使用以下内容在 vba 中进行域查找:
DLookup("island", "villages", "village = '" & txtVillage & "'")
这工作正常,直到 txtVillage 类似于狄龙湾,当撇号被视为单引号时,我得到一个运行时错误。
我写了一个简单的函数来转义单引号 - 它用“''”替换“'”。这似乎是经常出现的事情,但我找不到任何对具有相同功能的内置函数的引用。我错过了什么吗?
“替换”功能应该可以解决问题。根据您上面的代码:
DLookup("island", "villages", "village = '" & Replace(txtVillage, "'", "''") & "'")
它比你想象的还要糟糕。想想如果有人输入这样的值会发生什么,而你没有逃脱任何东西:
'); DROP TABLE [YourTable]
不漂亮。
没有内置函数来简单地转义撇号的原因是因为处理这个问题的正确方法是使用查询参数。对于 Ole/Access 样式查询,您可以将其设置为查询字符串:
DLookup("island", "village", "village = ? ")
然后单独设置参数。不过,我不知道您如何从 vba 设置参数值。
尽管 DLookup 等速记域函数很诱人,但它们也有其缺点。等效的 Jet SQL 类似于
SELECT FIRST(island)
FROM villages
WHERE village = ?;
如果您有多个匹配的候选者,它将选择“第一个”,“第一个”的定义取决于 Jet/ACE 引擎 IIRC 的实现(SQL 引擎)并且未定义。你知道哪个会是第一个吗?如果您不这样做,请避开 DLookup :)
[出于兴趣,Jet/ACE 的答案将是基于上次压缩数据库文件时的聚集索引的最小值,或者如果数据库从未压缩过,则为第一个(有效时间)插入值。聚集索引反过来由 PRIAMRY KEY 确定,否则为在 NOT NULL 列上定义的唯一约束或索引,否则是第一个(有效时间)插入的行。如果在 NOT NULL 列上定义了多个 UNIQUE 约束或索引,哪一个将用于集群?我不知道!我相信你明白“第一”并不容易确定,即使你知道如何确定!]
我还看到了微软的建议,从优化的角度避免使用域聚合函数:
有关 Access 数据库中查询性能的信息 http://support.microsoft.com/kb/209126
“避免使用域聚合函数,例如 DLookup 函数……Jet 数据库引擎无法优化使用域聚合函数的查询”
如果您选择使用查询重写,则可以利用 PARAMETERS 语法,或者您可能更喜欢 Jet 4.0/ACE PROCEDURE 语法,例如
CREATE PROCEDURE GetUniqueIslandName
(
:village_name VARCHAR(60)
)
AS
SELECT V1.island_name
FROM Villages AS V1
WHERE V1.village_name = :village_name
AND EXISTS
(
SELECT V2.village_name
FROM Villages AS V2
WHERE V2.village_name = V1.village_name
GROUP
BY V2.village_name
HAVING COUNT(*) = 1
);
这样,您可以根据需要使用引擎自己的功能(或至少是其数据提供者的功能)来转义所有字符(不仅仅是双引号和单引号)。
我相信访问可以使用 Chr$(34) 并且很高兴在里面有单引号/撇号。
例如
DLookup("island", "villages", "village = " & chr$(34) & nonEscapedString & chr$(34))
虽然那时你必须逃避 chr$(34) (")
您可以使用替换功能。
Dim escapedString as String
escapedString = Replace(nonescapedString, "'", "''")
Joel Coehoorn 建议的参数化查询是可行的方法,而不是在查询字符串中进行连接。首先 - 避免某些安全风险,其次 - 我有理由确定它需要逃到引擎自己的手中,你不必担心。
但是,它应该是这样的(每个多一个双引号):
sSQL = "SELECT * FROM tblTranslation WHERE fldEnglish=""" & myString & """;"
或者我更喜欢什么:
制作一个函数来转义单引号,因为用“[]”“转义”将不允许您的字符串中出现这些字符......
Public Function fncSQLStr(varStr As Variant) As String
If IsNull(varStr) Then
fncSQLStr = ""
Else
fncSQLStr = Replace(Trim(varStr), "'", "''")
End If
End Function
我将这个函数用于我的所有 SQL 查询,例如 SELECT、INSERT 和 UPDATE(以及 WHERE 子句中......)
strSQL = "INSERT INTO tbl" &
" (fld1, fld2)" & _
" VALUES ('" & fncSQLStr(str1) & "', '" & fncSQLStr(Me.tfFld2.Value) & "');"
或者
strSQL = "UPDATE tbl" & _
" SET fld1='" & fncSQLStr(str1) & "', fld2='" & fncSQLStr(Me.tfFld2.Value) & "'" & _
" WHERE fld3='" & fncSQLStr(str3) & "';"
顺便说一下,这是我的 EscapeQuotes 函数
Public Function EscapeQuotes(s As String) As String
If s = "" Then
EscapeQuotes = ""
ElseIf Left(s, 1) = "'" Then
EscapeQuotes = "''" & EscapeQuotes(Mid(s, 2))
Else
EscapeQuotes = Left(s, 1) & EscapeQuotes(Mid(s, 2))
End If
End Function
对于单引号和替换功能有问题的人,这行可能会节省你的时间^o^
Replace(result, "'", "''", , , vbBinaryCompare)
在可能带有撇号的标准周围加上括号。
就像是:
DLookup("island", "villages", "village = '[" & txtVillage & "]'")
它们可能需要在单引号之外或在 txtVillage 周围,例如:
DLookup("island", "villages", "village = '" & [txtVillage] & "'")
但是,如果您找到正确的组合,它将处理撇号。
基思乙
我的解决方案要简单得多。最初,我使用这个 SQL 表达式来创建 ADO 记录集:
Dim sSQL as String
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish='" & myString & "';"
当myString
其中有一个撇号时,比如 Int'l Electrics,我的程序就会停止。使用双引号解决了这个问题。
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish="" & myString & "";"