Oracle8i 中引入的查詢重寫特性在數(shù)據(jù)倉庫開發(fā)人員和 DBA 中轟動一時。從本質上而言,它將用戶查詢重寫為從 MV 而非表中進行選擇以利用現(xiàn)成的摘要。例如,請考慮以下一家大型連鎖酒店的數(shù)據(jù)庫中的三個表。 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(包含房間價格報價)中創(chuàng)建一個記錄。當顧客在酒店結帳時,將在另一個表 TRANS 中記錄現(xiàn)金交易。
但在結帳前,酒店可能決定根據(jù)訂房情況、升級、優(yōu)惠等因素向顧客提供不同的房價。因此,最終的房價可能與預訂時的報價不同,而且可以天天都各不相同。為正確記錄這些價格變化,表 TRANS 有一行專門用來保存天天的房價信息。
為縮短查詢響應時間,您可能決定根據(jù)用戶發(fā)出的不同查詢構建 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; 因此,假如設置了某些參數(shù)(如 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) 注重,查詢是如何從物化視圖 MV_HOTEL_RESV 而非表 HOTELS 和 RESERVATIONS 中進行選擇的。 這正是您所需要的。同樣,當您編寫一個查詢來匯總每個預訂編號的實際價格時,將使用物化視圖 MV_ACTUAL_SALES 而非表 TRANS. 我們來采用一個不同的查詢。假如要查明每個城市的實際銷售額,則將發(fā)出 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 該表已經(jīng)按如下所示進行了分區(qū): 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')) ) 在某個時刻,您決定將分區(qū) Y03Q2 移動到另一個表空間 (TRANSY03Q2),該表空間可能位于一個不 同類型的磁盤(一個慢一點、便宜一點的磁盤)上。為此,請首先確認您可以聯(lián)機重新定義該表: begin dbms_redefinition.can_redef_table( uname => 'ARUP', tname => 'TRANS', options_flag => dbms_redefinition.cons_use_rowid, part_name => 'Y03Q2'); end; / 此處沒有輸出,因此您確認可以聯(lián)機重新定義該表。接下來,創(chuàng)建一個臨時表保存該分區(qū)的數(shù)據(jù): 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 進行了范圍分區(qū),因此您已經(jīng)將該表定義為未分區(qū)表。該表在所需的表空間 TRANSY03Q2 中創(chuàng)建。假如表 TRANS 包含一些本地索引,則表示您已經(jīng)對表 TRANS_TEMP 創(chuàng)建了這些 索引(當然是創(chuàng)建為未分區(qū)索引)。
要演示這個新行為,您可以使用 10046 跟蹤跟蹤該會話。 alter session set events '10046 trace name context forever, level 12'; 然后,刪除該表。假如查看跟蹤文件,則將看到分區(qū)表刪除的代碼: delete from tabpart$ where bo# = :1 delete from partobj$ where obj#=:1 delete from partcol$ where obj#=:1 delete from subpartcol$ where obj#=:1 請注重,分區(qū)是按順序刪除的。該方法最大限度地降低了刪除過程中的資源使用率并增強了性能。