oracle物化视图
一、oracle物化视图基本概念
物化视图首先需要创建物化视图日志,
oracle依据用户创建的物化视图日志来创建物化视图日志表,
物化视图日志表的名称为mlog$_后面跟基表的名称,
如果表名的长度超过20位,则只取前20位,当截短后出现名称重复时,oracle会自动在物化视图日志名称后面加上数字作为序号。
创建物化视图日志在建立时有多种选项:可以指定为rowid、primary key和object id几种类型,同时还可以指定sequence或明确指定列名。
上面这些情况产生的物化视图日志的结构都不相同。
任何物化视图都会包括的列:
snaptime$$:用于表示刷新时间。
dmltype$$:用于表示dml操作类型,i表示insert,d表示delete,u表示update。
old_new$$:用于表示这个值是新值还是旧值。n(ew)表示新值,o(ld)表示旧值,u表示update操作。
change_vector$$表示修改矢量,用来表示被修改的是哪个或哪几个字段。
如果with后面跟了primary key,则物化视图日志中会包含主键列。
如果with后面跟了rowid,则物化视图日志中会包含: m_row$$:用来存储发生变化的记录的rowid。
如果with后面跟了object id,则物化视图日志中会包含:sys_nc_oid$:用来记录每个变化对象的对象id。
如果with后面跟了sequence,则物化视图日子中会包含:sequence$$:给每个操作一个sequence号,从而保证刷新时按照顺序进行刷新。
如果with后面跟了一个或多个column名称,则物化视图日志中会包含这些列。
二、oracle物化视图日志
1.primary key
drop table test_id; --删除表
create table test_id(id number,name varchar2(30),mark number); --创建表
alter table test_id add constraint pk_test_id primary key (id); --增加主键
drop materialized view log on test_id;--删除物化视图日志
create materialized view log on test_id tablespace ttts with primary key; --依据主键创建物化视图日志
--系统针对日志建表
sql> desc mlog$_test_id;
name type nullable default comments
--------------- ----------- -------- ------- --------
id number y 用主键记录发生dml操作的行
snaptime$$ date y snaptime$$列记录了刷新操作的时间。
dmltype$$ varchar2(1) y dmltype$$的记录值i、u和d,表示操作是insert、update还是delete。
old_new$$ varchar2(1) y old_new$$表示物化视图日志中保存的信息是dml操作之前的值(旧值)
还是dml操作之后的值(新值)。除了o和n这两种类型外,对于update操作,还可能表示为u。
change_vector$$ raw(255) y change_vector$$记录dml操作发生在那个或那几个字段上
--当创建物化视图日志使用primary key时,oracle创建临时表 RUPD$_基础表
sql> desc rupd$_test_id;
name type nullable default comments
--------------- ----------- -------- ------- --------
id number y
dmltype$$ varchar2(1) y
snapid integer y
change_vector$$ raw(255) y
2.rowid
drop table test_rowid; --删除表
create table test_rowid(id number,name varchar2(30),mark number); --创建表
drop materrialized view log on test_rowid;
--create materialized view log on test_rowid with rowid, sequence (id, name) including new values ;
create materialized view log on test_rowid with rowid;--依据rowid创建物化视图日志
sql> desc mlog$_test_rowid;
name type nullable default comments
--------------- ------------- -------- ------- --------
m_row$$ varchar2(255) y
snaptime$$ date y
dmltype$$ varchar2(1) y
old_new$$ varchar2(1) y
change_vector$$ raw(255) y
3.object id
create type test_object as object (id number, name varchar2(30), num number);--创建类型
create table test_objid of test_object; --创建表
create materialized view log on test_objid with object id;--依据object id创建物化视图日志
sql> desc mlog$_test_objid;
name type nullable default comments
--------------- ----------- -------- ------- --------
sys_nc_oid$ raw(16) y
snaptime$$ date y
dmltype$$ varchar2(1) y
old_new$$ varchar2(1) y
change_vector$$ raw(255) y
4.sequence+rowid+(属性列)
drop table test_sq; --删除表
create table test_sq(id number,name varchar2(30),mark number); --创建表
drop materialized view log on test_sq;--删除物化视图日志
create materialized view log on test_sq tablespace ttts with sequence; --依据sequence创建物化视图日志
--ora-12014: 表 'test_sq' 不包含主键约束条件
create materialized view log on test_sq with sequence (id, name,num) including new values;--包含基础表的所有列
--ora-12014: 表 'test_sq' 不包含主键约束条件
alter table test_sq add constraint uk_test_sq unique (id,name); --增加uk
create materialized view log on test_sq with sequence (id,name) including new values;
--ora-12014: 表 'test_sq' 不包含主键约束条件
即主键、rowid或object id用来唯一表示物化视图日志中的记录,sequence不能唯一标识记录,故不能单独用来建日志。
create materialized view log on test_sq with rowid,sequence (id, name) including new values ;
sql> desc mlog$_test_sq;
name type nullable default comments
--------------- ------------- -------- ------- --------
id number y 建立物化视图时指明的列会在物化视图日志中进行记录。
name varchar2(30) y
m_row$$ varchar2(255) y
sequence$$ number y sequence会根据操作发生的顺序对物化视图日志中的记录编号。
snaptime$$ date y
dmltype$$ varchar2(1) y
old_new$$ varchar2(1) y
change_vector$$ raw(255) y
三、oracle物化视图日志表
基础表:test_id,test_rowid,test_objid,test_sq
日志表:mlog$_test_id,mlog$_test_rowid,mlog$_test_objid,mlog$_test_sq
1.新增
insert into test_id values (1, 'a', 5);
insert into test_rowid values (1, 'a', 5);
insert into test_objid values (1, 'a', 5);
insert into test_sq values (1, 'a', 5);
commit; 2.修改
update test_id set name = 'c' where id = 1;
update test_rowid set name = 'c' where id = 1;
update test_objid set name = 'c' where id = 1;
update test_sq set name = 'c' where id = 1;
commit; 3.删除
delete test_id ;
delete test_rowid;
delete test_objid;
delete test_sq ;
commit;
在每一步commit后查看日志表记录。
四、oracle物化视图日志表字段取值解析
1.snaptime$$
当基本表发生dml操作时,会记录到物化视图日志中,这时指定的时间4000年1月1日0时0分0秒(物化视图未被刷新)。
如果物化视图日志供多个物化视图使用,则一个物化视图刷新后会将它刷新的记录的时间更新为它刷新的时间。
只有建立快速刷新的物化视图才能使用物化视图日志,如果只建立一个物化视图,则物化视图刷新完会将物化视图日志清除掉
2.dmltype$$
操作类型比较简单:只包括i(insert)、d(delete)和u(update)三种。
3.old_new$$
新旧值也包括三种:o表示旧值(一般对应的操作时delete)、n表示新值(一般对应的操作是insert),还有一种u(对应update操作)。
需要注意,对于基于主键的物化视图日志,如果更新了主键,则update操作转化为一条delete操作,一条insert操作。最后是delete操作。
唯一的区别是每条update操作都对应物化视图日志中的两条记录。
一条对应update操作的原记录dmltype$$和old_new$$都为u,一条对应update操作后的新记录,dmltype$$为u,old_new$$为n。
当建立物化视图日志时指出了including new values语句时,就会出现这种情况。
4.change_vector$$
最后简单讨论一下change_vector$$列。
insert和delete操作都是记录集的,即insert和delete会影响整条记录。
而update操作是字段集的,update操作可能会更新整条记录的所有字段,也可能只更新个别字段。
无论从性能上考虑还是从数据的一致性上考虑,物化视图刷新时都应该是基于字段集。
oracle就是通过change_vector$$列来记录每条记录发生变化的字段包括哪些。
基于主键、rowid和object id的物化视图日志在change_vector$$上略有不同,但是总体设计的思路是一致的。
change_vector$$列是raw类型,其实oracle采用的方式就是用每个bit位去映射一个列。
比如:第一列被更新设置为02,即00000010。
第二列设置为04,即00000100,
第三列设置为08,即00001000。
当第一列和第二列同时被更新,则设置为06,00000110。
如果三列都被更新,设置为0e,00001110。
依此类推,第4列被更新时为0x10,第5列0x20,第6列0x40,第7列0x80,第8列0x100。
当第1000列被更新时,change_vector$$的长度为1000/4+2为252。
除了可以表示update的字段,还可以表示insert和delete。delete操作change_vector$$列为全0,具体个数由基表的列数决定。
insert操作的最低位为fe,如果基表列数较多,而存在高位的话,所有的高位都为ff。
如果insert操作是前面讨论过的由update操作更新了主键造成的,则这个insert操作对应的change_vector$$列为全ff。
可以看到,正如上面分析的,insert为fe,delete为00,对第一列的更新为02,第二列为04,第二列和第三列都更新为0c。需要注意,正常情况下,第一列会从02开始。
但是如果对mlog$表执行了truncate操作,或者重建了物化视图日志,则可能造成第一列开始位置发生偏移。
这个结果和rowid类型基本一致,不同的是,如果更新了主键,会将update操作在物化视图日志中记录为一条delete和一条insert,不过这时insert对应的change_vector$$的值是ff。
这个结果也和rowid类型基本一致,需要注意的是,由于对象表包含两个隐含列,因此id不再是第一个字段,而是第三个,因此对应的值是08。
最后看一个包含列数较多的例子,唯一需要注意的是,低位在左,高位在右。
五、oracle物化视图
1.物化视图mv_test_id
create materialized view mv_test_id refresh fast on commit as
select * from test_id; --commit时物化视图被刷新
2.物化视图mv_test_rowid
create materialized view mv_test_rowid refresh fast as
select name, count(*) from test_rowid group by name;
--ORA-32401: "TT"."TEST_ROWID" 上的实体化视图日志没有新值
alter materialized view log on test_rowid add including new values;
create materialized view mv_test_rowid refresh fast as
select name, count(*) from test_rowid group by name;
--ORA-12033: 不能使用 "TT"."TEST_ROWID" 上实体化视图日志中的过滤器列
alter materialized view log on test_rowid add (name);
create materialized view mv_test_rowid refresh fast as
select name, count(*) from test_rowid group by name;
3.物化视图mv_test_objid
create materialized view mv_test_objid refresh fast as
select * from test_objid;
--ORA-12014: 表 'TEST_OBJID' 不包含主键约束条件
alter table test_objid add constraint pk_test_objid primary key (id); --增加主键
create materialized view mv_test_objid refresh fast as
select * from test_objid;
--ORA-23415: "TT"."TEST_OBJID" 的实体化视图日志不记录主键
alter materialized view log on test_objid add (id);
alter materialized view log on test_objid add primary key (id);
drop materialized view log on test_objid;
create materialized view log on test_objid tablespace ttts with primary key including new values;
create materialized view mv_test_objid refresh fast as
select * from test_objid;
4.物化视图mv_test_sq
create materialized view mv_test_sq refresh fast as
select name, count(*) from test_sq group by name; --需要用exec dbms_mview.refresh('mv_test_sq')来刷新
5.物化视图刷新
exec dbms_mview.refresh('mv_test_rowid');
exec dbms_mview.refresh('mv_test_objid');
exec dbms_mview.refresh('mv_test_sq');
物化视图刷新后日志表记录被清空。
refresh fast as 调用exec dbms_mview.refresh('mv_基本表')时物化视图刷新
refresh fast on commit as 在commit时物化视图刷新
refresh fast on demand 定时物化视图刷新
create materialized view mv_test_sq2 refresh fast on demand
with rowid start with to_date('22-04-2011 16:30:01', 'dd-mm-yyyy hh24:mi:ss') next /*1:hrs*/ sysdate + 1/(24*60)
as select id,count(*) from test_sq group by id;
六、错误提示:
--ORA-32401: "TT"."TEST_ROWID" 上的实体化视图日志没有新值
alter materialized view log on test_rowid add including new values;
--ORA-12033: 不能使用 "TT"."TEST_ROWID" 上实体化视图日志中的过滤器列
alter materialized view log on test_rowid add (name);
--ORA-12014: 表 'TEST_OBJID' 不包含主键约束条件
alter table test_objid add constraint pk_test_objid primary key (id); --增加主键
--ORA-23415: "TT"."TEST_OBJID" 的实体化视图日志不记录主键
drop materialized view log on test_objid;
create materialized view log on test_objid tablespace ttts with primary key including new values;
七、相关语法:
create {materialized view | snapshot} log on [tablespace ] [storage (…)] [pctfree <10>] [pctused <40>] [initrans <1>] [maxtrans ] [logging | nologging] [cache | nocache] [noparallel | parallel []] [partition…] [lob…] [using index…] [with [primary key] [, rowid] [( [, …])] ] [{including | excluding} new values];
alter {materialized view | snapshot} log on [add [primary key] [, rowid] [( [, …])] ] […];
drop {materialized view | snapshot} log on ;
create {materialized view | snapshot} [tablespace ] [storage (…)] [pctfree <10>] [pctused <40>] [initrans <1>] [maxtrans ] [logging | nologging] [cache | nocache] [noparallel | parallel []] [cluster ( [, …])] [lob…] [partition…] [build {immediate | deferred}] [on prebuilt table [{with | without} reduced precision]] [using index…] [ refresh [fast | complete | force] [on commit | on demand] [start with ‘’] [next ‘’] [with {primary key | rowid}] [using [default] [master | local] rollback segment []] ] | never refresh ] [for update] [{enable | disable} query rewrite] as ;
alter {materialized view | snapshot} … [compile];
drop {materialized view | snapshot} ;