Оптимизировать запрос доступа с помощью вложенных ИИФ - PullRequest
1 голос
/ 08 июля 2011

Есть ли лучший способ написать следующее в моем предложении where?

WHERE (IIf([GrpOrder]=3,IIf([LabelText]="Totals",True,False),True)) =True))

Спасибо,

Скотт

Ответы [ 3 ]

3 голосов
/ 08 июля 2011

Я предполагаю, что ваш код содержит опечатки (круглые скобки) и должен фактически читать:

WHERE IIf([GrpOrder]=3,IIf([LabelText]="Totals",True,False),True) = true

С точки зрения кода SQL на самом деле нужно рассмотреть девять случаев из-за логики трех значений SQL с NULL:

GrpOrder = 3
GrpOrder <> 3
GrpOrder IS NULL

LabelText = 'Totals'
LabelText <> 'Totals'
LabelText IS NULL

В комбинации есть девять случаев, например данные и результаты испытаний:

OP_result | GrpOrder | LabelText  
----------------------------------
     TRUE |       55 | 'Totals'
     TRUE |       55 | 'Tallies'
     TRUE |       55 | <NULL>
     TRUE |        3 | 'Totals'
    FALSE |        3 | 'Tallies'
    FALSE |        3 | <NULL>
     TRUE |   <NULL> | 'Totals'
     TRUE |   <NULL> | 'Tallies'
     TRUE |   <NULL> | <NULL>

Самый безопасный подход - выписать серию предложений OR, точно обработав NULL для обоих столбцов для каждого предложения OR. Тем не менее, это очень долго, было бы лучше, чтобы те два случая, которые возвращают ЛОЖЬ. И именно здесь большинство людей (включая меня!) Сталкиваются с проблемами с NULL: это слишком нелогично!

Например, заманчиво написать это:

(GrpOrder = 3 AND LabelText IS NULL)
OR
(GrpOrder = 3 AND LabelText <> 'Totals')

затем «перевернуть» его значение, используя NOT:

NOT (
     (GrpOrder = 3 AND LabelText IS NULL)
     OR
     (GrpOrder = 3 AND LabelText <> 'Totals')
    )

Однако при этом NULL пробирается в набор результатов:

OP_result | attempt_1 | GrpOrder | LabelText  
---------------------------------------------
     TRUE |      TRUE |       55 | 'Totals'
     TRUE |      TRUE |       55 | 'Tallies'
     TRUE |      TRUE |       55 | <NULL>
     TRUE |      TRUE |        3 | 'Totals'
    FALSE |     FALSE |        3 | 'Tallies'
    FALSE |     FALSE |        3 | <NULL>
     TRUE |      TRUE |   <NULL> | 'Totals'
     TRUE |    <NULL> |   <NULL> | 'Tallies'
     TRUE |    <NULL> |   <NULL> | <NULL>

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

Простейший предикат, который я смог придумать, дает желаемый результат в Access:

NOT
(
 (LabelText <> 'Totals' OR LabelText IS NULL)
 AND GrpOrder = 3 
 AND GrpOrder IS NOT NULL
)

[... что странно читать, интересно, дает ли код ОП желаемый результат в первую очередь.]

Основные уроки:

  • Следует избегать NULL в SQL: он не интуитивен, даже вызывает ошибки даже у очень опытных программистов SQL.
  • Всегда публикуйте свою схему (например, CREATE TABLE SQL DDL ...) и примеры данных (... например, INSERT INTO SQL DML ...) с ожидаемыми результатами (... или используйте слова и изображения, если необходимо ;) потому что если ваши столбцы помечены как NOT NULL, то ответ будет намного проще! :)

@ Комментарий Янира Клеймана:

GrpOrder не может быть 3 и NULL на в то же время, поэтому проверка не является нулевым в этом случае избыточно

Можно подумать, что так можно думать. Но это Access :) У нас есть отличные спецификации для продуктов SQL, которые заявляют о соответствии стандартам SQL. Access не заявляет о таком соответствии, и документация, предоставленная группой доступа, имеет особенно низкое качество .

Скорее, в Access-land, чтобы что-то было правдой, нужно на самом деле это проверить!

Когда я удаляю предикат

AND GrpOrder IS NOT NULL

пустые значения появляются в наборе результатов. Хотя кажется, что это «не поддается логике», имейте в виду, что логика трех значений SQL определяется только в спецификации, к которой Access не предъявляет никаких требований. Если группа доступа не сообщает нам, как должен работать продукт, как мы можем определить, является ли приведенная выше ошибка или функция? И даже если бы мы могли убедить их, что это ошибка, они исправят это?

Ниже я предоставляю код VBA для воспроизведения проблемы: просто скопируйте + вставьте в любой модуль VBA, ссылки не нужно устанавливать. Он создает новый .mdb в папке temp, затем создает таблицу и тестовые данные. Доступ не должен быть установлен на машине, например использовать редактор VBA в Excel.

Окно сообщения показывает набор результатов, когда вышеуказанный предикат включен и удален соответственно. В дополнение к двум столбцам таблицы отображаются два вычисляемых столбца со значениями -1 (ИСТИНА), 0 (ЛОЖЬ) и ПУСТО (NULL), а самый левый - это ОП:

Sub AccessStrangeLogic()

  On Error Resume Next
  Kill Environ$("temp") & "\DropMe.mdb"
  On Error GoTo 0

  Dim cat
  Set cat = CreateObject("ADOX.Catalog")
  With cat
    .Create _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=" & _
        Environ$("temp") & "\DropMe.mdb"
    With .ActiveConnection

      Dim Sql As String
      Sql = _
      "CREATE TABLE GrpOrders" & vbCr & _
      "(" & vbCr & _
      " GrpOrder INTEGER," & vbCr & _
      " LabelText NVARCHAR(10)" & vbCr & _
      ");"
      .Execute Sql

      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (55, 'Totals');"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (55, 'Tallies');"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (55, NULL);"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (3, 'Totals');"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (3, 'Tallies');"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (3, NULL);"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (NULL, 'Totals');"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (NULL, 'Tallies');"
      .Execute Sql
      Sql = _
      "INSERT INTO GrpOrders (GrpOrder, LabelText)" & _
      " VALUES (NULL, NULL);"
      .Execute Sql

      ' Include "AND GrpOrder IS NOT NULL"
      Sql = _
      "SELECT *, " & vbCr & _
      "       IIf([GrpOrder]=3,IIf([LabelText]=""Totals"",True,False),True) = true AS OP_result, " & vbCr & _
      "       NOT" & vbCr & _
      "       (" & vbCr & _
      "        (LabelText <> 'Totals' OR LabelText IS NULL)" & vbCr & _
      "        AND GrpOrder = 3 " & vbCr & _
      "        AND GrpOrder IS NOT NULL" & vbCr & "       )" & vbCr & _
      "  FROM GrpOrders" & vbCr & _
      " ORDER " & vbCr & _
      "    BY GrpOrder DESC, LabelText DESC;"

      Dim rs
      Set rs = .Execute(Sql)

      ' Remove "AND GrpOrder IS NOT NULL"
      Sql = Replace$(Sql, "AND GrpOrder IS NOT NULL", "")

      Dim rs2
      Set rs2 = .Execute(Sql)

      MsgBox _
          "Include 'AND GrpOrder IS NOT NULL':" & vbCr & _
          rs.GetString(, , vbTab, vbCr, "<NULL>") & vbCr & _
          "remove 'AND GrpOrder IS NOT NULL':" & vbCr & _
          rs2.GetString(, , vbTab, vbCr, "<NULL>")


    End With
    Set .ActiveConnection = Nothing
  End With
End Sub
1 голос
/ 08 июля 2011

Прежде всего, второй IIF является избыточным - «IIF (X, True, False)» всегда можно заменить на «X».

Кроме того, логика выбора «где GrpOrder»= 3 и LabelText = "Итоги", ИЛИ GrpOrder <> 3 ".

Это то же самое, что сказать" где LabelText = "Итоги" ИЛИ GrpOrder <> 3 ", следовательно:

WHERE [GrpOrder] <> 3 OR [LabelText]="Totals"

* Я не помню, использует ли доступ <> или! = Для неравенства, поэтому, какой бы ни работал.


Редактировать:

Всего у нас 4 случая:

GrpOrder = 3 и LabelText = "Итоги" => принять

GrpOrder = 3 и LabelText <> "Итоги" => не принять

GrpOrder <> 3 и LabelText= "Итоги" => принять

GrpOrder <> 3 и LabelText <> "Итоги" => принять

Единственный случай, который мы не принимаем, это когда GrpOrder = 3 и LabelText <> "Итоги ", то же самое, что сказать, что мы принимаем все строки, где GrpOrder <> 3 (две нижние строки) или LabelText =" Итоги "(первая и третья строка).Строка 2 является единственной, которая не принимается.

0 голосов
/ 08 июля 2011

Я не хочу никаких записей, где GrpOrder = 3, кроме случаев, когда GrpOrder = 3 и LabelText = "Итоги".

where GrpOrder <> 3 or (GrpOrder = 3 and LabelText="Totals")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...