Как вызвать функцию PL SQL в операторе CHECK? - PullRequest
2 голосов
/ 01 марта 2020

Я хотел бы добавить оператор CHECK, который вызывает функцию при вставке новых записей в таблицу. Я использовал следующий пример кода для реализации такой функциональности:

CREATE TABLE customers(
    id NUMBER NOT NULL,
    PRIMARY KEY(id));

CREATE OR REPLACE FUNCTION totalCustomers
RETURN NUMBER IS 
   total NUMBER := 0; 
BEGIN 
   SELECT count(*) into total 
   FROM customers; 

   RETURN total; 
END; 
/

ALTER TABLE customers
ADD CHECK (totalCustomers() < 10);

Когда я запускаю этот запрос в live sql. oracle .com , я получаю следующую ошибку:

ORA-00904: "TOTALCUSTOMERS": invalid identifier.

Как правильно вызывать эту функцию в операторе проверки?

PS Пожалуйста, игнорируйте содержимое функции. Я заменю его нужным содержимым позже.

Ответы [ 3 ]

4 голосов
/ 01 марта 2020

Нет ни одного.

Прямо из Oracle документации:

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/constraint.html#GUID -1055EA97-BA6F-4764-A15F-1024FD5B6DFE

Условия проверочных ограничений не могут содержать следующие конструкции:

.............

  • Вызовы для пользовательских functions

..............

Теперь: вы сказали «не обращайте внимания на фактическое содержание функции». Это не здоровое отношение; содержание тоже имеет значение. Во-первых, в любом случае функция должна быть детерминированной c (ваша - нет) - это проблема, не считая пользовательской функции. Более того, условия в ограничениях могут ссылаться только на значения в одной строке - они не могут быть «табличными» ограничениями, как у вас.

Тогда вы можете задаться вопросом - как можно реализовать «ограничение», подобное ваша? Один из наиболее распространенных методов - создать материализованное представление на основе «select count (*) .....» и наложить ограничение на MV. MV должен refre sh full при коммите. Всякий раз, когда вы изменяете базовую таблицу и делаете коммит. MV обновляется - и если число увеличивается выше 10, изменения отменяются.

2 голосов
/ 01 марта 2020

В своем комментарии к ответу mathguy вы говорите: «Я также пытаюсь сделать так, чтобы период времени новых записей не пересекался с существующими». Я сделал это с материализованными представлениями «refre sh fast on commit». Предупреждение: «быстрое» обновление может быть медленным, если вы не будете осторожны, обратитесь к этому блогу http://www.adellera.it/, особенно в отношении статистики в журнале материализованного представления.

Я предполагаю эксклюзивные даты окончания. Если дата окончания равна нулю, это означает, что диапазон даты и времени продолжается бесконечно. Многие совпадения будут немедленно обнаружены первичным ключом и уникальными ограничениями. Другие будут пойманы во время фиксации ограничением материализованного представления. Обратите внимание, что в конце транзакции у MV никогда не будет строк.

SQL> create table date_ranges (
  2    key1, start_date,
  3    primary key(key1, start_date),
  4    end_date,
  5    unique(key1, end_date),
  6    check(start_date < end_date)
  7  )
  8  as
  9  with a as (select date '2000-01-01' dte from dual)
 10  select 1, dte, dte+1 from a
 11  union all
 12  select 1, dte+1, dte+2 from a
 13  union all
 14  select 1, dte-1, dte from a
 15  union all
 16  select 2, dte+10, dte+11 from a
 17  union all
 18  select 2, dte+12, dte+13 from a
 19  union all
 20  select 2, dte+8, dte+9 from a
 21  /

Table DATE_RANGES created.

SQL> create materialized view log on date_ranges
  2  with sequence, rowid, primary key, commit scn (end_date) including new values
  3  /

Materialized view log DATE_RANGES created.

SQL> create materialized view overlapping_ranges refresh fast on commit
  2  as
  3  select a.rowid arid, b.rowid brid
  4  from date_ranges a, date_ranges b
  5  where a.key1 = b.key1
  6  and a.rowid != b.rowid
  7  and a.start_date < b.end_date
  8  and a.end_date > b.start_date;

Materialized view OVERLAPPING_RANGES created.

SQL> 
SQL> alter materialized view overlapping_ranges
  2  add constraint overlaps_not_allowed check (1=0) deferrable initially deferred
  3  /

Materialized view OVERLAPPING_RANGES altered.

SQL> insert into date_ranges select 1, date '1999-12-30', date '2000-01-4' from dual;

1 row inserted.

SQL> commit;

Error starting at line : 42 in command -
commit
Error report -
ORA-02091: transaction rolled back
ORA-02290: check constraint (STEW.OVERLAPS_NOT_ALLOWED) violated
1 голос
/ 01 марта 2020

Я бы предложил триггер для такого требования.

CREATE OR REPLACE TRIGGER AI_customers 
    AFTER INSERT ON customers 
DECLARE

total NUMBER; 

BEGIN
   SELECT count(*) into total 
   FROM customers; 
   IF total > 10 THEN
      RAISE_APPLICATION_ERROR(-20001, 'Total number of customers must not exceed 10');
   END IF;
END;
/

Обратите внимание, что это триггер STATEMENT LEVEL (нет условия FOR EACH ROW), поэтому вы не можете получить знаменитый "ORA-04091: таблица мутирует, триггер / функция может не видеть это "ошибка

Однако этот триггер имеет некоторые ограничения в многопользовательской среде. Если user_1 вставляет записи в таблицу клиентов, а user_2 также вставляет некоторые записи (до того, как user_1 выполнил COMMIT), вы можете получить более 10 записей в таблице клиентов.

...