Какие базы данных SQL поддерживают подзапросы в ограничениях CHECK?


Какие базы данных SQL, если таковые имеются, поддерживают подзапросы в ограничениях CHECK?

В настоящее время, насколько мне известно, Oracle, MySQL и PostgreSQL этого не делают.

EDIT

(уточнение, основанное на первоначальных ответах.) Я ищу что-то вроде этого:

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 7

6 ответов:

Движок баз данных Access (ACE, Jet, что угодно) поддерживает подзапросы в ограничениях CHECK, но я не решаюсь назвать его СУБД SQL, потому что он не поддерживает стандарт начального уровня SQL-92, а ограничения доступа 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 (некоторые допустимые CHECKs сбой доступа с ужасным сбоем, который требует перезапуска моей машины: (но не работает должным образом. Идея состоит в том, чтобы разрешить только две строки в таблице (или нулевые строки: ограничения не проверяются для пустой таблицы):

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 в папке temp создает таблицы, данные и тесты, которые работают/не работают с ограничениями (подсказка: установите точку останова, выполните шаг по коду, прочтите комментарии):

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

В документации Firebird говорится, что она разрешает подзапросы в ограничениях CHECK.

SQL Server 2000 + разрешает UDFs, содержащие запросы: вы не можете использовать подзапросы напрямую

Однако они не являются параллельными при высоких нагрузках

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-07-23, он все еще находится в" открытом " состоянии.

Вполне уверен, что триггер будет работать в каждой из упомянутых вами баз данных, и вы получите гораздо больше "локтевого пространства" для разработки вашего ограничения.

SQL server поддерживает его Вы можете найти ценную информацию по следующей ссылке

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

Они говорят, что POSTGRESQL также поддерживает его

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

DB2 поддерживает ограничение проверки

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