Access 数据库引擎(ACE、Jet 等)支持CHECK
约束中的子查询,但我不愿将其称为SQL DBMS,因为它不支持入门级标准 SQL-92,而且CHECK
MS 和 Access 团队几乎没有记录 Access 约束。
例如,我可以演示CHECK
为受影响的每一行检查访问约束(SQL-92 指定应该在每个 SQL 语句之后检查它们)但是这是一个错误还是一个我们不知道的功能,因为没有文档可以参考至。
这是一个包含子查询的 CHECK 约束的非常简单的示例。它符合完整的 SQL-92 并且在 Access 中运行良好。这个想法是将表限制为最多两行(以下 SQL DDL 需要ANSI-92 查询模式,例如使用 ADO 连接,例如Access.CurrentProject.Connection
):
CREATE TABLE T1
(
c INTEGER NOT NULL UNIQUE
);
ALTER TABLE T1 ADD
CONSTRAINT max_two_rows
CHECK (
NOT EXISTS (
SELECT 1
FROM T1 AS T
HAVING COUNT(*) > 2
)
);
但是,这里是 SQL-92 的另一个示例,可以在 Access 中创建(一些有效CHECK
的 s 在 Access 中失败,发生可怕的崩溃,需要重新启动我的机器:(但不能正常运行。这个想法是只允许表中有两行(或零行:不对空表测试约束):
CREATE TABLE T2
(
c INTEGER NOT NULL UNIQUE
);
ALTER TABLE T2 ADD
CONSTRAINT exactly_two_rows
CHECK (
NOT EXISTS (
SELECT 1
FROM T2 AS T
HAVING COUNT(*) <> 2
)
);
尝试在同一语句中插入两行,例如(假设表T1
至少有一行):
SELECT DT1.c
FROM (
SELECT DISTINCT 1 AS c
FROM T1
UNION ALL
SELECT DISTINCT 2
FROM T1
) AS DT1;
然而,这会导致CHECK
咬人。这(以及进一步的测试)意味着在将CHECK
每一行添加到表之后进行测试,而 SQL-92 指定在 SQL 语句级别测试约束。
当您考虑到在 Access2010 之前它没有CHECK
任何触发器功能并且某些经常使用的表将没有真正的键(例如 'sequenced ' 在有效状态时态表中键入)。请注意,Access2010 触发器与它们在行级别而不是语句级别测试的错误/功能相同。
下面是 VBA 重现上述两种场景。复制并粘贴到任何 VBA/VB6 标准 .bas 模块(例如使用 Excel)中,无需参考。在您的临时文件夹中创建一个新的 .mdb,创建约束工作/不工作的表、数据和测试(提示:设置断点,单步执行代码,阅读注释):
Sub AccessCheckSubqueryButProblem()
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 T1 " & vbCr & _
"( " & vbCr & _
" c INTEGER NOT NULL UNIQUE " & vbCr & _
");"
.Execute Sql
Sql = _
"ALTER TABLE T1 ADD " & vbCr & _
" CONSTRAINT max_two_rows " & vbCr & _
" CHECK ( " & vbCr & _
" NOT EXISTS ( " & vbCr & _
" SELECT 1 " & vbCr & _
" FROM T1 AS T " & vbCr & _
" HAVING COUNT(*) > 2 " & vbCr & _
" ) " & vbCr & _
" );"
.Execute Sql
Sql = _
"INSERT INTO T1 (c) VALUES (1);"
.Execute Sql
Sql = _
"INSERT INTO T1 (c) VALUES (2);"
.Execute Sql
' The third row should (and does)
' cause the CHECK to bite
On Error Resume Next
Sql = _
"INSERT INTO T1 (c) VALUES (3);"
.Execute Sql
MsgBox Err.Description
On Error GoTo 0
Sql = _
"CREATE TABLE T2 " & vbCr & _
"( " & vbCr & _
" c INTEGER NOT NULL UNIQUE " & vbCr & _
");"
.Execute Sql
Sql = _
"ALTER TABLE T2 ADD " & vbCr & _
" CONSTRAINT exactly_two_rows " & vbCr & _
" CHECK ( " & vbCr & _
" NOT EXISTS ( " & vbCr & _
" SELECT 1 " & vbCr & _
" FROM T2 AS T " & vbCr & _
" HAVING COUNT(*) <> 2 " & vbCr & _
" ) " & vbCr & _
" );"
.Execute Sql
' INSERTing two rows in the same SQL statement
' should succeed according to SQL-92
' but fails (and we have no docs from MS
' to indicate whether this is a bug/feature)
On Error Resume Next
Sql = _
"INSERT INTO T2 " & vbCr & _
" SELECT c " & vbCr & _
" FROM T1;"
.Execute Sql
MsgBox Err.Description
On Error GoTo 0
End With
Set .ActiveConnection = Nothing
End With
End Sub