ACE/Jet 的 IN 运算符或 CHECK 约束是否“损坏”?
考虑下表的约束,虽然有点愚蠢,但足够简单来证明我的观点。 请注意,为了使事情变得非常简单,约束条件仅涉及文字值。 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
的计算结果应为 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
成功,原因与第一个例子。
我希望第二个示例中的逻辑与以下第三个示例相同:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我可以通过创建验证规则来专门消除
CHECK
约束(@David W. Fenton:抱歉,我发现 SQL DDL 和 ADO 比 DAO 更容易编写,但感谢您的启发):验证规则咬住并且 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):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!