在Oracle中将另一个表中的字段用作max-number-rows-with-type约束

时间:2019-04-02 08:25:45

标签: oracle

我有两个表:

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 个答案:

答案 0 :(得分:1)

您不能在约束中使用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中,我为新插入的行在collection中增加了适当的值。在after statement中,我检查集合中的数据是否超出表type_restrictions中的允许范围。

当两个会话插入并发数据时,最后提交的数据会导致异常。