Oracle约束

时间:2014-03-14 23:45:40

标签: sql oracle constraints

我正在使用oracle数据库开展一个小型学校项目。我创建了一些表,其中两个是Mobile(Mobile_Number,Status_Flag)Status_Flag显示一个号码是否有效,还有另一个表Owner_Mobile(Owner_Id FK,Mobile_ID FK)。现在我应该编写一个Constraint,如果对应的Status_Flag为指定数字的N,则禁止插入操作。我尝试使用子查询来创建它,但这是不可能的。

当然,约束应该应用于OWNER_MOBILE表。例如,如果我说:INSERT INTO OWNER_MOBILE(25541,042536),则约束应检查Mobile表并查看Mobile 042536是否处于活动状态。如果该数字未激活,则insert语句应生成错误

1 个答案:

答案 0 :(得分:0)

您可以使用触发器或其他PL / SQL API,但您应该考虑ACID事务原则。让我们考虑一下当flag value = 0时应该阻止插入的情况:

SQL> create table mobile (mobile_id int primary key, flag int)
  2  /

SQL> create table owner_mobile(owner_id int,
  2  mobile_id int references mobile(mobile_id))
  3  /

SQL> insert into mobile values (1,1)
  2  /

SQL> commit
  2  /

SQL> create or replace trigger
  2  tr_owner_mobile
  3  before insert on owner_mobile
  4  for each row
  5  declare
  6    l_flag mobile.flag%type;
  7  begin
  8    select flag into l_flag
  9    from mobile where mobile_id = :new.mobile_id;
 10  
 11    if l_flag = 0 then
 12      raise_application_error(-20000, 'Unavalable mobile');
 13    end if;
 14  end;
 15  /

在上面的代码中,我只选择flag并依赖于检索到的值 - 我不关心ACID。

在第一个事务中,我更新标志值但不提交:

SQL> update mobile set flag = 0 where mobile_id = 1;

在第二个交易中,我插入owner_mobile并获得成功:

SQL> insert into owner_mobile values(1,1);

1 row inserted.

接下来,我提交第一个交易,然后提交第二个交易。那我得到了什么:

SQL> select * from mobile;

 MOBILE_ID       FLAG                                                           
---------- ----------                                                           
         1          0                                                           

SQL> select * from owner_mobile;

  OWNER_ID  MOBILE_ID                                                           
---------- ----------                                                           
         1          1    

似乎这不是我的期望。

我可以使用select for update来防止行为不一致:

SQL> update mobile set flag = 1;

1 row updated.

SQL> delete from owner_mobile;

1 row deleted.

SQL> commit;

SQL> create or replace trigger
  2  tr_owner_mobile
  3  before insert on owner_mobile
  4  for each row
  5  declare
  6    l_flag mobile.flag%type;
  7  begin
  8    select flag into l_flag
  9    from mobile where mobile_id = :new.mobile_id
 10    for update;
 11  
 12    if l_flag = 0 then
 13      raise_application_error(-20000, 'Unavalable mobile');
 14    end if;
 15  end;
 16  /

现在也这样做:

SQL> update mobile set flag = 0 where mobile_id = 1;

1 row updated.

第二个事务正在等待,因为父行已被锁定:

SQL> insert into owner_mobile values(1,1);

在第一次交易中提交后,我进入第二次交易:

SQL> insert into owner_mobile values(1,1);
insert into owner_mobile values(1,1)
            *
error in line 1:
ORA-20000: Unavalable mobile 
ORA-06512: at  "SCOTT.TR_OWNER_MOBILE", line 9 
ORA-04088: error in trigger 'SCOTT.TR_OWNER_MOBILE' 

因此,无论您如何实现要求,都必须考虑事务隔离。