Запрос не будет работать с переменными, будет работать, когда определения переменных вставлены в - PullRequest
1 голос
/ 28 марта 2009

Это запрос в VBA (Access 2007) У меня определены 3 строки:

str_a = "db.col1 = 5"
str_b = " and db.col2 = 123"
str_c = " and db.col3 = 42"

Затем я использую их в ГДЕ-части моего запроса:

"WHERE '" & str_a & "' '" & str_b & "' '" & str_c & "' ;"

Это не получается, но если я вставлю строки следующим образом:

"WHERE db.col1 = 5 and db.col2 = 123 and db.col3 = 42;"

Работает отлично. Я предполагаю, что синтаксис неверен при использовании нескольких переменных в строке.
У кого-нибудь есть намеки?

Ответы [ 7 ]

3 голосов
/ 28 марта 2009
"WHERE '" & str_a & "' '" & str_b & "' '" & str_c & "' ;"

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

"WHERE db.col1 = 5 and db.col2 = 123 and db.col3 = 42;"

Итак, попробуйте построить предложение WHERE без одинарных кавычек:

"WHERE " & str_a & " " & str_b & " " & str_c & ";"

В целях отладки полезно просмотреть окончательную строку после того, как VBA закончит ее создание. Если вы храните строку в переменной с именем strSQL, вы можете использовать:

Debug.Print strSQL

для отображения готовой строки в непосредственном окне редактора VB. (Вы можете попасть в окно «Немедленно» с помощью сочетания клавиш CTRL + g .)

В качестве альтернативы вы можете отобразить готовую строку в окне сообщения:

MsgBox strSQL
3 голосов
/ 28 марта 2009

У вас там есть дополнительные одиночные кавычки.

Попробуйте это:

"WHERE " & str_a & str_b  & str_c

Примечание. В общем случае не следует создавать строки запроса путем объединения строк, поскольку это делает вас уязвимым для внедрения SQL и неправильно обрабатывает специальные символы. Лучшее решение - использовать подготовленные заявления. Но при условии, что вы работаете в очень контролируемой среде, решение, которое я дал, должно работать.

2 голосов
/ 28 марта 2009

Для динамического SQL VB6 / VBA мне всегда удобнее создавать шаблон SQL, а затем использовать функцию Replace () для добавления динамических частей. Попробуйте это:

Dim sql As String
Dim condition1 As String
Dim condition2 As String
Dim condition3 As String

sql = "SELECT db.col1, db.col2, db.col3 FROM db WHERE <condition1> AND <condition2> AND <condition3>;"

condition1 = "db.col1 = 5"
condition2 = "db.col2 = 123"
condition3 = "db.col3 = 'ABCXYZ'"

sql = Replace(sql, "<condition1>", condition1)
sql = Replace(sql, "<condition2>", condition2)
sql = Replace(sql, "<condition3>", condition3)

Однако в этом случае будут меняться значения в предложении WHERE, а не сами поля, поэтому вы можете переписать это как:

Dim sql As String

sql = "SELECT col1, col2, col3 FROM db "
sql = sql & "WHERE col1 = <condition1> AND col2 = <condition2> AND col3 = '<condition3>';"

sql = Replace(sql, "<condition1>", txtCol1.Text)
sql = Replace(sql, "<condition2>", txtCol2.Text)
sql = Replace(sql, "<condition3>", txtCol3.Text)
2 голосов
/ 28 марта 2009

Краткий совет по устранению неполадок SQL, который создается динамически: echo строка SQL , полученная в результате всей конкатенации и интерполяции, вместо того, чтобы смотреть на ваш код.

WHERE 'db.col1 = 5' ' and db.col2 = 123' ' and db.col3 = 42';

В девяти случаях из десяти проблема становится намного яснее.

0 голосов
/ 01 апреля 2009

Хотя динамический SQL может быть более эффективным для движка, некоторые из приведенных здесь комментариев, похоже, подтверждают мое мнение о том, что динамический SQL может сбивать с толку читателя, особенно когда он не писал код (подумайте о человеке, который унаследует ваш код).

Я предпочитаю статический SQL в PROCEDURE и делаю вызов proc динамическим во время выполнения, выбирая соответствующие значения; если вы используете SQL DDL (пример ниже) для определения процедуры, вы можете указать DEFAULT значения (например, NULL) для параметров, чтобы вызывающая сторона могла просто пропустить те, которые не нужны, например, посмотрим, сможете ли вы следовать логике этого процесса:

CREATE PROCEDURE MyProc
(
   arg_col1 INTEGER = NULL, 
   arg_col2 INTEGER = NULL, 
   arg_col3 INTEGER = NULL
)
AS 
SELECT col1, col2, col3
  FROM db 
 WHERE col1 = IIF(arg_col1 IS NULL, col1, arg_col1) 
       AND col2 = IIF(arg_col2 IS NULL, col2, arg_col2) 
       AND col3 = IIF(arg_col3 IS NULL, col3, arg_col3);

Конечно, это может не привести к лучшему плану выполнения, но, по-моему, вы должны сбалансировать оптимизацию с хорошими принципами проектирования (и она работает очень быстро на моей машине:)

0 голосов
/ 28 марта 2009

У меня есть моя любимая функция addANDclause со следующими параметрами:

public addANDclause( _ 
    m_originalQuery as string, _
    m_newClause as string) _
as string
  • если m_originalQuery не содержит ключевое слово WHERE, то addANDClause () вернет исходный запрос с добавленным к нему «WHERE».
  • если m_orginalQuery уже содержит ключевое слово WHERE, то addANDClause () вернет исходный запрос с добавленным «И».

Так что я могу добавить как можно больше предложений «И». В вашем примере я бы написал следующее для создания моего SQL-запроса на лету:

m_SQLquery = "SELECT db.* FROM db"
m_SQLquery = addANDClause(m_SQLQuery, "db.col1 = 5")
m_SQLQuery = addANDClause(m_SQLQuery, "db.col2 = 123")
m_SQLQuery = addANDClause(m_SQLQuery, "db.col3 = 42")

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

m_SQLQuery = addANDClause(m_SQLQuery, "db.text1 like 'abc*'")
0 голосов
/ 28 марта 2009

Некоторые комментарии по созданию предложений WHERE в VBA.

Ваш пример по определению будет неверным, потому что вы помещаете одинарные кавычки там, где они не нужны. Это:

str_a = "db.col1 = 5"
str_b = " and db.col2 = 123"
str_c = " and db.col3 = 42"
"WHERE '" & str_a & "' '" & str_b & "' '" & str_c & "' ;"

... даст такой результат:

WHERE 'db.col1 = 5' ' and db.col2 = 123' ' and db.col3 = 42' ;

Это явно не сработает.

Уберите одинарные кавычки, и это должно сработать.

Теперь, как говорится, я бы никогда так не поступил. Я бы никогда не поместил AND в подстроки, которые используются для конструирования предложения WHERE, потому что, что бы я делал, если бы у меня было значение для второй строки, но не для первой?

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

str_a = "db.col1 = 5"
str_b = "db.col2 = 123"
str_c = "db.col3 = 42"

Чтобы объединить это, вы должны сделать:

If Len(str_a) > 0 Then
   strWhere = strWhere & " AND " str_a
End If
If Len(str_b) > 0 Then
   strWhere = strWhere & " AND " str_b
End If
If Len(str_c) > 0 Then
   strWhere = strWhere & " AND " str_c
End If

Когда назначены все три строки, это даст вам:

" AND db.col1 = 5 AND db.col2 = 123 AND db.col3 = 42"

Просто используйте Mid (), чтобы нарезать первые 5 символов, и он всегда будет корректным независимо от того, для каких переменных назначены значения:

strWhere = Mid(strWhere, 6)

Если ни один из них не назначен, вы получите строку нулевой длины, что вам и нужно. Если какой-либо из них назначен, вы сначала получите «И ...», который является ошибочным ведущим оператором, который вы просто удаляете командой Mid (). Это работает, потому что вы знаете, что все результаты до Mid () будут начинаться с «И», независимо от того, что - нет ненужных тестов на предмет того, было ли strWhere уже присвоено значение - просто вставьте туда «И» и нарежьте его в конце выкл.

В другой заметке кто-то упомянул SQL-инъекцию. Что касается Access, было долгое обсуждение того, что рассматривает много вопросов, близких к этой теме:

Инъекция SQL без веб-интерфейса

...