私は次のようなものでVBAでドメインルックアップを実行しようとしています:
DLookup("island", "villages", "village = '" & txtVillage & "'")
これは、アポストロフィが単一引用符であると見なされるtxtVillageがDillon's Bayのようなものになるまで正常に機能し、実行時エラーが発生します。
一重引用符をエスケープする簡単な関数を作成しました-「」を「」に置き換えます。これはかなり頻繁に現れるもののようですが、同じことをする組み込み関数への参照を見つけることができません。私は何かを見逃しましたか?
「置換」機能でうまくいくはずです。上記のコードに基づいて:
DLookup("island", "villages", "village = '" & Replace(txtVillage, "'", "''") & "'")
DLookupなどの省略形のドメイン関数は魅力的ですが、欠点があります。同等のJet SQLは次のようなものです
SELECT FIRST(island)
FROM villages
WHERE village = ?;
一致する候補が複数ある場合、「最初」の候補が選択されます。「最初」の定義は実装(SQLエンジン)に依存し、Jet/ACEエンジンIIRCには未定義です。どちらが最初になるか知っていますか?しない場合は、DLookupを避けてください。
[興味深いことに、Jet/ACEの回答は、データベースファイルが最後に圧縮されたときのクラスター化インデックスに基づく最小値、またはデータベースが一度も圧縮されていない場合は最初の(有効な時間)挿入値になります。クラスター化インデックスは、存在しない場合はPRIAMRY KEYによって決定され、そうでない場合はNOT NULL列に定義されたUNIQUE制約またはインデックス、そうでない場合は最初の(有効な時間)挿入された行。クラスタリングに使用される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
);
このようにして、エンジン自体の機能、または少なくともデータプロバイダーの機能を使用して、必要に応じてすべての文字(二重引用符や単一引用符だけでなく)をエスケープできます。
それはあなたが考えるよりも悪いです。誰かがこのような値を入力し、何もエスケープしていない場合はどうなるか考えてみてください。
'); DROP TABLE [YourTable]
きれいじゃない。
単にアポストロフィをエスケープする組み込み関数がない理由は、これを処理する正しい方法がクエリパラメータを使用することであるからです。 Ole/Accessスタイルのクエリの場合、これをクエリ文字列として設定します。
DLookup("island", "village", "village = ? ")
そして、パラメータを個別に設定します。ただし、vbaからパラメーター値を設定する方法はわかりません。
Joel Coehoornが提案するようなパラメーター化されたクエリは、クエリ文字列で連結を行う代わりに、進むべき道です。 1つ目は特定のセキュリティリスクを回避すること、2つ目はそれがエンジン自身の手にエスケープすることを合理的に確信しており、そのことについて心配する必要はありません。
しかし、それは次のようになります(それぞれ1つずつ二重引用符で囲みます)。
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
SELECT、INSERT、UPDATEなどのすべてのSQLクエリで(そして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) & "';"
アクセスにはChr $(34)を使用でき、喜んで単一引用符/アポストロフィを中に含めることができると思います。
例えば
DLookup("island", "villages", "village = " & chr$(34) & nonEscapedString & chr$(34))
ただし、chr $(34)をエスケープする必要があります( ")
置換機能を使用できます。
Dim escapedString as String
escapedString = Replace(nonescapedString, "'", "''")
アポストロフィが含まれる可能性のある基準を大括弧で囲みます。
何かのようなもの:
DLookup("island", "villages", "village = '[" & txtVillage & "]'")
次のように、単一引用符の外、またはtxtVillageの前後に配置する必要があります。
DLookup("island", "villages", "village = '" & [txtVillage] & "'")
しかし、正しい組み合わせを見つければ、アポストロフィが処理されます。
キースB
単一引用符と置換機能に問題がある場合は、この行で1日を節約できます^ o ^
Replace(result, "'", "''", , , vbBinaryCompare)
ちなみに、これが私の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