【发布时间】:2011-07-08 21:47:10
【问题描述】:
有没有更好的方法在我的 where 子句中编写以下内容?
WHERE (IIf([GrpOrder]=3,IIf([LabelText]="Totals",True,False),True)) =True))
谢谢,
斯科特
【问题讨论】:
有没有更好的方法在我的 where 子句中编写以下内容?
WHERE (IIf([GrpOrder]=3,IIf([LabelText]="Totals",True,False),True)) =True))
谢谢,
斯科特
【问题讨论】:
我假设您的代码包含拼写错误(非空白括号),实际上应该是:
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 子句,为每个OR 子句的两列明确处理NULL。但是,这是非常冗长的,最好将这两个返回 FALSE 的情况进行标记。这就是大多数人(包括我!)遇到 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
)
[...读起来很奇怪,我想知道 OP 的代码是否首先产生了预期的结果。]
要学习的主要课程:
CREATE TABLESQL DDL...)和示例数据(...例如INSERT INTOSQL DML...)(...或在必要时使用文字和图片;) 因为如果你的列被标记为NOT NULL 那么答案就简单多了! :)@Yanir Kleiman cmets:
GrpOrder 不能为 3 和 NULL 同时,所以检查它不为空 在这种情况下是多余的
这样想是可以原谅的。但这是 Access :) 对于声称符合 SQL 标准的 SQL 产品,我们有出色的规范。 Access 声称没有这样的合规性和the documentation the Access Team have provided is of a particularly low quality。
相反,在 Access-land 中,要使某件事属实,您必须对其进行实际测试!
当我删除谓词时
AND GrpOrder IS NOT NULL
空值出现在结果集中。虽然这感觉像是“违背逻辑”,但请记住,SQL 的三值逻辑仅在 Access 声称不符合规范的规范中定义。如果访问团队没有告诉我们产品应该如何工作,我们如何判断以上是错误还是功能?即使我们可以说服他们这是一个错误,他们会修复它吗?
下面我提供 VBA 代码来重现该问题:只需复制+粘贴到任何 VBA 模块中,无需设置引用。它在 temp 文件夹中创建一个新的 .mdb,然后创建表和测试数据。无需在机器上安装 Access,例如使用 Excel 的 VBA 编辑器。
消息框显示分别包含和删除上述谓词时的结果集。除了两个表列之外,还有两个计算列显示值为 -1 (TRUE)、0 (FALSE) 和 NULL,最左边的一个是 OP:
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
【讨论】:
首先,第二个 IIF 是多余的 - “IIF(X, True, False)” 总是可以替换为“X”。
除此之外,选择的逻辑是“where GrpOrder = 3 and LabelText="Totals", OR GrpOrder 3”。
这与“where LabelText="Totals" OR GrpOrder 3”相同,因此:
WHERE [GrpOrder] <> 3 OR [LabelText]="Totals"
*我不记得访问是否使用 或 != 来表示不等式,所以无论哪个有效。
编辑:
我们总共有4个案例:
GrpOrder = 3 和 LabelText = "Totals" => 接受
GrpOrder = 3 和 LabelText "Totals" => 不接受
GrpOrder 3 和 LabelText = "Totals" => 接受
GrpOrder 3 和 LabelText "Totals" => 接受
我们不接受的唯一情况是当 GrpOrder = 3 且 LabelText "Totals" 时,这与我们接受 GrpOrder 3(底部两行)或 LabelText="Totals" 的所有行相同(第一行和第三行)。第 2 行是唯一不被接受的。
【讨论】:
GrpOrder = 3 和LabelText IS NULL 时,OP 的谓词将评估为FALSE,而您的谓词将评估为NULL。虽然确实在 SQL DML 中效果将是从结果集中删除行,但如果在 SQL DDL(例如 CHECK 约束)或计算列中使用它,则效果将是允许更新成功什么时候应该失败。
GrpOrder IS NULL 和LabelText IS NULL 时的明显情况:在SQL DML 中(例如,在常规查询的WHERE 子句中),您的谓词将删除该行,而它将被OP 保留。
<>,顺便说一句。
我不想要任何记录 GrpOrder=3 除了 GrpOrder=3 和 LabelText="总计"。
where GrpOrder <> 3 or (GrpOrder = 3 and LabelText="Totals")
【讨论】:
GrpOrder IS NULL 时的情况。