Я предполагаю, что ваш код содержит опечатки (круглые скобки) и должен фактически читать:
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