第二章: Oracle 约束

2.1 什么是约束?

约束是数据库能够实施业务规则以及保证数据遵循实体-关系模型的一种手段。

如果违法约束,将自动回滚出现问题的整个语句,而不是语句中的单个操作,也不是整个事务。

2.2 约束的语法:

列级定义: 只能引用一个列,表中可以有多个列级约束。

表级定义: 引用一个或多个列,通常用来定义主键。

追加定义: 建表后,再通过ALTER TABLE命令追加的约束。

查看约束的两个数据字典视图

SCOTT@orcl> select * from user_constraints;

SCOTT@orcl> select constraint_name,column_name,table_name from user_cons_columns;
 

2.3 五种约束的语法。

2.3.1 非空约束

列级定义:

追加非空约束:

not null 约束比较特殊,一般只是列级定义和表外定义,当使用表外(追加)时,要使用modify关键字。

SCOTT@orcl> alter table emp1 modify ename not null;

Before Change :

Change After:

2.3.2 唯一性约束(唯一性约束允许列中输入空值)

列级定义:

SCOTT@orcl> create table a1(id number(2) unique,name varchar2(4));

表级定义:

SCOTT@orcl> create table a2(id number(2),name varchar2(4),constraint id_uk unique(id));

追加定义:

SCOTT@orcl> alter table a2 add constraint id_uk unique(id);

Note: 唯一加非空约束可以多列。unique 和not null 之间没有 ”,“

SCOTT@orcl> create table a (id int unique not null,name char(10) unique not null);

2.3.3 主键约束

Note :

1) 每个表只能建立一个主键约束,primary key = unique key + not null, 主键约束可以是一列,也可以是组合多列。

2) 主键列上需要索引,如果该列没有索引会自动建立一个unqiue index,如果该列上已有索引(非唯一也可以),那么就借用这个索引,由于是借用的索引,当主键约束被删除后,借用的索引不会被删除。 同理,多列组合的主键,需要建立多列组合索引,而多列主键的单列上还可以另键单列索引。

3) 主键约束和唯一约束不能同时建立在一个列上。

主键约束的六种写法

列级定义

1)SCOTT@orcl> create table u1(id char(10) primary key,name char(20)); -- 主键名字,Oracle 定义的

2)SCOTT@orcl> create table u2(id char(10) constraint pk_u2 primary key,name char(20));  -- 主键名字,自己指定的。

表级定义

3) SCOTT@orcl> create table u3 (id char(10),name char(20),primary key(id));--主键名字,Oracle 定义的

4) SCOTT@orcl> create table u4 (id char(10),name char(20),CONSTRAINT pk_u4 primary key(id)); -- 主键名字,自己指定的。

追加定义

SCOTT@orcl> alter table u5 add primary key(id); -- desc u5 会发现u5 已经自动加上了not null 属性。

SCOTT@orcl> alter table u5 add CONSTRAINT pk_u5 primary key (id); --- 表外,后来加上主键。

关于主键和索引关联的问题。

下面这两句话是一样的效果,因为缺省情况下 id 列已经有索引 t_id 了,建主键时就会自动 用这个索引(考点)

SCOTT@orcl> alter table t add constraint pk_id primary key (id);

SCOTT@orcl> alter table t add constraint pk_id primary key (id) using index t_idx;

SCOTT@orcl> select constraint_name,table_name,index_name from user_constraints;

SCOTT@orcl> alter table t drop constraint pk_id; // 删除了约束,索引还在,本来就是借用的索引。
 

SCOTT@orcl> drop table t purge; --- t_idx是和t表关联的关键字purge 使表和索引一并永久删除了。

也可以使用 using 子句在建表建、建约束、建索引一条龙下来,当然 primary key 也会自动 使用这个索引(考点)。删除该约束,索引还存在。

SCOTT@orcl> create table t(id int,name char(10),constraint pk_id primary key(id) using index (create index t_idx on t(id)));

Table created.

Elapsed: 00:00:00.04
SCOTT@orcl> select constraint_name,table_name,index_name from user_constraints;

CONSTRAINT_NAME                TABLE_NAME                     INDEX_NAME
------------------------------ ------------------------------ ------------------------------
SYS_C0010370                   A                              <null>
SYS_C0010369                   A                              <null>
SYS_C0010364                   STUD                           <null>
SYS_C0010063                   ???                            <null>
SYS_C0010365                   EMP1                           <null>
FK_DEPTNO                      EMP                            <null>
BIN$kovfkh2FKOzgUAB/AQAemA==$0 BIN$kovfkh2HKOzgUAB/AQAemA==$0 BIN$kovfkh2GKOzgUAB/AQAemA==$0
BIN$kovfkh2JKOzgUAB/AQAemA==$0 BIN$kovfkh2LKOzgUAB/AQAemA==$0 BIN$kovfkh2KKOzgUAB/AQAemA==$0
ID_UK                          A2                             ID_UK
PK_DEPT                        DEPT                           PK_DEPT
PK_EMP                         EMP                            PK_EMP
PK_U2                          U2                             PK_U2
PK_U4                          U4                             PK_U4
PK_U5                          U5                             PK_U5
SYS_C0010064                   ???                            SYS_C0010064
SYS_C0010366                   A1                             SYS_C0010366
SYS_C0010371                   A                              SYS_C0010371
SYS_C0010372                   A                              SYS_C0010372
SYS_C0010373                   U1                             SYS_C0010373
SYS_C0010375                   U3                             SYS_C0010375
PK_ID                          T                              T_IDX

21 rows selected.

Elapsed: 00:00:00.44
SCOTT@orcl> select index_name from user_indexes;

INDEX_NAME
------------------------------
T_IDX
PK_U5
PK_U4
SYS_C0010375
PK_U2
SYS_C0010373
SYS_C0010371
SYS_C0010372
ID_UK
SYS_C0010366
SYS_C0010064
SYS_IL0000070368C00003$$
PK_EMP
PK_DEPT

14 rows selected.

Elapsed: 00:00:00.03
SCOTT@orcl> 

2.3.4 外键约束(引用完整性约束)

作用:是为了和同一个表或其他表的主关键字(或唯一关键字)建立联系关系,外键值必须和父表中值匹配或者为空值。

Note:

1) 外键约束和unique 约束都可以有空值。

2) 外键需要参考主键约束,但也可以参考唯一键约束。

3) 外键和主键一般分别在两个表中,但也可以同处在一个表中。

SCOTT@orcl> create table emp1 as select * from emp;

SCOTT@orcl> create table dept1 as select * from dept;  

列级定义

SCOTT@orcl> alter table dept1 add constraint pk_dept1 primary key(deptno);

Table altered.

Elapsed: 00:00:01.02
SCOTT@orcl> create table emp100(empno int,deptno int references dept1(deptno),deptno2 int);

Table created.

Elapsed: 00:00:00.23

//外键的列级定义有点特殊,使用references 不使用foreign key 关键字。

表级定义

SCOTT@orcl> create table emp200 (empno int,deptno int,sal int,foreign key(deptno) references dept1(deptno));

追加定义

SCOTT@orcl> alter table emp1 add constraint fk_emp1 foreign key(deptno) references dept1(deptno);

关于 ON DELETE CASCADE 关键字 

SCOTT@orcl> delete from dept1 where deptno = 30;
delete from dept1 where deptno = 30
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_EMP1) violated - child record found


Elapsed: 00:00:00.09
SCOTT@orcl> alter table emp1 drop constraint fk_emp1;

Table altered.

Elapsed: 00:00:00.16
SCOTT@orcl> alter table emp1 add constraint fk_emp1 foreign key(deptno) references dept1(deptno) on delete cascade;

Table altered.

Elapsed: 00:00:00.05
SCOTT@orcl> select constraint_name,constraint_type,status,delete_rule from user_constraints;

CONSTRAINT_NAME                C STATUS   DELETE_RU
------------------------------ - -------- ---------
SYS_C0010063                   C ENABLED  <null>
SYS_C0010364                   C ENABLED  <null>
BIN$kpGme3io+O7gUAB/AQAiiQ==$0 C ENABLED  <null>
SYS_C0010369                   C ENABLED  <null>
SYS_C0010370                   C ENABLED  <null>
SYS_C0010389                   R ENABLED  NO ACTION
SYS_C0010390                   R ENABLED  NO ACTION
FK_EMP1                        R ENABLED  CASCADE
FK_DEPTNO                      R ENABLED  NO ACTION
PK_DEPT                        P ENABLED  <null>
PK_EMP                         P ENABLED  <null>
SYS_C0010064                   P ENABLED  <null>
SYS_C0010366                   U ENABLED  <null>
BIN$kovfkh2FKOzgUAB/AQAemA==$0 U ENABLED  <null>
ID_UK                          U ENABLED  <null>
SYS_C0010371                   U ENABLED  <null>
SYS_C0010372                   U ENABLED  <null>
SYS_C0010373                   P ENABLED  <null>
PK_U2                          P ENABLED  <null>
SYS_C0010375                   P ENABLED  <null>
PK_U4                          P ENABLED  <null>
BIN$kovfkh2JKOzgUAB/AQAemA==$0 P ENABLED  <null>
PK_U5                          P ENABLED  <null>
PK_ID                          P ENABLED  <null>
PK_DEPT1                       P ENABLED  <null>

25 rows selected.

Elapsed: 00:00:01.00
SCOTT@orcl> 

注意: delete_rule 列会显示 CASCADE,否则显示 NO ACTION(考点) 

测试: delete from dept1 where deptno=30 
 
再查看 emp1 表的 deptno 已经没有 30 号部门了,如果再对 dept1 的操作进行 rollback,emp1 的子记录也随之 rollback 
 
ON DELETE CASCADE 要慎用,父表中删除一行数据就可能引发子表中大量数据丢失。 
 
为此,还有 on delete set null 子句,顾名思义是子表不会删除(丢失)记录,而是将外键 的值填充 null。

如果 disable dept1 主键约束并使用级联 cascade 关键字,则 emp1 的外键也会 disable, 若 再次 enable dept1 主键,则 emp1 外键任然保持 disable

SCOTT@orcl> alter table dept1 disable constraint pk_dept1 cascade;

Table altered.

Elapsed: 00:00:00.29
SCOTT@orcl> alter table dept1 enable constraints pk_dept1;

Table altered.

Elapsed: 00:00:00.09
SCOTT@orcl> select constraint_name,constraint_type,status,delete_rule from user_constraints;

CONSTRAINT_NAME                C STATUS   DELETE_RU
------------------------------ - -------- ---------
SYS_C0010063                   C ENABLED  <null>
SYS_C0010364                   C ENABLED  <null>
SYS_C0010369                   C ENABLED  <null>
SYS_C0010370                   C ENABLED  <null>
BIN$kpGme3io+O7gUAB/AQAiiQ==$0 C ENABLED  <null>
FK_DEPTNO                      R ENABLED  NO ACTION
PK_DEPT                        P ENABLED  <null>
PK_EMP                         P ENABLED  <null>
SYS_C0010064                   P ENABLED  <null>
SYS_C0010366                   U ENABLED  <null>
BIN$kovfkh2FKOzgUAB/AQAemA==$0 U ENABLED  <null>
ID_UK                          U ENABLED  <null>
SYS_C0010371                   U ENABLED  <null>
SYS_C0010372                   U ENABLED  <null>
SYS_C0010373                   P ENABLED  <null>
PK_U2                          P ENABLED  <null>
SYS_C0010375                   P ENABLED  <null>
PK_U4                          P ENABLED  <null>
BIN$kovfkh2JKOzgUAB/AQAemA==$0 P ENABLED  <null>
PK_U5                          P ENABLED  <null>
PK_ID                          P ENABLED  <null>
PK_DEPT1                       P ENABLED  <null>
SYS_C0010389                   R DISABLED NO ACTION
SYS_C0010390                   R DISABLED NO ACTION
FK_EMP1                        R DISABLED CASCADE

25 rows selected.

Elapsed: 00:00:01.00
SCOTT@orcl> drop table dept1 purge;
drop table dept1 purge
           *
ERROR at line 1:
ORA-02449: unique/primary keys in table referenced by foreign keys


Elapsed: 00:00:00.01
SCOTT@orcl> drop table dept1 cascade constraint purge;

Table dropped.

Elapsed: 00:00:00.12
SCOTT@orcl> --这时外键约束也被删除了。
SCOTT@orcl> 

2.3.5 CHECK约束

列级定义:

SCOTT@orcl> create table emp100(empno int,sal int check(sal>0),comm int);

表级定义:

SCOTT@orcl> create table emp200(empno int,sal int,comm int,check(sal>1000));

追加定义:

SCOTT@orcl> alter table emp200 add constraint e_no_ck check (empno is not null);

2.3.6 check 约束中的表达式中不能使用变量日期函数。

SCOTT@orcl> alter table emp1 add  constraint chk check(hiredate<sysdate);
alter table emp1 add  constraint chk check(hiredate<sysdate)
                                                    *
ERROR at line 1:
ORA-02436: date or system variable wrongly specified in CHECK constraint


Elapsed: 00:00:00.10
SCOTT@orcl> alter table emp1 add constraint chk check(hiredate<to_date('2000-01-01','yyyy-mm-dd'));//这句是可以的。

Table altered.

Elapsed: 00:00:00.03

2.3.7 级联约束

SCOTT@orcl> CREATE TABLE test2 (     pk NUMBER PRIMARY KEY,     fk NUMBER,     col1 NUMBER,     col2 NUMBER,     CONSTRAINT fk_constraint FOREIGN KEY (fk) REFERENCES test2,     CONSTRAINT ck1 CHECK (pk > 0 and col1 > 0),     CONSTRAINT ck2 CHECK (col2 > 0)     ) ;

Table created.

Elapsed: 00:00:00.07
SCOTT@orcl> ALTER TABLE test2 DROP (col2); 

Table altered.

Elapsed: 00:00:00.39
SCOTT@orcl> ALTER TABLE test2 DROP (fk);

Table altered.

Elapsed: 00:00:00.88
SCOTT@orcl> ALTER TABLE test2 DROP (pk);
ALTER TABLE test2 DROP (pk)
                        *
ERROR at line 1:
ORA-12991: column is referenced in a multi-column constraint


Elapsed: 00:00:00.04
SCOTT@orcl> ALTER TABLE test2 DROP (col1); 
ALTER TABLE test2 DROP (col1)
                        *
ERROR at line 1:
ORA-12991: column is referenced in a multi-column constraint


Elapsed: 00:00:00.00
SCOTT@orcl> ALTER TABLE test2 DROP (pk) cascade constraint;

Table altered.

Elapsed: 00:00:00.06
SCOTT@orcl> 

CASCADE CONSTRAINTS 将丢弃在删除列上的唯一键或主键约束。

2.3.8 约束的四种状态。

enable validate     :无法输入违反约束的行,而且表中所有行都要符合约束

enable novalidate   :表中可以存在不合约束的状态, ,但对新加入数据必须符合约束条件.  

disable novalidate  :可以输入任何数据,表中或已存在不符合约束条件的数据.

disable validate    :不能对表进行插入/更新/删除等操作,相当于对整个表的 read only 设定. 
 
更改约束状态是一个数据字典更新,将对所有 session 有效

举例:   1)enable novalidate 这种组态的用法 
 
常用于当在表中输入了一些测试数据后﹐而上线后并不想去清除这些违规数据﹐但想从此开 始才执行约束。 
 
假设已经建立了一个 emp1 表,也插入了数据,如果有一天想在 empno 上加入 primary key 但是之前有不符合(not null+unique)约束的,怎样才能既往不咎呢? 
 
create table emp1 as select * from emp;(CTAS 语句使约束并没有考过来) 
 
update emp1 set empno=7788 where empno=7369;(设置一个重号) 
 
alter table emp1 add constraint pk_emp1 primary key (empno);因要检查主键唯一索 引,拒绝建立此约束。 

 
任何违反(not null+unique)的 update 或 insert 操作将被拒绝。 
 
alter table emp1 add constraint pk_emp1 primary key (empno) enable novalidate; (这句话也不行,为什么?原因是唯一索引在捣乱) 
 
create index empno_index on emp1(empno);建一个索引,一定要一个普通索引,不能是唯 一索引,普通索引不受 unquie 的限制) 
 
alter table emp1 add constraint pk_emp1 primary key (empno) enable novalidate; (这句话可以了)。 
 


从此之后,这个列的 DML 操作还是要符合(not null+unique)。 
 
2) disable validate 组态的特点: 
 
SQL> alter table emp1 add constraint pk_emp1 primary key(empno);

SQL> select index_name from user_indexes; 
 
INDEX_NAME

------------------------------

PK_EMP1 PK_EMP PK_DEPT 
 
SQL> alter table emp1 modify constraint pk_emp1 disable validate; //主键索引将 自动删除

SQL> select index_name from user_indexes; 
 
INDEX_NAME

------------------------------

PK_EMP PK_DEPT 
 
SQL> update emp1 set sal=8000;

update emp1 set sal=8000

* 第 1 行出现错误:

ORA-25128: 不能对带有禁用和验证约束条件 (SCOTT.PK_EMP1) 的表进行插入/更新/删除 
 
SQL> alter table emp1 modify constraint pk_emp1 enable validate; 
 
表已更改。 
 
SQL> select index_name from user_indexes; 

 
 
INDEX_NAME

------------------------------

PK_EMP1 PK_EMP PK_DEPT 
 
3)将 disable novalidate,enable novalidate 和 enable validate 三种状态组合起来的用 法: 
 
这种组合,可以避免因有个别不符合条件的数据而导致大数据量的传输失败。 假设有 a 表是源数据表,其中有空值,b 表是 a 表的归档表,设有非空约束,现要将 a 表数据 (远程)大批量的插入到 b 表(本地)。 
 
alter table b modify constraint b_nn1 disable novalidate; //先使 B 表非空约束无 效。

insert into b select * from a;     //大批数据可以无约束插入,空值也 插进 B 表里了。

alter table b modify constraint b_nn1 enable novalidate; //既往不咎,但若新输 入数据必须符合要求。

update b set channel='NOT KNOWN'where channel is null;  //将所有空值填充了, 新老数据都符合要求了。

alter table b modify constraint b_nn1 enable validate;          //最终是约束使能 +验证生效,双管齐下。 

2.3.9 延迟约束。

可延迟(deferrable)可以通过查询 User_Constraints 视图获得当前所有关于约束的系统信 息. 
 
查看 user_constraints 中的两个字段 
 
Deferrable  //是否为延迟约束 值为:Deferrable 或 Not Deferrable(缺省).

Deferred    //是否采用延迟 值为:Immediate(缺省)或 Deferred.                                                                                                     关于 Deferrable 可延迟,提醒以下几点: 
 

1)约束的默认方式下是:enable/validate 和 Not Defferrable。 
 
2)如果创建约束时没有指定 deferrable 那么无法在后来使约束成为延迟约束(只有通过重 建约束时再指定它是延迟约束) 
 
3)一个约束只有被定义成 deferrable,那么这个约束 session 级才可以在 deferred 和 immediate 两种状态间相互转换 

 
 
例: 
 
SQL> alter table emp1 add constraint chk_sal check(sal>500) deferrable; 
 
已经将 chk_sal 延迟设为可延迟了,在此基础上可以有两种面向 session 的方案 
 
SQL>set constraint chk_sal immediate;     //约束不延迟,插入数据立刻检查约束 SQL>set constraint chk_sal deferred;      //约束延迟,提交时将整个事务一起检查约 束 
 
也可以在建立约束时一次性指定系统级的延迟约束   alter table emp1 add constraint chk_sal check(sal>500) deferrable initially immediate; 或 alter table emp1 add constraint chk_sal check(sal>500) deferrable initially deferred; 
 
Note: 
 
1)使用 set immediate 和 deferred 的切换只影响当前会话,而 initially 状态将应用于所 有会话。 2) 延迟约束时,一旦有一条 DML 语句违反了约束,整个提交都将失败,全军覆没。

发布了39 篇原创文章 · 获赞 4 · 访问量 3481

猜你喜欢

转载自blog.csdn.net/u011868279/article/details/100844771