使用嵌套的IIF优化访问查询

时间:2011-07-07 22:59:36

标签: sql ms-access

有没有更好的方法在where子句中编写以下内容?

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

谢谢,

斯科特

3 个答案:

答案 0 :(得分:3)

我认为你的代码包含拼写错误(unblanaced括号),实际上应该是:

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。然而,这是非常长的啰嗦,最好将那两个返回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的代码是否首先产生了预期的结果。]

要学习的主要课程:

    应该避免SQL中的
  • NULL:即使是非常有经验的SQL编码器也会导致错误甚至是反直觉的。
  • 始终发布您的架构(例如CREATE TABLE SQL DDL ...)和示例数据(例如INSERT INTO SQL DML ...)以及预期结果(...或使用单词和如果你必须的图片;)因为如果你的列被标记为NOT NULL,那么答案就会简单得多! :)

@Yanir Kleiman评论:

  

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

nulls出现在结果集中。虽然感觉这是“违反逻辑”,但请记住,SQL的三个值逻辑仅在Access声称不符合规范的情况下定义。如果访问团队没有告诉我们产品应该如何工作,我们如何判断上述是错误还是功能?即使我们能够说服他们这是一个错误,他们会解决它吗?

下面我提供VBA代码来重现问题:只需复制+粘贴到任何VBA模块,不需要设置引用。它在temp文件夹中创建一个新的.mdb,然后创建表和测试数据。无需在机器上安装访问,例如使用Excel的VBA编辑器。

消息框show显示分别包含和删除上述谓词时的结果集。除了两个表列之外,两个计算列显示的值为-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

答案 1 :(得分:1)

首先,第二个IIF是多余的 - “IIF(X,True,False)”总是可以用“X”代替。

除此之外,select的逻辑是“其中GrpOrder = 3而LabelText =”Totals“,或者GrpOrder&lt;&gt; 3”。

这与说“LabelText =”Totals“OR GrpOrder&lt;&gt; 3”相同,因此:

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

*我不记得访问是否使用&lt;&gt;或!=不平等,所以无论哪种方式都有效。


编辑:

我们共有4个案例:

GrpOrder = 3,LabelText =“Totals”=&gt;接受

GrpOrder = 3和LabelText&lt;&gt; “总计”=&gt;不接受

GrpOrder&lt;&gt; 3和LabelText =“Totals”=&gt;接受

GrpOrder&lt;&gt; 3和LabelText&lt;&gt; “总计”=&gt;接受

我们不接受的唯一情况是GrpOrder = 3和LabelText&lt;&gt; “Totals”,与我们接受GrpOrder&lt;&gt;所有行的说法相同3(底部两行)或LabelText =“Totals”(第一和第三行)。第2行是唯一不被接受的。

答案 2 :(得分:0)

  

我不希望任何记录在哪里   GrpOrder = 3,除非GrpOrder = 3和   LabelText的= “总计”。

where GrpOrder <> 3 or (GrpOrder = 3 and LabelText="Totals")