Я пытаюсь выполнить поиск домена в vba примерно так:
DLookup("island", "villages", "village = '" & txtVillage & "'")
Это отлично работает до тех пор, пока txtVillage не станет чем-то вроде залива Диллона, когда апостроф принимается как одинарная кавычка, и я получаю ошибку во время выполнения.
Я написал тривиальную функцию, которая избегает одинарных кавычек - она заменяет "'" на "' '". Кажется, что это случается довольно часто, но я не могу найти никакой ссылки на встроенную функцию, которая делает то же самое. Я что-то упустил?


Это хуже, чем ты думаешь. Подумайте, что произойдет, если кто-то введет такое значение, а вы ничего не избежите:
'); DROP TABLE [YourTable]
Не очень.
Причина, по которой нет встроенной функции, позволяющей просто избежать апострофа, заключается в том, что правильный способ справиться с этим - использовать параметры запроса. Для запроса в стиле Ole / Access вы должны установить это как строку запроса:
DLookup("island", "village", "village = ? ")
А затем установите параметр отдельно. Однако я не знаю, как вы собираетесь устанавливать значение параметра из vba.
Используйте подготовленные заявления. См. stackoverflow.com/questions/6572448/… или technet.microsoft.com/en-us/library/aa905910(v=sql.80).aspx. Эта статья посвящена SQL Server, но я подозреваю, что она может работать и для Access.
Кстати, вот моя функция 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
это то, о чем я действительно просил. Я не часто использую vba, и файлы справки мне не очень помогают! ваше здоровье.
Функция «Заменить» должна помочь. На основе вашего кода выше:
DLookup("island", "villages", "village = '" & Replace(txtVillage, "'", "''") & "'")
Обратите внимание, что Replace (), похоже, не существует в более старых версиях Access (например, Access 97).
Replace () был представлен в Access 2000.
@David W. Fenton: Просто добавляю примечание, чтобы устранить неоднозначность вашего использования Access2000, если вас это устраивает :) Функция Replace () была введена в VBA6. VBA6 был введен в пользовательский интерфейс Access начиная с Access2000. SQL ядра СУБД Access не поддерживает функцию «замены» ни изначально, ни через службы Jet Expression Services. Использование Replace () в SQL работает только при использовании пользовательского интерфейса Access (хотя, признаюсь, я не знаю как), в противном случае будет отображаться синтаксическая ошибка.
... Следовательно, лучше избегать использования Replace () в постоянных объектах базы данных, таких как запросы / представления / процедуры и особенно правила проверки и ограничения CHECK.
Я считаю, что для доступа можно использовать Chr $ (34) и, к счастью, иметь внутри одинарные кавычки / апострофы. например
DLookup("island", "villages", "village = " & chr$(34) & nonEscapedString & chr$(34))
Хотя тогда вам придется избегать chr $ (34) (")
Вы можете использовать функцию «Заменить».
Dim escapedString as String
escapedString = Replace(nonescapedString, "'", "''")
Хотя функции сокращенной области, такие как DLookup, заманчивы, у них есть свои недостатки. Эквивалентный Jet SQL выглядит примерно так:
SELECT FIRST(island)
FROM villages
WHERE village = ?;
Если у вас есть несколько подходящих кандидатов, он выберет «первого», определение «первого» зависит от реализации (механизм SQL) и не определено для механизма IIRC Jet / ACE. Вы знаете, какой из них будет первым? Если вы этого не сделаете, то держитесь подальше от DLookup :)
[Для интереса ответом для Jet / ACE будет либо минимальное значение на основе индекса clusterd на момент последнего сжатия файла базы данных, либо первое (допустимое время) вставленное значение, если база данных никогда не уплотнялась. Кластерный индекс, в свою очередь, определяется PRIAMRY KEY, если он присутствует, в противном случае - ограничение UNIQUE или индекс, определенный для столбцов NOT NULL, в противном случае - первая (допустимое время) вставленная строка. Что если для столбцов NOT NULL определено более одного ограничения или индекса UNIQUE, какой из них будет использоваться для кластеризации? Понятия не имею! Надеюсь, вы понимаете, что «первый» непросто определить, даже если вы знаете, как это сделать!]
Я также видел совет от Microsoft избегать использования агрегатных функций домена с точки зрения оптимизации:
Информация о производительности запросов в базе данных 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
);
Таким образом, вы можете использовать собственные функции движка - или, по крайней мере, его поставщиков данных - для экранирования всех символов (а не только двойных и одинарных кавычек) по мере необходимости.
Лучше всего использовать параметризованные запросы, такие как предложил Джоэл Кохорн, вместо того, чтобы выполнять конкатенацию в строке запроса. Во-первых, позволяет избежать определенных рисков для безопасности, во-вторых, я вполне уверен, что для этого нужно убежать в собственные руки, и вам не о чем беспокоиться.
Для тех, у кого проблемы с одиночными кавычками и функцией замены, эта строка может спасти вас ^ 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 & "";"
Да, теперь вместо проблемы с апострофом у вас есть проблема с двойными кавычками: - /
Но тогда должно быть так (с еще одной двойной кавычкой):
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) & "';"
Как именно выставить параметр отдельно?