Использование подзапроса в операторе Check в Oracle - PullRequest
20 голосов
/ 01 ноября 2010

Так что я пытался решить это, но кажется, что последняя строка (проверка) не допускает вложенных запросов в нем.Любой способ заставить эту работу Oracle?

CREATE TABLE Tank (
    n_id            int,
    day             date,
    level           int,
    CONSTRAINT pk_w_td PRIMARY KEY (n_id,day),
    CONSTRAINT fk_w_td_tan FOREIGN KEY (n_id) REFERENCES Tanks ON DELETE CASCADE,
    CHECK (level > 0 AND level <= (SELECT capacity FROM Tanks WHERE Tanks.n_id = TanksDay.n_id))
);

Вот информация об ошибке:

Error at Command Line:7 Column:32 Error report: SQL Error: ORA-02251: subquery not allowed here
02251. 00000 -  "subquery not allowed here"
*Cause:    Subquery is not allowed here in the statement.
*Action:   Remove the subquery from the statement.

Ответы [ 5 ]

24 голосов
/ 01 ноября 2010

Существует три основных способа решения этой проблемы, поскольку ограничения CHECK не могут быть основаны на запросе.

Вариант 1: Триггеры

Самый упрощенный подходбыло бы поставить триггер на TANK, который запрашивает TANKS и выдает исключение, если УРОВЕНЬ превышает CAPACITY.Однако проблема такого упрощенного подхода заключается в том, что практически невозможно правильно обрабатывать проблемы параллелизма.Если сеанс 1 уменьшает CAPACITY, тогда сеанс 2 увеличивает УРОВЕНЬ, и затем обе транзакции фиксируются, триггеры не смогут обнаружить нарушение.Это может не быть проблемой, если одна или обе таблицы редко модифицируются, но в целом это будет проблемой.

Вариант 2: Материализованные представления

Вы можете решить проблему параллелизма, создав материализованное представление ON COMMIT, которое присоединяется к таблице TANK и TANKS, а затем создав ограничение CHECK для материализованного представления, которое подтверждает, что LEVEL <= CAPACITY.Вы также можете избежать двойного хранения данных, так как материализованное представление содержит только данные, которые нарушают ограничение.Это потребует материализованных журналов представлений в обеих базовых таблицах, что добавит немного накладных расходов на вставки (хотя и меньше, чем при использовании триггеров).Подтверждение проверки для фиксации времени решит проблему параллелизма, но она представляет небольшую проблему управления исключениями, поскольку операция COMMIT теперь может завершиться ошибкой, поскольку не удалось обновить материализованное представление.Ваше приложение должно быть в состоянии справиться с этой проблемой и предупредить пользователя об этом факте. </p>

Вариант 3: Изменить модель данных

Если у вас есть значениев таблице A, которая зависит от предела в таблице B, это может указывать на то, что предел в B должен быть атрибутом таблицы A (вместо или в дополнение к атрибуту таблицы B).Конечно, это зависит от специфики вашей модели данных, но это часто стоит учитывать.

7 голосов
/ 01 ноября 2010

К сожалению, ограничения CHECK не могут содержать подзапросов - см. документацию .

2 голосов
/ 17 июля 2017

Функция, которую вы ищете, называется утверждениями SQL, , и она еще не реализована в Oracle 12c

1 голос
/ 02 ноября 2010

В ответе Джастина есть несколько хороших идей. Другой способ - обернуть все вставки / обновления в таблицу пакетом (если хотите, TAPI) и выполнить там проверки. Вам нужно убедиться, что все приложения используют ваш TAPI. Вам также потребуется реализовать некоторую настраиваемую блокировку, чтобы защитить ограничение от воздействия одновременной активности.

0 голосов
/ 01 ноября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...