WHCSRL 技术网

教你如何成为Oracle 10g OCP - 第九章 对象管理


第9章  对象管理

 

对象 -- 逻辑意义上的概念,如表,索引,procedure,function等都是对象。

Schema -- 这些对象放在某个用户下,这些对象也叫schema对象,如果
用户下没有对象,该用户就不存在schema . 当用户下具有对象了,该
用户才具有schema .

普通表 -- 一般表都会对应一个segment , 表是作为逻辑意义上存放数据
的地方,物理上来说数据存放在segment中。 表的作用在于方便我们更新
对应的segment中的数据。 

 

 

表的分类:

1. 普通表 -- 最常见的表,一个表对应一个segment. 其实普通表还有一个
名称是Heap Organized Table堆组织表,是用来对应索引组织表的,堆组织
表中的数据行无序存储。

2. 分区表 -- 这种表为虚拟表,表没有对应的segment, 每个分区对应一个
segment, 建议一般将来可能大于2G左右的表都要考虑使用分区表。

3. 索引组织表(IOT) -- 也是一种虚拟表,简称IOT, 必须要有一个主键索引,
同时,所有的数据与主键列一起存放在主键索引的叶子节点里,IOT表不对应
Segment, 而是通过索引来对应segment .

4. 簇表(Cluster Table) -- 虚拟表,如果有两个主明细关系的表,比如定
单主表和明细表,这两个表经常进行关联查询,这时我们可以将他们创建成
簇表,首先我们要创建一个簇,对应一个segment, 然后创建主明细表,并
将主明细表根据关联字段与簇建立联系,向两个表插入数据时,它们会根据
关联字段共同存放在一个block中,也就是一个block存放了两个表的数据,
这样关联查询两表时只需要扫描一个block。簇表本身不对应segment,而是
通过簇来对应segment 的。

 

 

9.1.1 创建普通表

char(5)类型,如果插入abc字符,存储在库中是'abc  ',长度照样是5位。

创建表的时候,如果表空间启用了ASSM(Auto Segment Space Manange), 那么pctused失效。

initrans 默认该表的数据块头部ITL槽的个数(预先分配的),而maxtrans表示最多有多少ITL.
10g或以后版本,maxtrans被废弃,默认都为255,如果块的空闲空间不足以让ITL扩展到255个,
那么最大只能是受限于空闲空间,如果空闲空间足够扩展到255个,那么最大只能是255个ITL,
也就是取可扩展空闲空间和255中较小的那个作为限制。
(备注: ITL - Interested Transaction List) 

对于buffer pool来说,我们可以设置该表进入哪个buffer cache,比如keep buffer cache
或者default buffer cache及recycle buffer cache 等。

 


典型建表的例子 :


CREATE TABLESPACE LOG_DATA DATAFILE
  '/ocfs_data/mxdell/log_data01.dbf' SIZE 4001M AUTOEXTEND OFF,
  '/ocfs_ctrl_redo/mxdell/log_data02.dbf' SIZE 2048064K AUTOEXTEND OFF
LOGGING
ONLINE
PERMANENT
EXTENT MANAGEMENT LOCAL UNIFORM. SIZE 20M
BLOCKSIZE 16K
SEGMENT SPACE MANAGEMENT AUTO       -- ASSM
FLASHBACK ON;

 

CREATE TABLE DFMS.EDIBTO850_MAIN
(
  CONTROLNUMBER     VARCHAR2(40 BYTE)  NOT NULL,
  ISA06             VARCHAR2(50 BYTE),
  CREATE_TIME       DATE        DEFAULT sysdate
)
TABLESPACE LOG_DATA
PCTUSED    0            --- 使用ASSM, pctused参数失效
PCTFREE    10
INITRANS   1
MAXTRANS   255
STORAGE    (
            INITIAL          10M
            NEXT             20M
            MINEXTENTS       1
            MAXEXTENTS       UNLIMITED
            PCTINCREASE      0
            BUFFER_POOL      DEFAULT
           )
LOGGING
NOCOMPRESS
NOCACHE   
NOPARALLEL
MONITORING;

 

解释:

-- create tablespace 中的flashback on 及 flashback off .

对于create tablesapce中的FLASHBACK ON,默认情况下创建tablespace就是
启动flashback, 指定将tablespace放在FLASHBACK模式下,Oracle会为这个
tablespace保存闪回日志数据,那么在做FLASHBACK DATABASE 的时候,能够
应用到此表空间上,如果我们不设置flashback模式,默认是FLASHBACK ON. 

指定FLASHBACK OFF将使对应的tablespace不在FLASHBACK模式下,Oracle将
不会为它保存任何的闪回日志数据,在做FLASHBACK DATABASE之前,我们必
须先将这个表空间中的数据文件offline或者drop他们,或者还有一种方式,
你可以将整个tablespace offline, 在以上任何一种情况下,数据库不删除存
在的闪回日志 。

备注一下: flashback database默认情况下是off的。

 


-- create table 

log_data表空间启用了ASSM, 那么建表的时候,没有明确指定pctused, 默认
pctused是0,pctfree默认还是10,INITRANS 1, MAXTRANS 255 表示该表的
(每个)数据块头部最小和最大的ITL槽(Interested Transaction List), 一般
为了优化ITL,可以设置INITRANS为3或4即可(足够)。关于ITL可以参考下面的链
接。PCTINCREASE在autoallocate下起作用,表示下一个extent大小为前一个
的(1+pctincrease%)倍 ,在uniform. size设置的tablespace下不起作用,Oracle
建议使用PCTINCREASE 的零设置 . 参考:
http://space.itpub.net/35489/viewspace-671133 

 

BUFFER POOL ----- 
包括DEFAULT、KEEP、RECYCLE三个POOL,其大小分别对应三个参数:DB_CACHE_SIZE,
DB_KEEP_CACHE_SIZE, DB_RECYCLE_CACHE_SZIE,这三个POOL相互独立,没有包含的关系。
语法如下:
create/alter table ...... storage(buffer_pool default);
create/alter table ...... storage(buffer_pool keep);
create/alter table ...... storage(buffer_pool recycle); 

default -- 普通的buffer cache .

recycle -- 通常将偶尔访问的大表放入recycle pool中,这样做是为了防止它的一次访问
就将那些频繁使用且应该长时间留在buffer cache中的对象清除出内存。

keep -- 当表被读入的时候,放入keep pool,keep pool 中的数据会尽量长时间的保留,
保留的时间长短依据keep pool的负载而定。对于放入KEEP POOL中的表数据,要么全表放
入,要么全表都out,不会只放入一部分数据。通常将经常访问的对象放入keep pool中。

 


KEEP POOL 及 CACHE 参数的联系及区别 ---

建表时我们在最后可以看到有NOCACHE(默认),如果是cache呢,与storage中的
keep pool 有什么区别呢? 

create/alter table ...... cache;
设置表为cache时,当全表扫描时,将对象数据放入default pool的LRU热端(备注:
如果是nocache,则放入LRU冷端,很快就会被age out),cache适合频繁使用的小
表, 如果有太多的表cache, LRU链表会很拥挤,LRU机制也会受影响,所以该把表
放入keep pool,而且cache子句也不适合大表,因为有可能这个大表会占据整个
LRU链表。 

当BUFFER_POOL和CACHE同时使用时,KEEP比NOCACHE有优先权。BUFFER_POOL用来指
定存贮的缓冲池,而CACHE/NOCACHE指定存储的方式。建表时候不注明的话,NOCACHE
是默认值。


CACHE
For data that is accessed frequently, this clause indicates that the blocks
retrieved for this table are placed at the most recently used end of the
least recently used (LRU) list in the buffer cache when a full table scan
is performed. This attribute is useful for small lookup tables.

NOCACHE 
For data that is not accessed frequently, this clause indicates that the
blocks retrieved for this table are placed at the least recently used end
of the LRU list in the buffer cache when a full table scan is performed.
NOCACHE is the default for LOB storage.


附注:对于8i,需要设置参数buffer_pool_keep,还要修改 db_block_lru_latches,
该参数默认是1,无法使用buffer_pool_keep。该参数应该比2*3*CPU数量少,但是要
大于 1,才能使用buffer_pool_keep。buffer_pool_keep从db_block_buffers中分配,
因此也要小于db_block_buffers。

 

 

NOCOMPRESS  ---
这个设置只对heap-organized tables即普通表有作用,用于压缩data segments,这个
参数对于哪些update及insert操作比较少的data warehouses 特别有用,COMPRESS 设置
启动 table compression.  关键字NOCOMPRESS 关闭 table compression. NOCOMPRESS
是默认值。 当设置为COMPRESS时,Oracle尝试在 direct-path INSERT 操作的时候压缩
数据,imp操作不支持 direct-path INSERT 操作,因此不能在压缩格式下imp数据。


NOPARALLEL --- 
指定NOPARALLEL用于串行执行,默认为NOPARALLEL。如果你想oracle选择一个并行度,
它等于所有实例上的可用的CPU数乘以初始化参数 PARALLEL_THREADS_PER_CPU 的值。


PARALLEL integer ---

指定parallel度的值,它是用于parallel操作中的并行线程,每个并行线程可能使用一个或
两个parallel execution servers,一般来说,Oracle会计算最佳并行度,所以我们没有必要
指定这个值 。

 


MONITORING ---  

在早期版本中,可以使用这些子句去开启或关闭在table上的修改统计(监控),这个子句已经被摒弃。
如果以前你通过DBMS_STATS 包来收集表上的修改统计(in GATHER AUTO or GATHER STALE mode),
那么现在你不必做任何事情,现在Oracle数据库自动收集这些统计信息,在现有的代码中MONITORING
和 NOMONITORING 不会引起错误。 

如果因为性能原因,你不想在任何tables上收集修改统计信息,那么你应该设置初始话参数STATISTICS_LEVEL
为BASIC, 但是,小心,这样做会失去很多管理上的方便(比如自动统计信息) 。

 

 


数据行 ---

表创建好了之后,我们就可以向表里插入数据了,每个数据行在数据块中的结构如下:

[行头部]+[列长度]+[列数据]+[列长度]+[列数据]+[列长度]+[列数据]+..... 

行头部 -- 存放了该行包含的列的数量,以及锁定标记等,当某个事务更新某行记录
时,会在行头部记录所使用的ITL槽号 (表的数据块头部ITL槽)以及锁定标记。

行目录(row directory) -- 在数据块头部存在一个结构,即行目录,为这个块中的
每个数据行都记录了一个条目,每个条目就指向该记录的行头部,还包括记录了这个
块中的row数量及table数量,每个行的在块中的偏移量(距离头部)等等, 所以Oracle
能够区分不同的行。

 


ROWID列 --- 

ROWID是一个伪列,该列的值并没有真正的保存在数据块中,但是可以查询并显示出来。


ROWID的格式 OOOOOOFFFBBBBBBRRR , OOOOOO表示行所在的对象号,FFF表示行所在文件号,
BBBBBB表示该行所在的数据块号,RRR表示行在数据块中的行号,rowid采用64进制来表示,

SQL> select  rowid , book_id  from  books ;

ROWID                 BOOK_ID
-----------------------------------------
AAAM0hAAEAAAAGnAAA          1

可以看出插入的记录的rowid为 [AAAM0h][AAE][AAAAGn][AAA]

我们可以通过dbms_rowid包来验证:

select dbms_rowid.rowid_relative_fno('AAAM0hAAEAAAAGnAAA') as 'File No' from dual;
select dbms_rowid.rowid_block_number('AAAM0hAAEAAAAGnAAA') as 'File No' from dual;
select dbms_rowid.rowid_row_number('AAAM0hAAEAAAAGnAAA') as 'File No' from dual;
select dbms_rowid.rowid_relative_fno('AAAM0hAAEAAAAGnAAA') as 'File No' from dual;

 


9.1.2  管理普通表


扩展表 - 

有时候我们需要主动扩展一个表所占用的空间,或者要将一个表分布到多个数据文件上从而
将I/O分散到多个磁盘上,可以执行以下命令:

alter table books allocate extent (size 10M  datafile '/data/orcl/users02.dbf'); 
alter table books allocate extent;  表示在当前数据文件中扩展extent ;


重整表 - 

有时候我们需要消除表的数据块级别的碎片(即每个数据块含的数据行太少,比如100行分布在
100个数据块中,我们称为稀疏表),或者一个表从目前的表空间转移到另外一个表空间。

稀疏表产生的原因:  
表中存在很多insert及delete操作,在表的segment header中记录了一个值HWM(High Water Mark),
HWM表示当前segment中使用的最后一个数据块的位置,单segment中插入数据,不断扩展数据块,
需要不断增加HWM值. 使用HWM的好处在于,用户select count(*) 或其他语句引起全表扫描时,
服务器在扫描segment过程中,只扫描到HWM为止,HWM后面的数据块还没有被使用过,不含有数据,
不用扫描。 我们在delete数据的时候,不会降低HWM的值,即使全部删除,也不会降低HWM,
全表扫描的时候还是会扫描到HWM的位置。

长时间的insert及delete可能导致数据不紧密,因此我们需要重新整理数据行在数据块中的分布,
使他排列更加紧密一些。 Oracle10g之前,我们仅能使用CTAS(create table as select),move
或导入导出方式来降低HWM, Oracle10g或以后可以使用shrink来对表进行收缩。

附: 简单介绍一下CTAS,move 

CTAS : 

create table vbap_sorted tablespace vbap_copy
storage (initial 500m  next 50m maxextents unlimited)
parallel (degree 4)
as 
select *
from  sapr3.vbap order by  mandt, vbeln, posnr;

 

 

MOVE :

alter table table_name move ...
aleter table move 只是给表中所有数据搬到新的存储空间上,就相当于把A房间内乱七八糟的东西
整理起来放到B房间里面然后再把A房间让出来给别人,这样你不但让出了整个A房间而且仍然只占用
一个房间,但是你的东西(数据)也整齐了可能只占用B房间很少的一部分,也可以在同一表空间move.
将表move后,改变了原表中存储的物理属性,该表上的索引全都失效,需重建,且需要重新分析表。
eg:   alter table table_name move;  在当前表空间进行重整。 

ALTER TABLE...MOVE  是针对segment而言的,可以对非分区表重新设置数据的存放位置、可以对
分区表的一个分区移动到新的段中,甚至你能够转移到不同的表空间中。

移动表会导致行的rowid变化。这导致该表上面的index不可用,即标记为UNUSABLE,当用DML来
操作该表时用到该索引,会引发ORA-01502 错误, 索引必须rebuild。该表的统计信息也会失效,
所以需要重新对该表进行统计分析。因为rowid的变化,所以如果此表上有物化视图,也需要重
建。move 过程并没有引起数据的变化,不会引起触发器的触发。 

SKIP_UNUSABLE_INDEXES参数的主要用途是当索引为unusable状态时保证sql的运行,虽然它
保证了系统的健壮性,但是有可能系统没有使用该索引而导致性能的低下。还有该参数对于
使用hint的某些sql和唯一索引的插入、删除语句却不生效 。

注意: MOVE操作会根据原表的INITIAL大小为新表建立第1个EXTENT。MOVE不会自动减少表
的初始扩展的大小,如果原来的initial大小非常大,shrink不会收缩这部分大小即使也存
在大量稀疏块。


oracle9i以后对分区表做move或者truncate的时可以用update global indexes语句来
同步更新全局分区索引,用消耗一定资源来换取高度的可用性(DDL同时也更新global index
保证索引不失效)。一般比较少这样做。

alter table xxxx move update global indexes;

 

MOVE的原理:

可以通过一下语句查看trace文件,从而查看move过程oracle做了什么 :

SQL> alter session set events '10046 trace name context forever, level 12' ;
SQL> alter table aaaa move tablespace log_data ;
SQL> alter session set events '10046 trace name context off' ;

Move的具体原理还在研究中......

 

 


SHRINK ---

Oracle10g 之前通过move或imp/exp进行脆片处理,不管使用哪种方式,都会中断应用,因为
在move的过程中会锁定表,并且move结束后所有的索引失效。因此从Oracle10g以后引入了收缩
操作来消除数据块的碎片。 尽管收缩操作是事务,但是它并没有引起数据的变化,因此不会
引起触发器的触发。

进行收缩操作有两个前提条件:

1. 表所在的表空间必须使用 ASSM 。
2. 收缩表引起了数据行在不同数据块之间的转移,所以被收缩的表上必须启用row movement,
   如下: 
   alter table enable row movement;
   alter table shrink space;

收缩操作分为两个阶段,压缩和降低HWM :
 
对于压缩阶段来说,就是将数据行紧密地插入到数据块中。在这个阶段,其他用户可以对被收缩的
表进行DML操作;而在降低HWM阶段,则实际将HWM降低到最后使用的那个数据块上,该阶段会对表
加排他锁,因此其他用户不能对被收缩(降低HWM)的表进行任何操作。 两个阶段可以分开执行,
也可以同时执行。

Oracle建议在业务高峰期进行压缩,等到业务不繁忙的时候,再降低HWM. 

对表进行压缩处理:
SQL> alter table t1 shrink space compact ; 

如果加入cascade选项,表示不仅压缩表,同时还收缩表相关的其他segment .
SQL> alter table t1 shrink space cascade; 

最后在业务不繁忙时进行降低HWM操作;
SQL> alter table t1 shrink space ; 

----------------------------------------------
备注: 看了下eagle的几篇文章,推荐move和exp,可以完全消除碎片。
而shrink则没有那么彻底,还是会有部分碎片无法消除。
----------------------------------------------

 

DBMS_REDEFINITION (在线重定义表) --- 

虽然是可以在线重定义table, 但是还是要选择系统负载空闲的时候操作.
详细参考: http://space.itpub.net/35489/viewspace-681493

 

Truncate --- 

如果需要删除表中的所有数据,可以使用delete, delete是DML事务,需要对表的
数据加锁,产生重做记录,并消耗undo表空间。因此delete命令需要消耗较多的
资源,执行较长的时间。如果准备删除表中所有的记录,Oracle提供了更加有效的
命令: truncate .

Truncate是一个DDL操作,该命令只更新数据字典,在数据字典中将该表所占用的
空间记录全部删除;然后将表所占用的数据块全部释放(可以被其他objects使用了);
最后将表的HWM下降到最低(一般是init extent),因为是DDL命令,不能回滚。
SQL> truncate table t ;  (默认等同于加入了drop storage) 

有时候,我们可能需要truncate一个非常巨大的表(可能是几十G),更新数据字典非常
快,但是释放数据块的工作可能花费很长的时间(有可能超过几十个小时)。在这个过
程中,被truncate 的表是不能被使用的,因此有可能导致表不可用的时间过长。

因此,Oracle提供了一个选项,在truncate时,更新完数据字典以后,不立即释放全
部的数据块,尽管数据块没有被释放,仍然被表所拥有,但是表上的HWM已经下降到最低了。然后在系统比较空闲的时候,分多次释放数据块,每次释放部分的空间,通过这种方式,将truncate的操作对系统的影响降低到最低。


在原HWM线以下占用的block仍为此table占用, 只更新数据字典,降低HWM .
SQL> truncate table t1 reuse storage ; 

然后,在系统空闲时释放数据块.
SQL> alter table t1 deallocate unused keep 50M ;
SQL> alter table t1 deallocate unused keep 30M ;
SQL> alter table t1 deallocate unused keep 20M ;
SQL> alter table t1 deallocate unused keep 10M ;
SQL> alter table t1 deallocate unused keep 0M ; 
我们在进行最后一步执行keep 0M 的时候,因为用户已经向表T1中插入了数据,则不会把
表T1中的所有数据块释放,只是释放没有用的数据块而已。

 

 

------------------
Truncate的原理:
------------------

truncate操作会做什么:移动HWM,释放extent,重新生成data_object_id。truncate之所以比delete快,是因为它根本不需要删除数据。但是在实际中我们经常碰到truncate很慢的情况,实际上它比我们想象的要复杂。 在truncate之前,CKPT必须搜索整个buffer cache,把这个object的dirty buffer全部写回磁盘(这个操作在10g有了改进)。truncate会产生redo,undo,是修改数据字典产生的,不是删除数据产生的。

Truncate操作只是改了一下数据字典上的一部份信息, 另外将重新分配一个Extent, 并获得一个新的Data Object ID, 并修改Segment Header中的信息, 在数据文件中的数据部份没有作什么修改, 象以前的DOS下删除文件一样, 有undel工具可以将文件找回来, 主要是因为磁盘中存放文件的部份没有修改. 因此对于Truncate操作如果我们知道以前的Data Object ID, 然后在数据文件中是可以重新找回数据的.      Truncate和Windows上删除文件类似,更改的只是Segment Header和Inode表、 如果空间没有重用,数据就仍然在那里存在 ;很多系统在作删除操作的时候
都只是做一个标志位,不会去作给磁盘填0那样昂贵的操作 。

实验可以看出truncate是先降低HWM,然后释放extent .  
http://space.itpub.net/?uid-35489-action-viewspace-itemid-681564 


备注: 

object_id和data_object_id同样是表示数据库对象的一个唯一标志,但是object_id表示的是逻辑id,data_object_id表示的是物理id。只有表,索引,undo这些有实际物理存储位置的对象才有data_object_id,而一些没有物理属性的object不存在data_object_id,例如procedure,
function,package,data type,db link,mv定义,view定义,临时表,分区表定义等等这些object都是没有对应着某个segment,因此它们的data_object_id都为空 。 大多数情况下两者是相等的。
但对object进行truncate,move, rebuild等操作后,data_object_id就会发生改变,而object_id
不会改变。

 

 

问题: 
truncate 命令只是更新数据字典,释放数据块的动作是smon完成的 ???
还是说释放数据块也是truncate命令的一部分 ?? 

 

 


Drop table ---  

删除表属于DDL(Data Definition Language)命令,也就是只是更新数据字典的信息,orale并不会读取表所包含的数据块的信息。因此即使表出于read only的表空间,表
也是可以被删除的。
SQL> drop table t1  ;

如果被删除的表上具有一致性约束,比如,被删除的表是其他表的父表,或者有其他表上建立的外键,而外键指向了被删除的表,必须添加 cascade constraints 选项: 
SQL> drop table t1 cascade constraints ;
这样不但能删除表,同时把位于其他表上的,应用了表T1的外键也给删除了。

 


修改及删除列 ---

SQL> alter table t1 rename column code to id ;
SQL> alter table t1 drop column code ; 
如果有其他表上的外键引用了被删除的列,则需要加入cascade constraints
SQL> alter table t1 drop column code cascade constraints ;  
在删除列的过程中,会消耗undo, 如果数据行过多,会消耗过多资源,那么我们可以
使用checkpoint N 来每隔N行就提交一次。比如:
SQL> alter table t1 drop column code cascade constraints checkpoint 2000;   

在删除column的过程中,oracle会锁定表,其他用户不能进行DML操作,业务高峰期,如果删除花太长时间,影响会比较严重,因此我们可以先将column标记为不可用,这个过程只更新数据字典的信息,速度非常快,在业务闲的时候,我们再从物理上真正删除列所占用的空间。命令如下:
SQL> alter table t1 set unused column createdate ;
视图dba_unused_col_tabs 里记录了表上有多少个被标记为unused的列。我们甚至
可以再次建立同名的column . 物理意义上的删除如下:
SQL> alter table t1 drop unused columns; 
SQL> alter table t1 drop unused columns checkpoint 2000 ;   


9.1.3  约束(constraint)


非常不错的constraint 的文章:
http://www.gomudemi.org/?p=439

类型;
a. 非空,not null
b. 唯一, unique,实际是借助索引实现的,创建唯一约束时,如果没有Index,
   会自动创建
c. 主键, primary key, 值不能重复,与唯一约束的区别在于,唯一约束所在
   列可以为空,因为   在oracle中null不等于任何值,null也不等于null,所以
   唯一约束所在的列可以有多行为null,oracle认为他们是不同的,pk=unique+not null,
   主键也借助索引实现。
d. 外键, foreign key , 外键发生在两个表之间,父表列必须存在唯一约束或主键,
   外键在子表上定义。
f. 检查, check, 类似于where条件,表里所有的记录必须满足指定的条件,not null也
   应该算作check约束,相当于check约束: col_name is not null .

我们在设计系统,考虑数据一致性的时候,应该优先考虑使用约束来实现,因为使用约束
是集中式的管理,比较容易控制。应用程序相对分散,可控性差。


9.1.3.1 约束的状态

A. enable/disable, 这表示创建约束后,对表的数据进行插入或修改时,对插入或修改后
的数据进行校验,判断其是否违反了约束,enable表示是,disable表示否。

B. validate/novalidate, 表示创建约束时,是否要对表里现存的数据进行校验,判断是否
违反了约束,validate 表示是,novalidate表示否。

上面两种排列组合产生了约束的以下四种状态:

A. enable+validate  表示既要校验已经存在的数据,也要校验新进入的数据。
B. enable+novalidate  表示不校验已经存在的数据,只校验新进入的数据。
C. disable+validate  表示不校验新进入的数据,只校验存在的数据。这是矛盾状态,
   如果表上存在这种状态,表上不能进行DML操作。
D. disable+novalidate   相当于没有建立约束。

用的较多的前两种状态。对于第二种情况,一般用于数据合并时,老数据可能违反
约束,所以对老数据就不校验了,只对新插入的数据校验。

 

9.1.3.2  创建和管理约束

A.  enable validate :  这时需要对已经存在的数据进行校验,以判断是否违反了约束,
会对整个表加锁,这时该表不能进行dml操作,必须等待校验结束,如果已存在数据违反
了约束,则执行失败。 一般默认建立PK后就是这种状态。
alter table A_TEMP_T enable validate constraint PK_A_TEMP_T ; 

B.  enable novalidate :  不校验已存在的数据,速度非常快,不对表加锁。
alter table A_TEMP_T enable novalidate constraint PK_A_TEMP_T ;

 

例子:

1. 建表. 
CREATE TABLE DFMS.A_TEMP_T
(
  SO_NO    VARCHAR2(30 BYTE),
  STN      VARCHAR2(35 BYTE),
  SO_QTY   NUMBER,
  NG_GLAG  VARCHAR2(10 BYTE)
)  TABLESPACE BASE_DATA ; 


2. 建立PK constraint. 
ALTER TABLE DFMS.A_TEMP_T ADD (
  CONSTRAINT  PK_A_TEMP_T  PRIMARY KEY (SO_NO)
    USING INDEX TABLESPACE BASE_IDX) ; 


3. 我们在Toad中可以看到隐含为SO_NO字段建立了unique index.

CREATE UNIQUE INDEX DFMS.PK_A_TEMP_T ON DFMS.A_TEMP_T
(SO_NO)
LOGGING
TABLESPACE BASE_IDX ;

ALTER TABLE DFMS.A_TEMP_T ADD (
  CONSTRAINT PK_A_TEMP_T
PRIMARY KEY
(SO_NO)
    USING INDEX
    TABLESPACE BASE_IDX);

且可以看到SO_NO字段原来定义的没有NOT NULL, 现在在Toad中可以看到NOT NULL的标示。

 

4.  如果我们这时单独删除这个PK对应的unique index,那么会报错:
drop  index  PK_A_TEMP_T ;
ORA-02429: 无法删除用于强制唯一/主键的索引

在删除约束的时候会自动删除PK依附的Index .
alter table DFMS.A_TEMP_T drop constraints PK_A_TEMP_T ;

 

5. 我们查看constraints相关视图,可以看到约束的一些值。

select * from dba_constraints where table_name='A_TEMP_T'; 

STATUS为ENABLED,  表示对插入或修改后的数据进行校验。
DEFERRABLE值为NOT DEFERRABLE,  表示这个约束是不可延迟的。
DEFERRED为IMMEDIATE , IMMEDIATE表示DML处理完一条记录后立即验证 。
VALIDATED为VALIDATED , 表示要检验表里已经存在的数据是否违反了约束

具体见 9.1.3.3  约束校验数据的时机 。

 

9.1.3.3  约束校验数据的时机 (立即生效和延迟生效)

默认情况下,一旦发生DML操作(而不是提交时),Oracle会立即判断变化后的数据是否违反了约束,
如果违反了,则立即回滚该DML操作进行的修改;  不过,我们还可以设置约束在提交的时候才进行
校验,这样的约束叫做延迟约束(deferred constraint) .

有时候我们不希望立即校验数据,比如某表上存在一个检查约束:单价x数量=金额,可能某种原因
需要先插入单价及数量,然后用户会更改数量,如果立即校验的话,前期的单价数量不能正常写入。
那么我们可以利用约束的延迟校验特性。 

SQL> alter table sales add constraint chk_sales check(price*qty=value) deferrable initially deferred ;
SQL> alter table t1 add constraint pk_t1 unique(ID) DEFERRABLE initially deferred; 

上面两个例子中:
deferrable说明约束是否可以被延迟,添加该选项说明可以延迟。
initially deferred 说明创建完毕后,何时校验数据,initially deferred 说明提交时校验,当然
前提是必须设置了deferrable; 而initially immediate是默认值,说明DML操作时立即校验数据。


关于约束校验数据的时机只存在以下几种组合情况 : 

DEFERRABLE INITIALLY DEFERRED:
   允许延迟验证约束,默认方式为延迟,等待提交后验证,可以修改为下面的第二种状态
DEFERRABLE INITIALLY IMMEDIATE:
   允许延迟验证约束,默认方式为立即,处理完一条记录就立即验证,可以修改为上面的第一种状态
NOT DEFERRABLE:
   不允许延迟验证约束,不能与上面两种状态进行转换
NOT DEFERRABLE INITIALLY IMMEDIATE:
   和上面的第三种 NOT DEFERRABLE 是一样的
NOT DEFERRABLE INITIALLY DEFERRED:
   这种是矛盾的情况,会报错ORA-02447: 无法延迟不可延迟的约束条件

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/35489/viewspace-670897/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/35489/viewspace-670897/

推荐阅读