1

次の表を考えてみてください。制約は少し巧妙ですが、私の主張を示すのに十分単純です。物事を非常に単純にするために、制約の基準にはリテラル値のみが含まれることに注意してください。この列IDが存在するのは、テーブルに少なくとも1つの列(!!)が必要であるが、その列は制約に関与していないためです。少し手間がかかりますが(そのため名前が付けられています)、これは完全に合法的な構文であり、クエリに追加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はに評価される必要があり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);

少なくとも私にとって、これは予想外の行動です。最初の例と同じ理由で、5 IN (0, 1, NULL)が再び評価されUNKNOWN、成功することを期待します。INSERT

2番目の例のロジックは、次の3番目の例と同じであると思います。

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で3つの例すべてをテストしましたが、すべてが期待どおりに機能します。つまり、3つのINSERTステートメントすべてが成功します。実際、INFORMATION SCHEMAを調べると、2番目の例のSQL Serverは、「役立つ」(grrr)として制約の句を書き直して、IN演算子を次のように置き換えていることがわかります。

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

したがって、ACE / Jetの場合、ここで「壊れている」とは何ですか:IN演算子またはCHECK制約?

NULL可能な列を使用して問題を再現するための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を返します

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 に答える