8

哪些 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 都支持此功能。

4

6 回答 6

7

Access 数据库引擎(ACE、Jet 等)支持CHECK约束中的子查询,但我不愿将其称为SQL DBMS,因为它不支持入门级标准 SQL-92,而且CHECKMS 和 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
于 2011-06-01T05:42:13.447 回答
4

Firebird 文档说它允许 CHECK 约束中的子查询。

于 2012-03-29T00:16:19.007 回答
2

SQL Server 2000+ 允许包含查询的 UDF:不能直接使用子查询

但是,它们在高负载下不并发

于 2011-06-01T04:43:21.963 回答
1

H2 还支持约束中的子查询。在 Psql 模式下不少于:P

MariaDB似乎也不支持它作为约束

ALTER TABLE Table_1 ADD CONSTRAINT constraint_1 
CHECK (column_1 > (SELECT MAX(column_2) FROM Table_2) NOT DEFERRABLE;

重要因为本书是关于 SQL-99 标准的,所以本书中这一页和其他页面的内容可能并不直接适用于 MariaDB。使用导航栏浏览图书。

这种事情曾经是非法的,但在 SQL 的现代变体中,您会偶尔看到表间约束引用。

作为参考,这是在 MariaDB 上实施检查约束的票证。截至 2015 年 7 月 23 日,它仍处于“开放”状态。

于 2015-07-22T07:01:56.403 回答
0

很确定 TRIGGER 将在您提到的每个数据库中工作,并且您获得更多“肘部空间”来解决您的约束。

于 2011-06-01T03:30:31.173 回答
-3

SQL server 支持你可以在下面的链接中找到有价值的信息

http://www.craigsmullins.com/sql_1298.htm

他们说 POSTGRESQL 也支持它

http://developer.postgresql.org/pgdocs/postgres/ddl-constraints.html

DB2 支持 CHECK 约束

http://www.ibm.com/developerworks/data/library/techarticle/dm-0401melnyk/

于 2011-06-01T03:19:49.787 回答