Escaping 'в Access SQL

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

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

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

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

Ответы

Ответ 1

Функция "Заменить" должна выполнить трюк. Основываясь на вашем коде выше:

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

Ответ 2

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

'); DROP TABLE [YourTable]

Не очень.

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

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

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

Ответ 3

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

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

Если у вас есть несколько подходящих кандидатов, он будет выбирать "первый", определение "первым" - это реализация (зависимая от SQL engine) и undefined для двигателя 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
       );

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

Ответ 4

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

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

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

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

Dim escapedString as String

escapedString = Replace(nonescapedString, "'", "''")

Ответ 5

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

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

Ответ 6

Кстати, здесь моя функция 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

Ответ 7

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

Ответ 8

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

Replace(result, "'", "''", , , vbBinaryCompare)

Ответ 9

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

SOmething like:

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

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

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

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

Keith B

Ответ 10

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

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

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

sSQL="SELECT * FROM tblTranslation WHERE fldEnglish="" & myString & "";"