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

Какие базы данных 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 поддерживают эту функцию.


person pilcrow    schedule 01.06.2011    source источник


Ответы (6)


Механизм базы данных Access (ACE, Jet и т. д.) поддерживает подзапросы в ограничениях CHECK, но я не решаюсь назвать его СУБД SQL, потому что он не поддерживает начальный уровень Standard SQL-92, а ограничения Access CHECK едва задокументировано MS и группой доступа.

Например, я могу продемонстрировать, что ограничения доступа CHECK проверяются для каждой затрагиваемой строки (в SQL-92 указано, что они должны проверяться после каждого оператора SQL), но является ли это ошибкой или функцией, мы не знаем, поскольку нет документации по этому вопросу. Ссылаться на.


Вот очень простой пример ограничения CHECK, содержащего подзапрос. Он совместим с Full 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 не работают в 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 у него не было никаких функций триггера, а в противном случае некоторые часто используемые таблицы не имели бы истинного ключа (например, ' 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
person onedaywhen    schedule 01.06.2011
comment
+1 MS Доступ? Я с трудом в это верю. :) Можете ли вы показать мне пример рабочего ограничения Access CHECK с подзапросом? - person pilcrow; 01.06.2011
comment
@pilcrow: ну, вы могли бы проголосовать за некоторые из моих ответов на другие вопросы... :) Шучу, у меня уже достаточно голосов, и спасибо за добрые слова. - person onedaywhen; 03.06.2011
comment
Спасибо за пример с "не существует". Это помогло мне. - person Yawar; 08.04.2013
comment
+1 вы знаете, выполняется ли CHECK перед INSERT и UPDATE в Firebird? - person ; 15.10.2013

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

person ypercubeᵀᴹ    schedule 29.03.2012
comment
@Gracchus Можете ли вы предоставить более подробное объяснение ваших требований? (были бы полезны примеры данных или кода) - person ypercubeᵀᴹ; 15.10.2013
comment
большое спасибо! Практично ли заменить внешние ключи проверками с подзапросами? у меня есть таблица с двумя типами данных, которые более или менее равны, за исключением логического флага. чтобы сослаться на первичное условное условие этой таблицы на флаге, должен ли я просто отказаться от внешнего ключа без условного и вместо этого использовать проверки с подзапросом? (извините за удаление моего предыдущего комментария, но это просто поразило меня и, возможно, сделало мой пост с вопросом спорным) - person ; 15.10.2013
comment
@Gracchus Я бы не стал использовать проверки с подзапросами. Я бы предпочел разделить/нормализовать таблицы, чтобы определить правильное ограничение внешнего ключа. Если вы напишете вопрос со своим примером, оставьте комментарий здесь, и я проверю. Или, что еще лучше, напишите вопрос на родственном сайте: dba.se - person ypercubeᵀᴹ; 15.10.2013
comment
огромное спасибо! зачем тебе идти таким путем? (стараюсь избегать болтовни) не могли бы вы опубликовать ссылку, объясняющую ваши рассуждения? еще раз заранее спасибо! - person ; 15.10.2013
comment
@Gracchus Прочитайте ответ onedaywhen на этот вопрос! Извините, я имел в виду этот ответ: Почему СУБД не поддерживает УТВЕРЖДЕНИЕ - person ypercubeᵀᴹ; 15.10.2013

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

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

person gbn    schedule 01.06.2011
comment
Кроме того, предполагается, что ограничения CHECK должны проверяться после каждого оператора SQL (или завершения транзакции, если отложено, но SQL не поддерживает это), но SQL Server проверяет их для каждой затронутой строки. См. Проблемы с ограничениями CHECK - person onedaywhen; 01.06.2011
comment
Хорошая связанная статья, @onedaywhen. - person pilcrow; 01.06.2011

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. По состоянию на 23 июля 2015 г. он все еще находится в статусе «Открыто».

person lloydmeta    schedule 22.07.2015
comment
В каких версиях появилась эта поддержка? (Прошло некоторое время с тех пор, как был задан этот вопрос.) - person pilcrow; 22.07.2015
comment
Извините, я ошибся, видимо, ссылка, которую я разместил для MariaDB, не о самой MariaDB, а скорее о зеркале книги по SQL99, которая размещена на веб-сайте MariaDB. Я отредактирую свой ответ, чтобы отразить этот новый вывод. Однако H2 действительно поддерживает его (проверено локально), но я не знаю, с какой версии. - person lloydmeta; 23.07.2015

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

person Rocjoe    schedule 01.06.2011
comment
Абсолютно триггеры будут работать, но вопрос не в этом. :) - person pilcrow; 01.06.2011
comment
Триггеры не запускаются повторно при изменении таблицы, упомянутой в подзапросе. Поэтому триггеры работают только в том случае, если триггеры настроены для нескольких таблиц или таблицы, упомянутые в подзапросе, гарантированно не изменятся. - person Augustus Kling; 09.11.2011
comment
вы получаете намного больше свободы действий [с TRIGGER] — но вы теряете много функций, которые вы получаете «бесплатно» с объявляемыми ограничениями, и вам приходится управлять ими самостоятельно в триггере: параллелизм/сериализация, оптимизация и т. д., не говоря уже о правильное кодирование запроса целостности данных в первую очередь (множественные ограничения имеют тенденцию быть нетривиальными по сравнению с ограничениями атрибутов). Я склонен думать, больше (локоть) места, чтобы сделать что-то неправильно! - person onedaywhen; 16.03.2012

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/

person gmhk    schedule 01.06.2011
comment
Я почти уверен, что SQL Server не разрешает подзапросы в контрольных ограничениях. Все записи msdn говорят только о ссылке на рассматриваемый столбец (в ограничении на уровне столбца) или на столбцы в таблице (в ограничении на уровне таблицы). Здесь также сообщение на форуме, в котором отправитель сказал, что это не сработало, вместо этого им было предложено использовать триггеры. - person Mike Shepard; 01.06.2011
comment
Нет, эти ссылки показывают только обычные ограничения CHECK, а не подзапросы внутри ограничений CHECK. - person pilcrow; 01.06.2011