sql - 哪些 SQL 数据库支持 CHECK 约束中的子查询?

标签 sql subquery check-constraints

哪些 SQL 数据库(如果有)支持 CHECK 约束中的子查询?

目前,据我所知,Oracle、MySQL 和 PostgreSQL 没有。

编辑

(基于初步答案的澄清。)我正在寻找这样的东西:

CREATE TABLE personnel (
  ...,
department VARCHAR(64) NOT NULL,
salary NUMERIC NOT NULL,
CHECK (salary >= (SELECT MIN(p.salary) FROM payranges p WHERE p.dept = department)
        AND
       salary <= (SELECT MAX(p.salary) FROM payranges p WHERE p.dept = department)
)

更新

MS Access 和 Firebird 都支持此功能。

最佳答案

Access 数据库引擎(ACE、Jet 等)支持 CHECK 中的子查询限制,但我不愿将其称为 SQL DBMS,因为它不支持入门级标准 SQL-92 和 Access CHECK MS 和访问团队几乎没有记录约束。

例如,我可以演示 Access CHECK为受影响的每一行检查约束(SQL-92 指定应该在每个 SQL 语句之后检查它们),但这是一个错误还是一个我们不知道的特性,因为没有可引用的文档。

这是一个包含子查询的 CHECK 约束的非常简单的示例。它符合完整的 SQL-92 并且在 Access 中运行良好。想法是将表限制为最多两行(以下 SQL DDL 需要 ANSI-92 Query Mode 例如使用 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 在 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 语句级别测试约束。

Access 具有真正的表级 CHECK 应该不足为奇。当您考虑到 Access2010 之前它没有任何触发功能并且某些经常使用的表将没有真正的键(例如,有效状态时态表中的“排序”键)时,会受到限制。请注意,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

关于sql - 哪些 SQL 数据库支持 CHECK 约束中的子查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6195881/

相关文章:

sql - 使用 SQL 搜索精确值

mysql - SQLite 在 WHERE 中传递多个参数

java - Hibernate 检查注解

SQL CHECK 约束问题

java - 如何在Java中解析这个 "31/10/13"到Oracle日期

mysql - 错误使用内连接函数/分组函数?

c# - 无法将 DBNull.Value 转换为类型 'System.DateTime' 。请使用可空类型

sql-server - 使用WITH将Oracle查询转换为T-SQL查询

mysql - 结合两个复杂的查询

sql-server - 检查约束以确认 Exactly One 不为 NULL