Oracle8i 中引入的查询重写特性在数据仓库开发人员和 DBA 中轰动一时。从本质上而言,它将用户查询重写为从 MV 而非表中进行选择以利用现成的摘要。例如,请考虑以下一家大型连锁酒店的数据库中的三个表。 SQL> DESC HOTELS Name Null?Type ----------------------------------------- -------- ------------- HOTEL_ID NOT NULL NUMBER(10) CITY VARCHAR2(20) STATE CHAR(2) MANAGER_NAME VARCHAR2(20) RATE_CLASS CHAR(2) SQL> DESC RESERVATIONS Name Null?Type ----------------------------------------- -------- ------------- RESV_ID NOT NULL NUMBER(10) HOTEL_ID NUMBER(10) CUST_NAME VARCHAR2(20) START_DATE DATE END_DATE DATE RATE NUMBER(10) SQL> DESC TRANS Name Null?Type ----------------------------------------- -------- ------------- TRANS_ID NOT NULL NUMBER(10) RESV_ID NOT NULL NUMBER(10) TRANS_DATE DATE ACTUAL_RATE NUMBER(10) 表 HOTELS 保存酒店的相关信息。当顾客预订酒店时,将在表 RESERVATIONS(包含房间价格报价)中创建一个记录。当顾客在酒店结帐时,将在另一个表 TRANS 中记录现金交易。
但在结帐前,酒店可能决定根据订房情况、升级、优惠等因素向顾客提供不同的房价。因此,最终的房价可能与预订时的报价不同,而且可以天天都各不相同。为正确记录这些价格变化,表 TRANS 有一行专门用来保存天天的房价信息。
为缩短查询响应时间,您可能决定根据用户发出的不同查询构建 MV,如: create materialized view mv_hotel_resv refresh complete enable query rewrite as select city, resv_id, cust_name from hotels h, reservations r where r.hotel_id = h.hotel_id; 和 create materialized view mv_actual_sales refresh complete enable query rewrite as select resv_id, sum(actual_rate) from trans group by resv_id; 因此,假如设置了某些参数(如 query_rewrite_enabled = true),则类似如下所示的查询 select city, cust_name from hotels h, reservations r where r.hotel_id = h.hotel_id; 将重写为 select city, cust_name from mv_hotel_resv; 您可以通过运行该查询并启用自动跟踪来确认 MV。 SQL> set autot traceonly eXPlain SQL> select city, cust_name 2> from hotels h, reservations r 3> where r.hotel_id = h.hotel_id; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=80 Bytes=2480) 1 0 MAT_VIEW access (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW) (Cost=3 Card=80 Bytes=2480)
我们来采用一个不同的查询。假如要查明每个城市的实际销售额,则将发出 select city, sum(actual_rate) from hotels h, reservations r, trans t where t.resv_id = r.resv_id and r.hotel_id = h.hotel_id group by city; 注重此查询结构:从 MV_ACTUAL_SALES 中,您可以获得 RESV_ID 和预订的总销售额。从 MV_HOTEL_RESV 中,您可以获得 CITY 和 RESV_ID。
我们来看一下它的工作原理。以下是 TRANS 表的结构: SQL> desc trans Name Null?Type --------------------------------- -------- ------------------------- TRANS_ID NUMBER TRANS_DATE DATE TXN_TYPE VARCHAR2(1) ACC_NO NUMBER TX_AMT NUMBER(12,2) STATUS 该表已经按如下所示进行了分区: partition by range (trans_date) ( partition y03q1 values less than (to_date('04/01/2003','mm/dd/yyyy')), partition y03q2 values less than (to_date('07/01/2003','mm/dd/yyyy')), partition y03q3 values less than (to_date('10/01/2003','mm/dd/yyyy')), partition y03q4 values less than (to_date('01/01/2004','mm/dd/yyyy')), partition y04q1 values less than (to_date('04/01/2004','mm/dd/yyyy')), partition y04q2 values less than (to_date('07/01/2004','mm/dd/yyyy')), partition y04q3 values less than (to_date('10/01/2004','mm/dd/yyyy')), partition y04q4 values less than (to_date('01/01/2005','mm/dd/yyyy')), partition y05q1 values less than (to_date('04/01/2005','mm/dd/yyyy')), partition y05q2 values less than (to_date('07/01/2005','mm/dd/yyyy')) ) 在某个时刻,您决定将分区 Y03Q2 移动到另一个表空间 (TRANSY03Q2),该表空间可能位于一个不同类型的磁盘(一个慢一点、便宜一点的磁盘)上。为此,请首先确认您可以联机重新定义该表: begin dbms_redefinition.can_redef_table( uname => 'ARUP', tname => 'TRANS', options_flag => dbms_redefinition.cons_use_rowid, part_name => 'Y03Q2'); end; / 此处没有输出,因此您确认可以联机重新定义该表。接下来,创建一个临时表保存该分区的数据: create table trans_temp ( trans_id number, trans_date date, txn_type varchar2(1), acc_no number, tx_amt number(12,2), status varchar2(1) ) tablespace transy03q2 / 请注重,由于表 TRANS 进行了范围分区,因此您已经将该表定义为未分区表。该表在所需的表空间 TRANSY03Q2 中创建。假如表 TRANS 包含一些本地索引,则表示您已经对表 TRANS_TEMP 创建了这些索引(当然是创建为未分区索引)。
要演示这个新行为,您可以使用 10046 跟踪跟踪该会话。 alter session set events '10046 trace name context forever, level 12'; 然后,删除该表。假如查看跟踪文件,则将看到分区表删除的代码: delete from tabpart$ where bo# = :1 delete from partobj$ where obj#=:1 delete from partcol$ where obj#=:1 delete from subpartcol$ where obj#=:1 请注重,分区是按顺序删除的。该方法最大限度地降低了删除过程中的资源使用率并增强了性能。