Экранирование ' в Access SQL

Я пытаюсь выполнить поиск домена в vba примерно так:

DLookup("island", "villages", "village = '" & txtVillage & "'")

Это работает нормально, пока txtVillage не станет чем-то вроде залива Диллона, когда апостроф принимается за одинарную кавычку, и я получаю ошибку времени выполнения.

Я написал тривиальную функцию, которая избегает одинарных кавычек - она ​​заменяет "'" на "''". Кажется, это происходит довольно часто, но я не могу найти ссылку на встроенную функцию, которая делает то же самое. Я что-то пропустил?


person inglesp    schedule 14.10.2008    source источник


Ответы (10)


Функция «Заменить» должна помочь. На основе вашего кода выше:

DLookup("island", "villages", "village = '" & Replace(txtVillage, "'", "''") & "'")
person Matt    schedule 14.10.2008
comment
Обратите внимание, что Replace() не существует в более старых версиях Access (например, Access 97). - person apenwarr; 18.01.2009
comment
Функция Replace() появилась в Access 2000. - person David-W-Fenton; 08.12.2009
comment
@David W. Fenton: Просто добавьте примечание, чтобы устранить неоднозначность использования Access2000, если это вас устраивает :) Функция Replace() была введена в VBA6. VBA6 был введен в пользовательский интерфейс Access начиная с Access2000. SQL ядра СУБД Access не поддерживает функцию «замены» ни изначально, ни через службы Jet Expression. Использование Replace() в SQL работает только при использовании пользовательского интерфейса Access (хотя я признаю, что не знаю, как это сделать), в противном случае будет отображаться синтаксическая ошибка. - person onedaywhen; 08.12.2009
comment
... Следовательно, лучше избегать использования Replace() в постоянных объектах базы данных, таких как запросы/представления/процедуры и особенно правила проверки и ограничения CHECK. - person onedaywhen; 08.12.2009

Это хуже, чем вы думаете. Подумайте о том, что произойдет, если кто-то введет такое значение, а вы ничего не экранируете:

'); DROP TABLE [YourTable]

Не красиво.

Причина, по которой нет встроенной функции для простого экранирования апострофа, заключается в том, что правильный способ справиться с этим - использовать параметры запроса. Для запроса в стиле Ole/Access вы должны установить это как строку запроса:

DLookup("island", "village", "village = ? ")

А затем установить параметр отдельно. Однако я не знаю, как вы устанавливаете значение параметра из vba.

person Joel Coehoorn    schedule 14.10.2008
comment
Как именно вы устанавливаете параметр отдельно? - person StockB; 30.11.2013
comment
Используйте подготовленные операторы. См. stackoverflow.com/questions/6572448/ или technet.microsoft.com/en-us/library/aa905910(v= sql.80).aspx. Эта статья посвящена SQL Server, но я подозреваю, что она может работать и для Access. - person Trevor Dixon; 16.04.2014

Хотя сокращенные доменные функции, такие как DLookup, заманчивы, у них есть свои недостатки. Эквивалентный Jet SQL что-то вроде

SELECT FIRST(island)
FROM villages
WHERE village = ?;

Если у вас есть более одного подходящего кандидата, он выберет «первый», определение «первого» зависит от реализации (механизма SQL) и не определено для движка Jet/ACE IIRC. Вы знаете, кто из них будет первым? Если нет, то держитесь подальше от DLookup :)

[Для интереса ответом для Jet/ACE будет либо минимальное значение, основанное на кластеризованном индексе на момент последнего сжатия файла базы данных, либо первое (допустимое время) вставленное значение, если база данных никогда не уплотнялась. Кластеризованный индекс, в свою очередь, определяется 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
       );

Таким образом, вы можете использовать собственные функции движка — или, по крайней мере, его поставщиков данных — для экранирования всех символов (а не только двойных и одинарных кавычек) по мере необходимости.

person onedaywhen    schedule 14.10.2008

Я считаю, что доступ может использовать Chr$(34) и, к счастью, иметь одинарные кавычки/апострофы внутри.
например

DLookup("island", "villages", "village = " & chr$(34) & nonEscapedString & chr$(34))

Хотя тогда вам придется избегать chr$(34) ("")

Вы можете использовать функцию замены.

Dim escapedString as String

escapedString = Replace(nonescapedString, "'", "''")
person Rob Gray    schedule 14.10.2008

Параметризованные запросы, такие как предложенный Джоэлом Коегоорном, являются правильным путем вместо конкатенации в строке запроса. Во-первых, вы избегаете определенных рисков безопасности, во-вторых, я достаточно уверен, что вам нужно сбежать в собственные руки движка, и вам не нужно об этом беспокоиться.

person Gnudiff    schedule 04.01.2009

Но тогда это должно быть так (с еще одной двойной кавычкой):

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) & "');"

or

strSQL = "UPDATE tbl" & _
    " SET fld1='" & fncSQLStr(str1) & "', fld2='" & fncSQLStr(Me.tfFld2.Value) & "'" & _
    " WHERE fld3='" & fncSQLStr(str3) & "';"
person Community    schedule 18.06.2013

Кстати, вот моя функция 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
person inglesp    schedule 14.10.2008
comment
это то, что я просил на самом деле. я не использую vba много, и я не нахожу файлы справки очень полезными! ваше здоровье. - person inglesp; 14.10.2008

Для тех, у кого проблемы с одинарными кавычками и функцией замены, эта строка может спасти ваш день ^o^

Replace(result, "'", "''", , , vbBinaryCompare)
person niceboomer    schedule 25.02.2010

поместите в скобки критерии, которые могут содержать апостроф.

Что-то типа:

DLookup("island", "villages", "village = '[" & txtVillage & "]'")

Возможно, они должны быть за пределами одинарных кавычек или просто вокруг txtVillage, например:

DLookup("island", "villages", "village = '" & [txtVillage] & "'")

Но если вы найдете правильную комбинацию, она позаботится об апострофе.

Кейт Б.

person keith b    schedule 04.04.2013

Мое решение намного проще. Первоначально я использовал это выражение SQL для создания набора записей ADO:

Dim sSQL as String
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish='" & myString & "';"

Когда в myString был апостроф, как в Int'l Electrics, моя программа останавливалась. Использование двойных кавычек решило проблему.

sSQL="SELECT * FROM tblTranslation WHERE fldEnglish="" & myString & "";"
person dubi    schedule 14.06.2013
comment
Да, теперь вместо проблемы с апострофом у вас проблема с двойными кавычками :-/ - person Sascha L.; 13.11.2017