1

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

(标准 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应评估为UNKNOWN。这INSERT是“给予怀疑的好处”并成功了。那里没问题。

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

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

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

INSERT INTO Test2 (ID) VALUES (1);

这是出乎意料的行为,至少在我看来。由于与第一个示例相同的原因,我希望 the5 IN (0, 1, NULL)再次被评估为UNKNOWN并且成功。INSERT

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

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语句都成功。事实上,检查 INFORMATION SCHEMA 表明,对于第二个示例,SQL Server as 'helpfully' (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

编辑:我只是想试试这个:

在 (1) 中选择空值;-- 返回 NULL

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

4

1 回答 1

1

我可以通过创建验证规则来专门消除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 子句!

于 2009-06-06T06:43:57.860 回答