ACE / Jet的IN运算符或CHECK约束是否“已损坏”?

时间:2009-06-04 10:48:11

标签: ms-access jet

考虑下面的表格,其约束有点愚蠢但很简单,足以证明我的观点。请注意,为了使事情变得非常简单,约束的条件仅涉及文字值。列ID仅存在,因为表必须至少有一列(!!),但该列不参与约束。虽然有点愚蠢(因此名称),但这是完全合法的语法,类似于将WHERE 0 = 1添加到SELECT查询以确保它返回零行。

(标准SQL DDL代码,将在ACE / Jet的ANSI-92查询模式下执行)

CREATE TABLE Test1 
(
   ID INTEGER NOT NULL, 
   CONSTRAINT daft_1 CHECK (5 = NULL)
);

以下INSERT成功:

INSERT INTO Test1 (ID) VALUES (1);

这是预期的行为。谓词5 = NULL应评估为UNKNOWNINSERT是“给予怀疑的好处”并取得成功。没问题。

使用IN运算符考虑此类似示例:

CREATE TABLE Test2 
(
   ID INTEGER NOT NULL, 
   CONSTRAINT daft_2 CHECK (5 IN (0, 1, NULL))
);

以下INSERT失败,因为约束咬人:

INSERT INTO Test2 (ID) VALUES (1);

这至少是我出乎意料的行为。我希望5 IN (0, 1, NULL)再次被评估为UNKNOWNINSERT成功的原因与第一个示例相同。

我希望第二个例子中的逻辑与下面的第三个例子相同:

CREATE TABLE Test3
(
   ID INTEGER NOT NULL, 
   CONSTRAINT daft_3 CHECK((5 = 0) OR (5 = 1) OR (5 = NULL))
);

以下INSERT成功:

INSERT INTO Test3 (ID) VALUES (1);

这是预期的行为。

我已经测试了SQL Server上的所有三个示例以及我期望的所有工作,即所有三个INSERT语句都成功。事实上,检查信息SCHEMA显示,对于第二个示例,SQL Server作为“帮助”(grrr)重写了约束的子句以用

替换IN运算符
((5)=NULL OR (5)=(1) OR (5)=(0))

那么,对于ACE / Jet,这里有什么'破坏':IN运算符或CHECK约束?

这是使用NULLable列重现问题的一些VBA代码;还证明了删除约束允许INSERT成功:

Sub TestJetInCheck()

  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 Test" & vbCr & _
          "(" & vbCr & _
          "   ID INTEGER, " & vbCr & _
          "   CONSTRAINT daft_constraint " & vbCr & _
          "      CHECK (5 IN (0, 1, NULL))" & vbCr & _
          ");"
      .Execute Sql

      Sql = "INSERT INTO Test (ID) VALUES (1);"

      On Error Resume Next
      .Execute Sql
      If Err.Number <> 0 Then
        MsgBox Err.Description
      Else
        MsgBox "{{no error}}"
      End If
      On Error GoTo 0

      .Execute "ALTER TABLE Test DROP CONSTRAINT daft_constraint;"

      On Error Resume Next
      .Execute Sql
      If Err.Number <> 0 Then
        MsgBox Err.Description
      Else
        MsgBox "{{no error}}"
      End If
      On Error GoTo 0

    End With

    Set .ActiveConnection = Nothing
  End With
End Sub
编辑:我只是想试试这个:

SELECT NULL IN(1); - 返回NULL

SELECT 1 IN(NULL) - 返回零,即FALSE

1 个答案:

答案 0 :(得分:0)

我可以通过创建验证规则来专门消除CHECK约束(@David W. Fenton:对不起,我发现SQL DDL和ADO比DAO更容易编写,但感谢灵感):

Sub TestJetInValidationRule()

  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 Test" & vbCr & _
          "(" & vbCr & _
          "   ID INTEGER" & vbCr & _
          ");"
      .Execute Sql
    End With

    ' Create Validation Rules
    Dim jeng
    Set jeng = CreateObject("JRO.JetEngine")
    jeng.RefreshCache .ActiveConnection

    .Tables("Test").Columns("ID") _
    .Properties("Jet OLEDB:Column Validation Rule").value = _
    "5 IN (0, 1, NULL)"

    jeng.RefreshCache .ActiveConnection

    With .ActiveConnection

      Sql = "INSERT INTO Test (ID) VALUES (1);"

      On Error Resume Next
      .Execute Sql
      If Err.Number <> 0 Then
        MsgBox Err.Description
      Else
        MsgBox "{{no error}}"
      End If
      On Error GoTo 0

    End With

    Set .ActiveConnection = Nothing
  End With
End Sub

验证规则咬人并且INSERT失败。因此,我怀疑IN子句的行为是出乎意料的。我将来会使用嵌套的OR子句!