Используйте поля из другой таблицы как ограничение max-number-row-with-type в oracle - PullRequest
0 голосов
/ 02 апреля 2019

У меня есть две таблицы:

CREATE TABLE users (
   user_id          INT(7) NOT NULL,
   restricted_type  VARCHAR(64) NOT NULL
)
CREATE TABLE type_restrictions (
   name           VARCHAR(64) NOT NULL,
   restriction    INT NOT NULL
)

Я хочу проверить на вставке, что не более restriction пользователей с restricted_type = type_restriction.name.

На данный момент я вставляю данные с этим запросом:

INSERT INTO users (user_id, restricted_type) SELECT <id>, <type> FROM DUAL
       WHERE NOT EXISTS (
         SELECT 1
         FROM type_restrictions T
         WHERE T.name = <type> AND T.restriction < (
           SELECT COUNT(*)
           FROM users U
           WHERE U.user_id = <id> AND U.restricted_type = <type>)
       )

Но с двумя или более параллельными запросами можно получить больше пользователей с restricted_type, чем фактическое ограничениедля этого типа.

Есть ли способ заставить такое ограничение работать?(Кроме того, я всегда вставляю только одну строку в запрос, если это помогает)

1 Ответ

1 голос
/ 02 апреля 2019

Вы не можете использовать select ... в ограничении. Вы не можете выбрать из таблицы, в которую вы вставляете в обычном триггере. Что ты можешь сделать? Материализованное представление (вероятно, я не уверен) или составной триггер. Вот моя (рабочая) попытка:

create or replace trigger trg_users_restrict 
  for insert on users compound trigger

  type tt is table of number index by varchar2(5);
  vt tt;
  i varchar2(5);
  v_max int;

before statement is 
begin 
  for r in (select restricted_type, count(1) cnt from users group by restricted_type) 
  loop
    vt(r.restricted_type) := r.cnt;
  end loop;
end before statement;

after each row is
begin
  begin
    vt(:new.restricted_type) := vt(:new.restricted_type) + 1;
  exception when no_data_found then 
    vt(:new.restricted_type) := 1;
  end;
end after each row;

after statement is
begin
  i := vt.first;
  while i is not null loop
    select nvl(max(restriction), 0) into v_max 
      from type_restrictions where name = i;
    if vt(i) > v_max then
      raise_application_error( -20001, 
        'maximum number exceeded for restriction type ' || i );
    end if;
    i := vt.next(i);
  end loop;
end after statement;

end trg_users_restrict;

В before statement Я сгруппировал данные из таблицы users в коллекцию. В after each row я увеличил правильные значения в коллекции для вновь вставленных строк. В after statement я проверяю, превышают ли данные в коллекции допустимые диапазоны в таблице type_restrictions.

Когда два сеанса вставляют совпадающие данные, то это, которое фиксирует последнее, вызывает исключение.

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