次の表を考えてみてください。制約は少し巧妙ですが、私の主張を示すのに十分単純です。物事を非常に単純にするために、制約の基準にはリテラル値のみが含まれることに注意してください。この列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を返します