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

发布于 2024-07-22 06:31:51 字数 3024 浏览 5 评论 0原文

考虑下表的约束,虽然有点愚蠢,但足够简单来证明我的观点。 请注意,为了使事情变得非常简单,约束条件仅涉及文字值。 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) 再次被评估为 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 语句都成功。 事实上,检查信息架构表明,对于第二个示例,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

Consider the following table with a constraint that's a bit daft but simple enough to demonstrate my point. Note that, to keep things very simple, the constraint's criteria only involve literal values. The column ID only exists because a table must have at least one column (!!) but that column is not involved in the constraint. While a little daft (hence the name) this is perfectly legal syntax and similar to adding WHERE 0 = 1 to a SELECT query to ensure it returns zero rows.

(Standard SQL DDL code, will execute in ACE/Jet's ANSI-92 Query Mode)

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

The following INSERT succeeds:

INSERT INTO Test1 (ID) VALUES (1);

This is expected behaviour. The predicate 5 = NULL should evaluate to UNKNOWN. The INSERT is 'given the benefit of the doubt' and succeeds. No problem there.

Consider this similar example using the IN operator:

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

The following INSERT fails because the constraint bites:

INSERT INTO Test2 (ID) VALUES (1);

This is unexpected behviour, by me at least. I would expect the 5 IN (0, 1, NULL) to again be evaluated as UNKNOWN and the INSERT to succeed for the same reasons as the first example.

I would expect the logic in the second example to be the same as the following third example:

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

The following INSERT succeeds:

INSERT INTO Test3 (ID) VALUES (1);

This is expected behaviour.

I've tested all three examples on SQL Server and the all work as I expect i.e. all three INSERT statements succeed. In fact, examining the INFORMATION SCHEMA reveals that for the second example SQL Server as 'helpfully' (grrr) rewritten the constraint's clause to replace the IN operator with

((5)=NULL OR (5)=(1) OR (5)=(0))

So, for ACE/Jet, what is 'broken' here: the IN operator or the CHECK constraint?

Here's some VBA code to reproduce the problem using a NULLable column; also demonstrates that dropping the constraint allows the INSERT to succeed:

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

EDIT: I just thought to try this:

SELECT NULL IN (1);
-- returns NULL

SELECT 1 IN (NULL)
-- returns zero i.e. FALSE

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

终止放荡 2024-07-29 06:31:51

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

I can eliminate the CHECK constraint specifically by creating a Validation Rule (@David W. Fenton: sorry, I find SQL DDL and ADO and easier to write than DAO but thanks for the inspiration):

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

The Validation Rule bites and INSERT fails. Therefore, I suspect the IN clause is behaving unexpectedly. I shall be using nested OR clauses in future!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文