Необходимо выполнить итерацию через ListBox, чтобы извлечь записи с истинными значениями из логических полей

#ms-access #vba

#ms-access #vba

Вопрос:

У меня есть (плохо спроектированная) БД, которая содержит таблицу добровольцев. В этой таблице более 70 полей «Да» / «Нет». Каждое поле представляет округ, который каждый доброволец может выбрать для обслуживания. Каждый доброволец может выбрать несколько округов.

У меня есть отчет, который должен разрешать выбор нескольких округов. Затем этот список необходимо сравнить с выбором логических полей по выбору, сохраняя записи только с истинными значениями.

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

 Dim s As Variant
Dim ctl As Control
Dim t As TCondition   'this is to compile multiple variables into one large "where" clause to run the report. 3 such variables are coded in this function.
Set ctl = Me.Counties
If ctl.ItemsSelected.Count <> 0 Then
   If t.WhereCondition = "" Then
     For Each s In ctl.ItemsSelected
        t.WhereCondition = (ctl.ItemData(s) amp; " = -1")
          Next s
Else
     For Each s In ctl.ItemsSelected
       t.WhereCondition = t.WhereCondition amp; " AND (ctl.ItemData(s) = -1)"
          Next s
  End If
End If
  

Где это сломалось, так это если кто-то выбрал более одного округа. Я понял, что они выбрали только последний округ.

Например — Графства: красный, синий, серебристый и зеленый вернут только зеленый.

Аналогичная проблема возникла с позициями добровольцев. Мне нужно было учитывать выбор нескольких

Итак, я нашел следующее:

 Public Function listBoxToString(ByRef theListBox As ListBox, Optional ByVal theDelimiter As String = vbNullString)
  Dim varTemp As Variant

  listBoxToString = vbNullString

  For Each varTemp In theListBox.ItemsSelected
    If listBoxToString <> vbNullString Then listBoxToString = listBoxToString amp; ","
    listBoxToString = listBoxToString amp; theDelimiter amp; theListBox.ItemData(varTemp) amp; theDelimiter
  Next varTemp
End Function
  

Это принимает все выбранные элементы списка и создает их список, разделенный запятыми. Это СРАБОТАЛО для полей позиций (их несколько), потому что эти значения полей являются ТЕКСТОВЫМИ.

Я попытался применить это к округам следующим образом:

 Dim s As String
Dim ctl As Control
Dim t As TCondition 
Set ctl = Me.Counties
If ctl.ItemsSelected.Count <> 0 Then
s = listBoxToString(Me.Counties, Chr(34))
   If t.WhereCondition = "" Then
        t.WhereCondition = (ctl.ItemData(s) amp; " = -1")
Else
       t.WhereCondition = t.WhereCondition amp; " AND (ctl.ItemData(s) = -1)"
  End If
End If
  

Это НЕ работает для округов, потому что ListBox возвращает ТЕКСТ, который затем необходимо сравнить со значениями в округе, которые являются ЛОГИЧЕСКИМИ. Итак, логически, я получаю ошибку несоответствия типа данных.

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

 Public Function MakeListCounties(intID As Integer) As String
Dim rs As DAO.Recordset
Dim strList As String
Dim x As Integer
Set rs = CurrentDb.OpenRecordset("SELECT * FROM Volunteer WHERE ID=" amp; intID)
For x = 44 To 105
    If rs(x).Type = dbBoolean Then
        If rs(x) = True Then strList = strList amp; rs(x).Name amp; ", "
    End If
Next
If strList <> "" Then MakeListCounties = Left(strList, (Len(strList) - 2))
rs.Close
Set rs = Nothing
End Function
  

Есть ли способ работать с этим, чтобы получить то, что я хочу? Это может быть излишним, и, если это так, приносим извинения и полностью игнорируем эту часть.

Напомним, что мне нужно выполнить итерацию по списку значений, сравнить их со значениями более 70 логических полей, сохранив только записи, где Boolean = True для имен полей, соответствующих значениям в списке

Комментарии:

1. Обычно мне не нравятся динамические параметры в запросе, но это может понадобиться. Думаю, я бы вызвал функцию из запроса. Функция возвращает True или False. Критерии для построенного поля будут =True следующими.

2. Я очень новичок в ООП. Я знаю, как вызвать функцию при разработке запросов, но создание функции, которая это делает, ставит меня в тупик.

Ответ №1:

Функция сборки в общем модуле, которая возвращает значение True или False. Вызов из запроса с выражением типа: MC: MatchCounties([ID]) с критериями фильтрации =True .

Предполагая, что в выпадающем списке перечислены названия округов, точно соответствующие именам полей да / нет, рассмотрим:

 Function MatchCounties(intID As Integer) As Boolean
Dim rs As DAO.Recordset
Dim varItem As Variant
With Forms!formname.Counties
    If .ItemsSelected.Count <> 0 Then
        Set rs = CurrentDb.OpenRecordset("SELECT * FROM Volunteer WHERE ID=" amp; intID)
        For Each varItem In .ItemsSelected
            If rs(.ItemData(varItem)) Then 'use the listbox value as field name and test for True
                MatchCounties = True
                Exit For 'only need to match 1 yes/no county field to retrieve record
            End If
        Next
        rs.Close
        Set rs = Nothing
    End If
End With
End Function
  

Если в списке не выбрано ни одного элемента, никакие записи не вернутся, потому что функция вернет False . Если вы хотите разрешить отсутствие выбора и по-прежнему возвращать записи, то, вероятно, хотите, чтобы функция возвращала True if .Элементы выбраны.Count = 0. Настройте код с помощью Else.

Комментарии:

1. Прочитайте это сейчас. Прошу прощения за задержку, я занимаюсь этим только днем.

2. Каким бы блестящим это ни было, оно непригодно для использования. Причина в том, что в VBA компилируется условие WHERE, затем в VBA вызывается запрос. Так что это никогда не оценивается во время выполнения.

3. Почему вы говорите, что это не оценивается во время выполнения? Я проверил это. Создал отчет с помощью функции вызова запроса. Применены дополнительные критерии фильтрации с помощью метода OpenReport. Работает идеально. Вы сказали, что хотите отчет. Вы тестировали? Как вы «вызываете» запрос?

4. Да, я проверил это. Я поместил функцию в общий модуль. Я вызвал это в запросе. Я установил точку останова в функции. Это должно было сработать, когда запрос вызвал его. Запрос никогда не вызывал функцию.

5. Ну, не могу этого объяснить. Это определенно работает для меня.

Ответ №2:

Измените первый фрагмент кода на следующий. Причина, по которой вы всегда получали последнее графство, заключается в том, что ваш цикл перезаписывал предыдущие значения предложения WHERE.

 Dim s As Variant
Dim ctl As Control
Dim t As TCondition
Set ctl = Me.Counties
If ctl.ItemsSelected.Count <> 0 Then
    For Each s In ctl.ItemsSelected
        t.WhereCondition = t.whereCondition amp; ctl.ItemData(s) amp; " = -1 OR"
    Next s
    ' trim trailing " OR"
    t.WhereCondition = Left(t.WhereCondition, Len(t.WhereCondition)-3)
End If
  

Комментарии:

1. В конкатенации нет оператора AND или OR. Это должно завершиться неудачей. Использование оператора AND не будет работать, потому что сомневаюсь, что какая-либо запись может соответствовать условию. И до 70 операторов OR могут работать, но будут довольно запутанными в представлении дизайна запроса, особенно если у других полей есть критерии.

2. @June7 Спасибо, что поймали мою ошибку конкатенации

3. Хорошо, но я не совсем понимаю объект TCondition. Никогда не видел этого. Предположим, у OP есть какой-то пользовательский код класса?

4. Да, я предполагаю, что у OP есть пользовательский класс

5. Кроме того, вероятно, следует обрезать завершающий ИЛИ вне цикла.