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

时间:2011-06-01 03:14:29

标签: 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都支持此功能。

6 个答案:

答案 0 :(得分:7)

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

例如,我可以证明对每个受影响的行检查了Access CHECK约束(SQL-92指定在每个SQL语句之后应该检查它们)但是这是一个bug还是我们没有的功能知道因为没有可以参考的文件。


这是一个包含子查询的CHECK约束的一个非常简单的例子。它符合Full 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语句级别测试约束。

当您考虑到Access2010之前它没有任何触发功能并且某些经常使用的表会有其它方面时,Access确实存在表级CHECK约束,这不应该太令人意外。没有真正的密钥(例如,有效状态时态表中的“有序”密钥)。请注意,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

答案 1 :(得分:4)

Firebird documentation表示它允许CHECK约束中的子查询。

答案 2 :(得分:2)

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

但是,它们不是concurrent under high loads

答案 3 :(得分:1)

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

MariaDB 似乎也支持as a constraint

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

重要由于本书是关于SQL-99标准的,因此本书中此页面和其他页面的内容可能不会直接应用于MariaDB。使用导航栏导航书籍。

     

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

供参考,this is the ticket for implementing check constraints on MariaDB。截至2015-07-23,它仍处于“开放”状态。

答案 4 :(得分:0)

非常确定TRIGGER可以在您提到的每个数据库中运行,并且您可以获得更多“肘部空间”来计算您的约束。

答案 5 :(得分:-3)

SQL服务器支持它 您可以在以下链接中找到有价值的信息

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/