Невозможно использовать значения ListBox в MS Access 2007 для перехода к запросу

Доброе утро,

Я помогаю разработать интерфейс через форму в MS Access. У нас есть список с различными пользовательскими значениями, и пользователь должен иметь возможность выбрать несколько значений в ListBox, а затем нажать кнопку, чтобы выполнить запрос, возвращая только те строки, имя машины которых является тем, что было выбрано.

ОБНОВЛЕНИЕ - благодаря отличным отзывам на этом форуме основная проблема была решена. Моя вторичная проблема теперь не может выполнить запрос. При попытке я получаю сообщение об ошибке, что запрос не может быть выполнен.

Мой код (как процедура события) для кнопки:

Option Explicit
Private Sub btnSearchCars_Click()
    MsgBox "Starting Sub"
    Call QueryCars.myQuery
    MsgBox "Ending Sub"
End Sub

Тогда мой модуль QueryCars выглядит так:

Sub myQuery()
    Dim strWhere As String
    Dim strSQL As String
    Dim varItem As Variant

    For Each varItem in Forms!FormSelect!listCarID.SelectedItems
        strWhere = strWhere & "'" & Forms!FormSelect!listCarID.ItemData(varItem) & "',"
    Next

strWhere = Left (strWhere, Len (strWhere) -1)

    strSQL = "SELECT tblBig.* FROM tblCars INNER JOIN tblBig ON tblCars.Car_ID = tblBig.Car_ID WHERE tblCars.Car_ID IN (" & strWhere & ");"
    DoCmd.RunSQL strSQL

End Sub

Моя ошибка - это сообщение об ошибке «RunSQL требует аргумент оператора SQL» в строке.

DoCmd.RunSQL strSQL

Я был бы очень признателен, если бы кто-нибудь мог помочь. Все, что я пытаюсь сделать, это взять значения из списка, который выбирает пользователь, и использовать их в качестве критериев WHERE в моем запросе. Я искал различные форумы SO и Access все утро и не нашел ничего, что могло бы помочь.

Спасибо. Пожалуйста, дай мне знать, если возникнут какие-либо вопросы.


person wundermahn    schedule 26.06.2018    source источник
comment
strWhere кажется не очень хорошо построенным: во-первых, у вас в два раза больше добавленных элементов, во-вторых, у вас есть, в конце, который необходимо удалить.   -  person Vincent G    schedule 26.06.2018
comment
@VincentG благодарим вас за отзыв. Я внес эти изменения, но все равно получаю ту же ошибку.   -  person wundermahn    schedule 26.06.2018
comment
ОбъявлениеmyForm неверно. Оно должно быть As Access.Form. Используя Option Explicit в верхней части каждого модуля, вы сможете выбрать эти ошибки во время компиляции.   -  person Kostas K.    schedule 26.06.2018
comment
Привет, @KostasK. Благодарю за ваш ответ. К сожалению, это не устранило мою ошибку, хотя я скорректировал код.   -  person wundermahn    schedule 26.06.2018
comment
Попробуйте напрямую сослаться на элемент управления ListBox. For Each varItem In FormName!listCarID.ItemsSelected   -  person Kostas K.    schedule 26.06.2018
comment
Привет, @KostasK. Похоже, что это делает ситуацию лучше. Большое спасибо! Но теперь у меня проблема, когда я получаю сообщение об ошибке «Невозможно выполнить запрос выбора».   -  person wundermahn    schedule 26.06.2018
comment
У вас все еще есть избыток в конце strWhere   -  person Vincent G    schedule 26.06.2018
comment
Привет @VincentG. Мне это нужно, не так ли? Кроме того, я добавил дополнительную функцию для удаления завершающего символа ',', который здесь не отражен. Меня больше всего беспокоит невыполнение запроса.   -  person wundermahn    schedule 26.06.2018
comment
Как мы можем узнать, что вы добавили дополнительную функцию для удаления завершающего символа ',', если вы не сообщите нам об этом?   -  person Vincent G    schedule 26.06.2018
comment
@VincentG, спасибо за отзыв. Я добавлю это, но это не имеет отношения к рассматриваемой проблеме.   -  person wundermahn    schedule 26.06.2018


Ответы (2)


Это не идеальный ответ, который я надеялся вам дать, но не могу понять, как использовать запросы с параметрами в команде IN.

Я предполагаю, что ваш список содержит два столбца данных, а значения CarID находятся в первом столбце.

Основная функция называется ProcessQuery и принимает в качестве аргумента ссылку на список:
Public Sub ProcessQuery(myList As ListBox)

Затем вы можете вызвать свой код из события в списке и передать ему ссылку на список.

Private Sub btnSearchCars_Click()
    ProcessQuery Me.listCarID
End Sub  

Затем процедура ProcessQuery просматривает первый столбец, чтобы получить номера индексов, строит SQL, открывает полученный набор записей и извлекает информацию из каждой записи.

Public Sub ProcessQuery(myList As ListBox)

    Dim vItem As Variant
    Dim IDList As String
    Dim qdf As dao.QueryDef
    Dim rst As dao.Recordset

    For Each vItem In myList.ItemsSelected
        'Column 0 is first column in listbox.
        IDList = IDList & "'" & myList.Column(0, vItem) & "',"
    Next vItem
    'Removes the final ,
    IDList = Left(IDList, Len(IDList) - 1)

    'Create a temporary query definition & open the recordset.
    Set qdf = CurrentDb.CreateQueryDef("", _
        "SELECT tblBig.* FROM tblCars INNER JOIN tblBig ON tblCars.Car_ID = tblBig.Car_ID WHERE tblCars.Car_ID IN (" & IDList & ")")
    Set rst = qdf.OpenRecordset

    'Move through the recordset and output the first two fields from each record
    'to the Immediate window.
    With rst
        If Not (.BOF And .EOF) Then
            .MoveFirst
            Do While Not .EOF
                Debug.Print .Fields(0) & " - " & .Fields(1)
                .MoveNext
            Loop
        End If
    End With

End Sub 

Чтобы отобразить результат запроса в виде таблицы, вы можете использовать следующее, но я бы предпочел использовать сохраненный запрос с параметром для IN. Я постараюсь понять это.

Public Sub ProcessQuery(myList As ListBox)

    Dim vItem As Variant
    Dim IDList As String
    Dim qdf As dao.QueryDef
    Dim rst As dao.Recordset

    For Each vItem In myList.ItemsSelected
        'Column 0 is first column in listbox.
        IDList = IDList & "'" & myList.Column(0, vItem) & "',"
    Next vItem
    'Removes the final ,
    IDList = Left(IDList, Len(IDList) - 1)

    'Create a temporary query definition & open the recordset.
    Set qdf = CurrentDb.CreateQueryDef("TempQDF", _
        "SELECT tblBig.* FROM tblCars INNER JOIN tblBig ON tblCars.Car_ID = tblBig.Car_ID WHERE tblCars.Car_ID IN (" & IDList & ")")

    DoCmd.OpenQuery "TempQDF", acViewNormal

End Sub
person Darren Bartrup-Cook    schedule 26.06.2018
comment
Привет, @Darren Bartrup-Cook, большое спасибо за ответ! Я просто пошел дальше и потратил последние 30 минут на реализацию вашей идеи, а также на попытки узнать, что вы делаете, и поискать в Google термины, с которыми я был незнаком. У меня есть одна проблема. Он отлично работает, без ошибок, но ничего не происходит ... это означает, что записи никуда не возвращаются. Я добавил несколько MsgBox, чтобы показать, что функция работает, что она и делает, но на самом деле ничего не заполняется. У тебя есть какой-нибудь совет? - person wundermahn; 26.06.2018
comment
Несколько вещей, которые нужно проверить - 1. выполните код, используя F8. Выполняет ли он код в блоке If Not (.BOF And .EOF) Then - в противном случае это хороший знак, что запрос ничего не вернул. 2. Когда код будет приостановлен после IDList = Left(IDList, Len(IDList) - 1), откройте окно «Немедленное» (Ctrl+G, чтобы открыть в VBE) и введите ?"SELECT tblBig.* FROM tblCars INNER JOIN tblBig ON tblCars.Car_ID = tblBig.Car_ID WHERE tblCars.Car_ID IN (" & IDList & ")". Создайте запрос с помощью SQL и посмотрите, вернет ли он что-нибудь. - person Darren Bartrup-Cook; 27.06.2018
comment
3. Откройте окно «Локальные переменные» и следите за переменными по мере выполнения кода - IDList заполняются правильными значениями? - person Darren Bartrup-Cook; 27.06.2018
comment
Привет, @Darren Batrup-Cook. Спасибо большое за вашу помощь. К сожалению, я не могу пройти через код. Нажатие F8 ничего не делает, а когда я нажимаю «Выполнить» - ›« Шаг с заходом », Access предлагает мне найти макрос, который сохраняется как модуль. Вы посоветуете мне создать формальный макрос? - person wundermahn; 27.06.2018
comment
Ах да .... забыл, что ты не сможешь так пройти. Поместите точку останова на строку ProcessQuery Me.listCarID в событии щелчка (щелкните границу слева от строки, и она должна выделить строку с точкой на границе). Откройте форму и нажмите кнопку. Ваш код остановится на точке останова, а затем вы можете перейти к нему с помощью F8. - person Darren Bartrup-Cook; 27.06.2018
comment
Привет, @ Даррен Бартруп-Кук. Еще раз спасибо за то, что вы были так терпеливы со мной. Мне кажется, я живу на SO и UtterAccess, пытаясь настроить эту штуку. Я прошел через код, и переменная IDList заполняется соответствующим образом, блок If Not Then также проходит цикл соответствующим образом. Я также последовал вашему совету и скопировал SQL из VBE, и когда я это сделал, он работал отлично. (FYI - это в кавычках в VBE, и мне нужно было удалить кавычки ... должен ли он быть в VBE ТОЧНО, как он запускается?) - person wundermahn; 27.06.2018
comment
После того, как вы ввели его в окне «Немедленное» и нажали клавишу «Ввод», вы сможете скопировать полученный SQL прямо в запрос и запустить его - в коде и в битах, которые вы вводите в непосредственное окно, потребуются кавычки. Если он достигает строки .MoveFirst, значит, возвращаются записи. Как только выполнение достигнет этой точки, наведите курсор на .Fields(0) - отображается ли значение во всплывающей подсказке? Попробуйте rst.MoveLast и ?rst.RecordCount в окне «Немедленное» - возвращает ли оно значение больше 0? - person Darren Bartrup-Cook; 27.06.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person wundermahn; 27.06.2018
comment
Собирался сказать по поводу моего последнего комментария - к сожалению, я не могу из-за категоризации содержания: Чат (IM) / SMS; Технологии / Интернет (по крайней мере, так мне говорит мой компьютер). - person Darren Bartrup-Cook; 27.06.2018
comment
Совершенно нормально. Я просто ценю вашу помощь. Я хочу сообщить вам, что строка .Fields показывает значение, а также оба других элемента возвращают значения ›0. - person wundermahn; 27.06.2018
comment
Тогда он работает - .Fields(0) - это просто первое поле, возвращаемое вашим запросом для той записи, в которой он сейчас находится. Вы можете использовать .Fields("FieldName") для определенных полей. Итак, я предполагаю, что следующая часть - что вы хотите делать с данными, когда они у вас есть? Одним из предложений был бы ответ, который я дал здесь: контролировать, в какую таблицу экспортировать запрос в msaccess - person Darren Bartrup-Cook; 27.06.2018
comment
Думаю, моя проблема в том, что ничего не открывается. В идеале я просто хочу отображать данные. Это не обязательно должно быть в каком-то определенном формате, мне просто нужно, чтобы он отображался правильно. У нас есть большая база данных конфигурации автомобилей и их марки / модели, какой тип масла они используют, тормоза и т. Д. Когда кто-то выбирает список автомобилей, я хочу, чтобы появилась вся эта информация. Так что просто вернуть записи было бы здорово. Проблема в том, что при запуске в Access ничего не отображается. - person wundermahn; 27.06.2018
comment
Я добавил код для отображения результатов в таблице данных - хотя это не тот ответ, который я надеялся вам дать, но у меня возникли проблемы с определением синтаксиса для передачи списка IN из параметра. - person Darren Bartrup-Cook; 27.06.2018
comment
Привет, @ Даррен Бартруп-Кук. Кажется, это работает идеально. Я действительно не могу отблагодарить вас за всю вашу помощь. Я ценю ваше терпение и готов научить меня нескольким вещам; Теперь я определенно чувствую себя более комфортно. - person wundermahn; 27.06.2018

Я бы посоветовал сначала взглянуть на фактическое генерируемое предложение WHERE ... сохранить отдельную строковую переменную для ее хранения, а затем выгружать ее в Immediate Window, когда она сгенерирована.

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

Public Function getListBoxSelection(ctl As Access.ListBox) As Variant
   Dim arr() As Variant
   Dim varItem As Variant, i As Long

   If ctl.ItemsSelected.Count > 0 Then
      ReDim arr(0 To ctl.ItemsSelected.Count - 1)
      i = 0

      For Each varItem In ctl.ItemsSelected
         arr(i) = ctl.ItemData(varItem)
         i = i + 1
      Next varItem

   End If

   getListBoxSelection = arr
End Function

Затем вы бы назвали его генерацией SQL. Что-то типа

whereClause = join(getListBoxSelection(me.listCarID), " AND ")
debug.Print whereClause

qdf.SQL = _
    "select tblBig.* " & _
    "from tblCars " & _
    "inner join tblBig on tblCars.Cat_ID = tblBig.Car_ID " & _
    "where tblCars.Card_ID in (" & whereClause & ")"
person Zack    schedule 26.06.2018
comment
Привет @Zack. Я могу пойти дальше и внести эти изменения. Спасибо, что предложили их. Моя текущая проблема в том, что даже когда я запускаю Debug.Print ИЛИ запускаю DoCmd.OpenQuery, на самом деле ничего не появляется и не происходит ... несмотря на то, что я не получаю никаких ошибок. - person wundermahn; 26.06.2018