ORACLE DB 的學習者們

2017年12月27日 星期三

ORACLE分群彙總函數 ROLLUP、CUBE、GROUPING、GROUPING SET

GROUP的功能配合加總函數,可對資料分群後,以群為單位加總,如果資料當中,用以分群的欄位數超過一個以上,對分群欄位如何做到個別加總及最後的完整加總? ROLLUP、CUBE和GROUPING SET提供了很好的方法。

參考文件:文件一文件二文件三

以下範例假設有某資料表,有三個屬性欄位(如部門及子部門等)和一個數值欄位(如薪水或價格等),該資料表的SQL請參考文件一

對前三個欄位做群組,列出欄位數及合計

SELECT YEAR,
       AREA,
       DEPARTMENT_ID,
       COUNT(*) AS num_rows,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales;

ROLLUP : 分群部分加總函數   

ROLLUP 函數可對一個以上的欄位進行分項加總,進行加總時,從最右邊的欄位進行分項加總。若 ROLLUP 有 N 個欄位,會產生 N+1 個階層的子項加總,以下針對年度與區域做子項加總,求分年和分區域的總和,查詢部分截圖如下,完整結果請參考結果檔案
SELECT YEAR,
       AREA,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales
GROUP BY ROLLUP (YEAR, AREA)
ORDER BY YEAR, AREA;
使用三個欄位進行分項加總,共有4個階層的子加總,其結果檔案和截圖如下所示。
SELECT YEAR,
       AREA,
       DEPARTMENT_ID,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales
GROUP BY ROLLUP (YEAR, AREA, DEPARTMENT_ID)
ORDER BY YEAR, AREA, DEPARTMENT_ID;
分項加總也可以搭配一般的群組GROUP,其結果如檔案所示。
SELECT YEAR, AREA, DEPARTMENT_ID,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales
GROUP BY YEAR, ROLLUP (AREA, DEPARTMENT_ID)
ORDER BY YEAR, AREA, DEPARTMENT_ID;

使用GROUP BY 和 UNION ALL 取代 ROLLUP 函數

上述分項加總的ROLLUP函數也有替代方案。若是三個欄位進行ROLLUP,則需四組SQL進行 UNION ALL 運算,第一組列出所有欄位,第二組從ROLLUP最右邊的欄位起,將該欄位以NULL取代,依此類推。 下列ROLLUP查詢
SELECT YEAR,
       AREA,
       DEPARTMENT_ID,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales
GROUP BY ROLLUP (YEAR, AREA, DEPARTMENT_ID)
ORDER BY YEAR, AREA, DEPARTMENT_ID;
與使用替代方案的UNION ALL 的下列查詢,產生完全相同的結果
Select YEAR,AREA,DEPARTMENT_ID,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR,AREA,DEPARTMENT_ID  
union all  
Select YEAR,AREA,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR,AREA  
union all  
Select YEAR,null,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR  
union all  
Select null,null,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES  
order by 1,2,3;

CUBE 函數

使用 CUBE 函數,是對函數內的參數,列舉完整的加總結果。若是 N 個參數的 CUBE 函數,會產生 2^N 個子加總的組合。
SELECT YEAR, AREA,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales
GROUP BY CUBE (YEAR, AREA)
ORDER BY YEAR, AREA;
上述CUBE函數的結果。 當參數增加,子加總的結果也會大幅增加,前述兩個參數共產生18筆,若改為三個參數如下,則共產生198筆資料
SELECT YEAR, AREA, DEPARTMENT_ID,
       SUM(sales) AS TOTAL_SALES
FROM   total_sales
GROUP BY CUBE (YEAR, AREA, DEPARTMENT_ID)
ORDER BY YEAR, AREA,DEPARTMENT_ID;

使用GROUP BY 和 UNION ALL 取代 CUBE 函數的替代方法

前述三個參數的 CUBE 函數,若是使用 GROUP BY 和 UNION ALL,則需要 2^3 個 SQL,也就是 8 個SQL來組成替代方案。
Select YEAR,AREA,DEPARTMENT_ID,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR,AREA,DEPARTMENT_ID 
union all  
Select YEAR,AREA,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR, AREA 
union all  
Select YEAR,null,DEPARTMENT_ID,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR, DEPARTMENT_ID  
union all  
Select YEAR,null,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by YEAR  
union all  
Select null,AREA,DEPARTMENT_ID,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by AREA, DEPARTMENT_ID  
union all  
Select null,AREA,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by AREA  
union all  
Select null,null,DEPARTMENT_ID,sum(SALES) AS TOTAL_SALES from TOTAL_SALES group by DEPARTMENT_ID  
union all  
Select null,null,null,sum(SALES) AS TOTAL_SALES from TOTAL_SALES; 
上述UNION的結果,共產生 198 筆資料

GROUPING Functions

在所產生的子項加總資料列,所進行加總的欄位以NULL顯示,但是若原有資料本身就有NULL值,會難以分辨是原有的NULL還是因為加總而產生的NULL,GROUPING函數用來顯示該欄位是否因為執行加總(ROLLUP、CUBE)而產生NULL值,它接受一個參數(欄位),1表示該欄位的NULL由加總產生,0 表示欄位是其他的值。
SELECT YEAR,
       AREA,
       SUM(sales) AS TOTAL_SALES,
       GROUPING(YEAR) as Grouping_Year,
       GROUPING(AREA) as Grouping_Area,
FROM   total_sales
GROUP BY ROLLUP (YEAR, AREA)
ORDER BY YEAR, AREA;
上述查詢結果多了兩個欄位Grouping_Year與Grouping_Area。當該欄位顯示為1,表示該分項子加總由該欄位產生,而其對應的欄位亦為NULL。

下圖顯示,Grouping_Area 的值為1,表示欄位Area的NULL是因為加總而產生,而子項加總 27435.34 也是由2015年各區域的總和而產生。

有此欄位,就可以對子項加總的結果,篩選你要的結果,比方說,你只想看2015年各分項加總以及總結的數量,可用下列方式,從113筆資料當中,篩選出7筆2015年的分項總計。
SELECT YEAR, AREA, DEPARTMENT_ID, SUM(sales) AS TOTAL_SALES,
       GROUPING(YEAR) as Grouping_Year,
       GROUPING(AREA) as Grouping_Area,
       GROUPING(DEPARTMENT_ID) as Grouping_Dept
FROM   total_sales
WHERE YEAR=2015
GROUP BY ROLLUP (YEAR, AREA, DEPARTMENT_ID)
HAVING GROUPING(YEAR)=1 or GROUPING(AREA)=1 or GROUPING(DEPARTMENT_ID)=1
ORDER BY YEAR, AREA, DEPARTMENT_ID;
其分項結果如下圖所示。

GROUPING_ID

此函數接受一個以上的欄位為參數,通常與ROLLUP或是CUBE的參數相同,它用來顯示加總欄位的階層,0表示第一階。以下使用兩個欄位以CUBE函數查詢所有分項加總及其組合。由結果顯示共有4個階層。
SELECT YEAR, AREA,
       SUM(sales) AS TOTAL_SALES, 
       GROUPING_ID(YEAR, AREA) AS GROUPING_LEVEL
FROM   total_sales
GROUP BY CUBE (YEAR, AREA)
ORDER BY YEAR, AREA;

GROUPING SETS

有時我們只想得到某些欄位組合的分項加總, GROUPING SETS用於此時,可將想得到的欄位組合以括號組合在一起,就只會列出該組合的子加總,例如,若只想得到(年度,區域)及(年度,部門)這兩種組合的子加總時,便可用 GROUPING SETS((YEAR, AREA), (YEAR, DEPARTMENT_ID))來取得,如下範例所示,其結果請參考。
SELECT YEAR, AREA, DEPARTMENT_ID,
       SUM(sales) AS TOTAL_SALES,
       GROUPING_ID(YEAR, AREA, DEPARTMENT_ID) AS GROUPING_LEVEL
FROM   total_sales
GROUP BY GROUPING SETS  ((YEAR, AREA),(YEAR, DEPARTMENT_ID))
ORDER BY YEAR, AREA,DEPARTMENT_ID;

Composite Columns

在ROLLUP與CUBE函數中的參數可用欄位組合方式來取得特定組合的加總,例如ROLLUP((YEAR, AREA), DEPARTMENT_ID)或是CUBE((YEAR, AREA), DEPARTMENT_ID),如此,這些以括號放在一起的欄位就會一起列出。其結果請參考。
SELECT YEAR, AREA, DEPARTMENT_ID,
       SUM(sales) AS TOTAL_SALES,
       GROUPING_ID(YEAR, AREA, DEPARTMENT_ID) AS GROUPING_LEVEL
FROM   total_sales
GROUP BY CUBE  ((YEAR, AREA), DEPARTMENT_ID)
ORDER BY YEAR, AREA,DEPARTMENT_ID;

Concatenated Groupings

這是把組合的欄位,以交錯的方式組合輸出。多個GROUPING SETS,透過逗點組合在一起,例如GROUPING SETS(id1, id2), GROUPING SETS(id3, id4) ,會產生的組合是 :

(id1, id3)

(id1, id4)

(id2, id3)

(id2, id4)

依此類推,以下範例就會產生出這些欄位的組合和子項加總 ,其結果請參考

(YEAR, DEPARTMENT_ID)

(YEAR, MANAGER_ID)

(AREA, DEPARTMENT_ID)

(AREA, MANAGER_ID)

SELECT YEAR, AREA, DEPARTMENT_ID,MANAGER_ID,
       SUM(sales) AS TOTAL_SALES,
       GROUPING_ID(YEAR, AREA, DEPARTMENT_ID) AS GROUPING_LEVEL
FROM   total_sales
GROUP BY GROUPING SETS  (YEAR, AREA),GROUPING SETS (DEPARTMENT_ID, MANAGER_ID)
ORDER BY YEAR, AREA,DEPARTMENT_ID;

2017年12月23日 星期六

階層式查詢

參考連結: ORACLE 文件簡單範例一簡單範例二

語法架構

階層式查詢用於以下類型結構的資料:

資料彼此間存在從屬關係:比方說,員工資料表:員工編號、姓名、部門、主管編號。資料表內的員工可依照從屬關係畫出樹狀圖。

或是親屬資料表:姓名、性別、父母等

語法結構如下:

select ... start with initial-condition connect by nocycle recurse-condition

select ... connect by recurse-condition

select ... start with initial-condition connect by nocycle recurse-condition

select ... connect by recurse-condition

範例:列出所有從屬關係

此語法先從滿足 initial-condition 的根節點開始,以DFS(Deep First Search) 或是 LDR(Left-Dump-Right)的順序,列印出根節點的子節點且符合recurse-condition的其餘子節點。

以下範例,從第一筆資料起,列出所有從屬關係

      SELECT last_name, employee_id, manager_id, LEVEL
      FROM emp  
      CONNECT BY PRIOR employee_id =  manager_id
      ORDER SIBLINGS BY last_name;

UNARY OPERATOR PRIOR 的用法

CONNECT BY PRIOR employee_id = manager_id

PRIOR可以放在欄位 employee_id 前或是欄位 manager_id 之前,它是一個 UNARY OPERATOR ,放在EMPLOYEE_ID前,代表後續其他的子節點,需以根節點的這個EMPLOYEE_ID,當作子節點的MANAGER_ID,因此PRIOR放在EMPLOYEE_ID和放在MANAGER_ID所得的結果是不同的。

為根節點加上起始條件

根節點的起始條件用 START....WITH 語法來定義,可定義符合該條件的根節點,才會被查詢出來。

以下列出,從員工代碼 100 開始的員工,列出所有員工的從屬關係

      SELECT last_name, employee_id, manager_id, LEVEL
      FROM emp  
      START WITH employee_id = 100
      CONNECT BY PRIOR employee_id =  manager_id
      ORDER SIBLINGS BY last_name;
其中 LEVEL 是用於此階層查詢中的虛擬欄位,表示由根節點開始的階層編號,而根節點為 1 。

CONNECT_BY_ROOT 與 SYS_CONNECT_BY_PATH

CONNECT_BY_ROOT 可以列出根節點的欄位,而 SYS_CONNECT_BY_PATH 則可列出由根節點到子節點的完整路徑。參看下列範例

   SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager",
   LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path"
   FROM emp
   WHERE LEVEL > 1 and department_id = 100
   CONNECT BY PRIOR employee_id = manager_id; 
上述範例列出所有子節點的管理者,並列出從根節點到子節點的完整階層。

2017年10月3日 星期二

Oracle Scheduler

Oracle 的排程器(Scheduler) 是由一支背景執行的 CJQ0 (JOB QUEUE COORDINATOR) 來監控管理,主要的動態VIEWS 為 DBMS_SCHEDULER_JOBS。若有使用者指定的 JOB 執行時,會指派 JOB ID,如 Jnnn,其中 nnn為 JOB ID。 JOB ID 的範圍由參數JOB_QUEUE_PROCESSES (預設1000):若設為0,Scheduler不會啟動。

SCHEDULER 的背景程式

下圖顯示在系統背景執行的這支 CJQ0。
或是由LINUX的SHELL也可看出
使用以下查詢都可以找出在背景執行的 JOBS
select * from v$process where pname like 'CJQ%';  
或是
select program from v$process where program like '%J%';

SCHEDULER 的 PACKAGE

主要是 DBMS_SCHEDULER ,該套件有許多介面可供使用,可透過 DESCRIBE DBMS_SCHEDULER 來查詢。所建立的為資料庫的物件,僅列舉以下常常使用的使用方法
  • CREATE JOB
  • JOB 包含幾個重點:做什麼 (WHAT) 和何時做 (WHEN)。其他參數如工作型態、參數等。
    • 做什麼 (WHAT)
    • SCHEDULER 可執行的包含有匿名 PL/SQL 區塊、STORED PROCEDURE,或甚至執行檔。
    • 何時做 (WHEN)
    • 型態(Job Type)
    • PLSQL_BLOCK(可為單一INSERT), STORED_PROCEDURE, EXECUTABLE, CHAIN (具備程式邏輯的一串 SCHEDULE)
    • 開始日期時間、結束日期時間
    • 執行頻率
    • ENABLE
    • 此參數必須設為 TRUE 才會啟動 (預設是 FALSE)
    • AUTODROP
    • JOB 結束後是否自動刪除 JOB 的定義。預設是 TRUE
  • CREATE PROGRAM
  • CREATE SCHEDULE
以下使用其中一個定義來展示
PROCEDURE create_job(
  job_name                IN VARCHAR2,
  job_type                 IN VARCHAR2,
  job_action              IN VARCHAR2,
  number_of_arguments     IN PLS_INTEGER              DEFAULT 0,
  start_date              IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,
  repeat_interval         IN VARCHAR2                 DEFAULT NULL,
  end_date                IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,
  job_class               IN VARCHAR2              DEFAULT 'DEFAULT_JOB_CLASS',
  enabled                 IN BOOLEAN                  DEFAULT FALSE,
  auto_drop               IN BOOLEAN                  DEFAULT TRUE,
  comments                IN VARCHAR2                 DEFAULT NULL,
  credential_name         IN VARCHAR2                 DEFAULT NULL,
  destination_name        IN VARCHAR2                 DEFAULT NULL);

SCHEDULER 的相關權限

由於 SCHEDULER 有可能執行匿名區塊、STORED PROCEDURE或是執行檔等,因此需要的相關權限如下
  • CREATE JOB
  • CREATE ANY JOB
  • CREATE EXTERNAL JOB
  • EXECUTE ANY PROGRAM
  • EXECUTE ANY CLASS
  • MANAGE SCHEDULE
  • EXECUTE ON
  • ALTER ON
  • ALL ON
其中,角色 SCHEDULER_ADMIN 包含有前六項權限

SCHEDULER 的動態 VIEWS

user_scheduler_jobs 、 dba_scheduler_jobs 、 dba_jobs 或是 dba_jobs 等等 以下在 HR SCHEMA 下新增一TABLE,然後透過一分鐘執行一次的 JOB ,將系統時間INSERT到該新的TABLE 首先透過 SYSTEM 權限,授權 HR 。
grant create job to hr;
grant alter job to hr;
接下來登入 HR,建立 TABLE及 JOB
create table times (c1 date);
alter session set nls_date_format='dd-mm-yy hh24:mi:ss';

begin
    dbms_scheduler.create_job
    (
        job_name=>'savedate',
        job_type=>'plsql_block',
        job_action=>'insert into times values(sysdate);',
        start_date=>sysdate,
        repeat_interval=>'freq=minutely;interval=1',
        enabled=>true,
        auto_drop=>false
    );
end;
/
執行完畢後,透過動態 VIEWS ,查詢執行次數如下 select job_name, enabled, to_char(next_run_date, 'dd-mm-yy hh24:mi:ss'), run_count from user_scheduler_jobs;

透過DATABASE CONTROL新增SCHEDULER

在網頁中選擇伺服器==>排程==>程式==>新增 ,在所有欄位填入必要資訊後,選擇【顯示SQL】可看到該工作的對應SQL,如下圖
確定執行後可看到多了一筆排程

SCHEDULER 的LOGS

紀錄可透過下列兩個TABLES來取得,dba_scheduler_job_log 或 user_scheduler_job_log 。
select job_name, log_date, status from dba_scheduler_job_log ; 
或是
select job_name, log_date, status from user_scheduler_job_log ;
以下透過HR帳號取得該帳號下的紀錄
以下則為管理者帳號所取得紀錄

2017年9月18日 星期一

ORACLE FLASHBACK

flashback 處理user errors。datafile損壞等錯誤則是由一般的備份與還原程序處理。

flashback 只可用在 table drop,不含 table truncation

flashback drop, flashback query, flashback transaction, flashback table 不須額外的設定

flashback database: 有異動的block images ,定期由database buffer cache,寫入到 flashback buffer(SGA內)

Configuring Flashback Database

  • 確認處於 Archive Log Mode
  • select log_mode from v$database;
  • 設定 FLASH Recovery Area
  •             alter system set db_recovery_file_dest = '/flash_recovery' ;
                alter system set db_recovery_file_dest_size = 8G;
             
  • 設定 flashback retention target (單位: 分鐘)
  • alter system set db_flashback_retention_target = 240;
  • 關閉後再啟動
  •            shutdown immediate;
               startup mount;
            
  • 啟動 flashback logging
  • alter database flashback on;
  • 打開資料庫
  • alter database open open;
  • 如何確認FLASHBACK已經啟動
  • select flashback_on from v$database;

Flashback usage statistics

設定完成後,有以下動態VIEW可觀察

v$flashback_database_log

v$flashback_database_stat

v$sgastat

select * from v$sgastat where name = 'flashback generation buff';

觀察 SGA 中,FLASHBACK 的記憶體使用狀態

Flashback enable

下圖顯示啟動 FLASHBACK 的過程

啟動後再觀察上述動態 VIEW , 可發現上述動態VIEW不再毫無資料

Database recovery point-in-time using Flashback

以下建立一個TABLE,並記錄現在時間,然後將表格刪除,再使用 Database Control 進行復原
以下使用 RMAN 進行復原

在復原前,資料庫必須處於 MOUNT 狀態,然後執行復原

RMAN> shutdown immediate; 
RMAN> startup mount;
RMAN> run{ flashback database to time "to_date('2017-09-18 01:20:22', 'YYYY-MM-DD HH24:MI:SS')"; }
RMAN> alter database open resetlogs;
執行畫面如下

執行完畢後,再重新查詢,可再找到該表格

Exclude tablespaces from Flashback Logging

啟動 flashback 後,會將 block 的異動寫入到 flashback logs,因此 logs 的成長會影響資料庫的效能,可將部分 TABLESPACE ,取消其 flashback logging

查詢 TABLESPACES 的 FLASHBACK 啟動狀態可用下列指令

select name, flashback_on from v$tablespace;

取消 FLACKBACK

alter tablespace flashback off;

開啟 FLASHBACK

alter tablespace flashback on;

因為 FLASHBACK 是透過 CONTROLFILE 啟動,而非資料字典,因此其相關資訊需透過動態效能視表(dynamic performance view)查詢,如 v$tablespace,而非資料字典 (DBA_TABLESPACES)。

Flashback DROP

Flashback Drop 用於 table recovery,透過 flashback drop,被刪除(不包含 truncate)的表格,可復原和無法復原的物件如下

  • 可復原物件: table, index, grant, triggers, grants, unique-key, primary-key, not-null constraints。
  • 無法復原物件:foreign constraints
從版本 10g 以後,drop table命令,會將原有 table,以重新命名的方式,從資料字典移開

DROP 以後,物件會被放置到 Recyclebin,可用 USER_RECYCLEBIN 或是 DBA_RECYCLEBIN 來查詢

舉例來說,以下刪除某個表格

刪除以後查詢回收桶

select OBJECT_NAME, ORIGINAL_NAME, OPERATION, TYPE, CAN_UNDROP, CAN_PURGE from user_recyclebin;

查詢後透過 FLASHBACK 復原

SQL> flashback table emp_082209 to before drop;

Flashback complete.
上述命令亦可改為

SQL> flashback table emp_082209 to before drop rename to new_name;

也可以透過ENTERPRISE MANAGER 的DATABASE CONTROL 來復原,但必須以 SYSDBA 身分登入,選擇以下路徑

使用狀態 ==> 管理 ==> 執行復原 ==> 下拉選單選擇(表格) ==> 倒溯刪除的表格 ==> 綱要名稱 (輸入 HR ) ==> 執行

Flashback Query

Flashback Query最常使用在使用者不慎刪除某個表格內的某筆紀錄 (不是整個表格),透過它確認已經刪除的資料。

常用語法如下:

--查詢某個時間戳記,某表格的內容
SQL> select * from hr.emp_082209 as of timestamp to_timestamp('18-09-17 01:22:00','dd-mm-yy hh24:mi:ss') ;
--將某個表格,回逤到某個時間戳記
SQL> flashback table emp to timestamp to_timestamp('18-09-17 01:22:00','dd-mm-yy hh24:mi:ss') ;
--一次回朔兩個表格到某個 SCN ,同時啟用 TRIGGERS
SQL> flashback table emp, dept to scn 65544 enable triggers ;

前述回逤時間戳記,有一個先決條件,必須開啟 ROW MOVEMENT。語法如下:

SQL> ALTER TABLE XXX ENABLE ROW MOVEMENT ;

假設欲刪除某表格最後一筆資料如下

先記錄時間戳記,將表格開放 ROW MOVEMENT,然後將該筆資料刪除

SQL> select sysdate from dual;

SYSDATE
-------------------
2017-09-18 20:28:10

SQL> alter table hr.emp_082209 enable row movement;

Table altered.

SQL> delete from hr.emp_082209 where last_name='Jeng';

1 row deleted.

SQL> commit;

Commit complete.
刪除以後該筆資料消失

以 FLASHBACK TABLE 命令復原該表格至前述時間戳記(或之前)

SQL> flashback table hr.emp_082209 to timestamp to_timestamp('18-09-17 20:28:00','dd-mm-yy hh24:mi:ss') ;                                   
Flashback complete.
復原以後再查詢,可以看到原先被刪除的最後一筆資料又出現了。

Flashback Versions Query

Flashback Versions Query 可讓使用者查詢某表格中,一筆紀錄的數個committed versions。

這些資訊被包含在表格的數個虛擬欄位當中 (ROW ID也是虛擬欄位),虛擬欄位有:

  • VERSIONS_STARTSCN
  • VERSIONS_STARTTIME
  • VERSIONS_ENDSCN
  • VERSIONS_ENDTIME
  • VERSIONS_XID
  • VERSIONS_OPERATION
為了實驗,我們在短時間內對某一筆的SALARY進行數次更新,然後我們進行 FLASHBACK VERSIONS QUERY

欲查詢上述虛擬欄位,需在查詢時,在SQL中使用關鍵字 VERSIONS BETWEEN ,例如,

select employee_id, first_name, salary, VERSIONS_XID, VERSIONS_STARTSCN, VERSIONS_ENDSCN, VERSIONS_OPERATION 
from emp_082209
versions between scn minvalue and maxvalue
where last_name='Jeng';
  • versions between scn minvalue and maxvalue
  • versions between timestamp (SYSTIMESTAMP- 1/24) and SYSTIMESTAMP
  • versions between timestamp to_timestamp('19-09-17 08:10:00','dd-mm-yy hh24:mi:ss') and SYSTIMESTAMP
VERSIONS_OPERATION 包含下列:(I) INSERT (U) UPDATE (D) DELETE

Flashback and Undo Data

Flashback 能復原的資料,端視存放在 UNDO DATA 的歷史資料而異。此區域的資料會被新的資料所覆蓋。UNDO DATA資料保留的時間長度,由參數 undo_retention 決定。 下圖顯示保留時間為900秒,也就是15分鐘。
如果該區域過小導致舊的資料被覆蓋,將會發生 ORA-1555 snapped too old 的錯誤訊息。 要改善此種狀況可透過設定TABLESPACE的參數 RETENTION GUARANTEE 來改善,或是透過下列的 FLASHBACK DATA ARCHIVE 來紀錄更多歷史資料。

FLASHBACK DATA ARCHIVE

FLASHBACK DATA ARCHIVE 使用額外的 TABLESPACE 來記錄歷史資料,其進行步驟如下

  • Create tablespace
  • Create flashback archive
  • Create user & schema that using the flashback data archive
  • Grant flashback archive on to user
  • Alter table flashback archive
範例如下 :
create tablespace fda datafile '/oracle/app/oracle/oradata/sedb/fda1.dbf' size 10m;

create flashback archive fla1 tablespace fda retention 1 month;

CREATE USER fbdauser
  IDENTIFIED BY xxxx
  DEFAULT TABLESPACE fda
  TEMPORARY TABLESPACE TEMP
  PROFILE DEFAULT
  ACCOUNT UNLOCK;
  
  GRANT CONNECT TO fbdauser;
  GRANT RESOURCE TO fbdauser;
  ALTER USER fbdauser DEFAULT ROLE ALL;
  -- 2 System Privileges for PROCHUB 
 
  
  grant dba to fbdauser identified by xxxx;

grant flashback archive administer to fbdauser;
  
  grant flashback archive on fla1 to hr;
  alter table hr.emp_082209 flashback archive fla1;


建立後,可透過下列 VIEW 來觀察使用狀況
  • dba_flashback_archive
  • dba_flashback_archive_ts
  • dba_flashback_archive_tables
例如:

2017年9月4日 星期一

Online Redo Log Multiplexing and Recovery

Online redo log files 如果有 multiplex ,若不慎遺失同組內的其中一個檔案,資料庫不至於無法使用,也不會有資料遺失,但會有錯誤訊息。但若未將logfile做multiplex,則logfile的損壞可能對資料庫狀態及使用者資料都有損傷。

透過V$LOG 調查目前LOGFILE的狀況,有三個欄位特別重要:

GROUP : 顯示目前有幾個GROUP

ARCHIVED: YES表示,LOGFILE 寫入到 ARCHIVE LOGFILE

STATUS : CURRENT表示資料庫正在寫入使用此LOGFILE,INACTIVE表示已經寫入完成

透過V$LOGFILE看出,每個GROUP只有一個LOGFILE

先將每個GROUP 加入所要的LOGFILE

加入以後,要調整LOGFILE成兩兩成對的模式,要將多出來的LOGFILE刪除。刪除前,必須反覆地運用 alter system switch logfile 指令,強迫資料庫將CURRENT移動到其他 GROUP, 然後靜候GROUP 的狀態由CURRENT => ACTIVE => INACTIVE,才能對該成員LOGFILE做刪除的動作。

刪除的SQL並非會真正在作業系統中刪除該成員,而是在SQL執行完畢後,回到作業系統,以作業系統的指令刪除。

2017年9月3日 星期日

User-Managed Backup

Backup & Recovery的分類

Backup 分為 Closed 及 Open,Recovery 則分為 Complete 及 Incomplete。
  • Backup
    • Closed : Noarchivelog Mode 適用,資料庫Shutdown
      • Close the controlfile
      • Close the online logfile
      • Close the datafile
    • Open : 只有 Archivelog Mode 可以,資料庫Open
      • Alter database backup control file to ...
      • Archive the online logfile
      • Alter tablespace .... begin backup - copy the datafiles - alter tablespace .... end backup
  • Restore & recover
    • Complete
      • Take the damaged file offline
      • Restore it
      • Recover it
      • Bring it online
    • Incomplete
      • Mount the database
      • Restore all datafiles
      • Recover database until ...
      • Open resetlogs

Noarchivelog Mode Backup

此模式只能選用Closed Backup,備份前,需關閉資料庫,將檔案使用作業系統的命令來複製。但複製前必須確認所複製檔案沒有漏掉。可在資料庫關閉前使用下列命令來產生完整複製的指令。

select 'cp     ' || name || '    /home/oracle/bkup' from v$controlfile  ; 
select 'cp     ' || name || '    /home/oracle/bkup' from v$datafile  ; 
select 'cp     ' || name || '    /home/oracle/bkup' from v$tempfile  ; 
select 'cp    ' || member || '    /home/oracle/bkup' from v$logfile  ; 

Archivelog Mode Backup

User-Managed Backup 主要包含CONTROLFILE 和 DATAFILE 的備份

要先確定資料庫屬於 ARCHIVELOG MODE

    以下範例展示在 Archivelog Mode下進行的User-managed backup
  1. 建立暫時的TABLESPACE
  2. create tablespace ex181 datafile '/oracle/app/oracle/oradata/sedb/ex181.dbf' size 10m extent management local segment space management auto;
  3. 在新的表格空間建立新TABLE
  4. create table t1 (c11 date) tablespace ex181;
  5. 將TABLESPACE 設定為 BACKUP MODE
  6. alter tablespace ex181 begin backup ;
  7. Backup datafile
  8. [oracle@db01 bkup]$ cp /oracle/app/oracle/oradata/sedb/ex181.dbf /home/oracle/bkup/ex181.dbf
  9. 將TABLESPACE 設定結束 BACKUP MODE
  10. alter tablespace ex181 end backup;
  11. 對CONTROLFILE做BINARY BACKUP
  12. alter database backup controlfile to '/home/oracle/bkup/controlfile01.bin';
  13. 對CONTROLFILE做Logical BACKUP
  14. alter database backup controlfile to trace as '/home/oracle/bkup/controlfile01.log';

2017年8月25日 星期五

ORACLE Data Recovery Advisor

Oracle Data Recovery Advisor (DRA) 可以用來偵測並且處理資料庫失效事件

SCENARIOS 1:非系統的 DATAFILE 遺失

首先進行備份

rman target /
 
backup as backupset tablespace uses ;
backup as backupset incremental level 0 database;
backup database plus archivelog delete all input;
list backup of tablespace uses;
shutdown immediate;
exit;
將資料庫關閉。將某一DATAFILE (非系統) 搬移到其他位置,然後啟動,將發生下列錯誤訊息
出現錯誤訊息。執行RMAN並登入,查詢錯誤訊息(LIST FAILURE)之細節。

呼叫DRA,找出修正方案

執行修正程序

SCENARIOS 2:非系統的 DATAFILE 損壞

首先進行備份

rman target /
 
backup as backupset tablespace noncrit;
backup as backupset incremental level 0 database;
backup database plus archivelog delete all input;
list backup of tablespace noncrit;
shutdown immediate;
exit;
然後使用編輯器去編輯DATAFILE,記得對第一列的DATAFILE做異動

編輯存檔後,當資料庫關閉時將發生錯誤

進入RMAN,透過DRA 查詢錯誤

查詢解決方案

查詢完畢,在RMAN 底下執行 REPAIR FAILURE 指令,將一步一步進行修復

RMAN> repair failure;

Strategy: The repair includes complete media recovery with no data loss
Repair script: /oracle/app/oracle/diag/rdbms/prisedb/sedb/hm/reco_107603627.hm

contents of repair script:
   # restore and recover datafile
   restore datafile 14;
   recover datafile 14;

Do you really want to execute the above repair (enter YES or NO)? yes
executing repair script

Starting restore at 2017-08-25 23:05:25
using channel ORA_DISK_1

channel ORA_DISK_1: starting datafile backup set restore
channel ORA_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_DISK_1: restoring datafile 00014 to /oracle/app/oracle/oradata/sedb/noncrit.dbf
channel ORA_DISK_1: reading from backup piece /oracle/app/oracle/flash_recovery_area/PRISEDB/backupset/2017_08_25/o1_mf_nnndf_TAG20170825T205615_dt1wc0bs_.bkp
channel ORA_DISK_1: piece handle=/oracle/app/oracle/flash_recovery_area/PRISEDB/backupset/2017_08_25/o1_mf_nnndf_TAG20170825T205615_dt1wc0bs_.bkp tag=TAG20170825T205615
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:00:15
Finished restore at 2017-08-25 23:05:40

Starting recover at 2017-08-25 23:05:40
using channel ORA_DISK_1

starting media recovery

archived log for thread 1 with sequence 401 is already on disk as file /oracle/app/oracle/oradata/archivelog/1_401_904775628.dbf
archived log for thread 1 with sequence 402 is already on disk as file /oracle/app/oracle/oradata/archivelog/1_402_904775628.dbf
archived log for thread 1 with sequence 403 is already on disk as file /oracle/app/oracle/oradata/archivelog/1_403_904775628.dbf
archived log file name=/oracle/app/oracle/oradata/archivelog/1_401_904775628.dbf thread=1 sequence=401
media recovery complete, elapsed time: 00:00:07
Finished recover at 2017-08-25 23:05:48
repair failure complete

Do you want to open the database (enter YES or NO)? yes
database opened

SCENARIOS 3:Incomplete Recovery with RMAN (Point-in-Time Recovery)

非完全的復原(Recovery)只能在資料庫處於 MOUNT 狀態下時進行,且須擁有資料庫及ARCHIVE LOG的備份才行。進行該復原只有兩個原因: Complete Recovery 不可能或無法實施;或是刻意回到過去特定時間點,故意忽略或遺失該時間點之後的資料變更。例如,某時間點不慎將某 TABLE 或甚至 TABLESPACE 刪除,或不慎進行了某個條件錯誤的DML,導致使用者資料被錯誤地刪改。

Incomplete Recovery 有以下四個步驟

  1. Mount the database
  2. Restore all the datafiles
  3. Recover the database until a certain point
  4. Open the database with reset logs
Incomplete Recovery 有三個選項

  1. Until time
  2. Until system change number
  3. Until log sequence number
前置工作:進行資料庫與ARCHIVE LOG 的備份

設定 NLS_DATE_FORMAT 環境參數,並進行備份

在UNIX下,執行

export NLS_DATE_FORMAT="dd-mm-yy hh24:mi:ss"

開兩個視窗,一個是RMAN,另一個是SQLPLUS,兩個都去設定前述的 NLS_DATE_FORMAT

RMAN

[oracle@db01 ~]$ rman target /
connected to target database: SEDB (DBID=1140755226)

RMAN> backup as compressed backupset database;
Starting backup at 26-08-17 15:30:36
using target database control file instead of recovery catalog
......
Finished Control File and SPFILE Autobackup at 26-08-17 15:33:16
SQLPLUS 以SYSTEM身分登入SQLPLUS CONSOLE,執行

SQL> alter system switch logfile;

RMAN 再回到RMAN視窗,備份ARCHIVED LOG

RMAN> backup archivelog all delete all input;

Starting backup at 26-08-17 15:44:12
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archived log backup set
......
Finished Control File and SPFILE Autobackup at 26-08-17 15:45:24
SQLPLUS 以SYSTEM身分登入SQLPLUS CONSOLE,執行下列步驟

SQL> alter system switch logfile;

紀錄異動前時間點,並且插入一筆資料

SQL> select sysdate from dual;   --紀錄刪除前時間

SYSDATE               
-----------------    
26-08-17 15:47:57    
SQL> insert into system.ex16 values(sysdate);   --插入一筆資料

1 row created.

SQL> commit;

Commit complete.
SQL> drop tablespace noncrit including contents and datafiles;   --刪除TABLESPACE

Tablespace dropped.
RMAN

產生一個復原用的 CONTROLFILE,它的時間點為刪除TABLESPACE 之前,也就是剛剛執行 SELECT SYSDATE 的時間。

RMAN> run{
2> shutdown immediate;
3> startup mount;
4> set until time = '26-08-17 15:46:00';
5> restore controlfile to '/oracle/app/oracle/oradata/sedb/control03.ctl';
6> }
-- 15:47:57 之後,插入一筆,然後刪除 TABLESPACE
-- 步驟 5 : 將復原用的 CONTROLFILE 寫入到新的 CONTROLFILE 檔案 << CONTROLFILE03.CTL >>
database closed
database dismounted
Oracle instance shut down

connected to target database (not started)
Oracle instance started
database mounted

Total System Global Area     477073408 bytes

Fixed Size                     1337324 bytes
Variable Size                297797652 bytes
Database Buffers             171966464 bytes
Redo Buffers                   5971968 bytes

executing command: SET until clause

Starting restore at 26-08-17 16:04:07
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=18 device type=DISK
......
channel ORA_DISK_1: restored backup piece 1
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
Finished restore at 26-08-17 16:04:09
執行畫面如下
SQLPLUS 以SYSTEM身分登入SQLPLUS CONSOLE,執行下列步驟

SQL> alter system set control_files='/oracle/app/oracle/oradata/sedb/control03.ctl' scope=spfile;                                                     
System altered.

SQL> shutdown immediate;
ORA-01109: database not open

Database dismounted.
ORACLE instance shut down.

SQL> startup mount;
ORACLE instance started.

Total System Global Area  477073408 bytes
Fixed Size                  1337324 bytes
Variable Size             297797652 bytes
Database Buffers          171966464 bytes
Redo Buffers                5971968 bytes
Database mounted.
SQL> show parameter control_files

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
control_files                        string      /oracle/app/oracle/oradata/sed
                                                 b/control03.ctl


RMAN

執行前述的4 個步驟,以進行 INCOMPLETE RECOVERY。

RMAN> run {
2> allocate channel d1 type disk;
3> allocate channel d2 type disk;
4> set until time = '26-08-17 15:46:00';
5> restore database;
6> recover database;
7> alter database open resetlogs;
8> }

using target database control file instead of recovery catalog
allocated channel: d1
channel d1: SID=20 device type=DISK

allocated channel: d2
channel d2: SID=21 device type=DISK
....
executing command: SET until clause

Starting restore at 26-08-17 16:46:45
....
searching for all files in the recovery area
cataloging files...
cataloging done

List of Cataloged Files
=======================
File Name: /oracle/app/oracle/flash_recovery_area/PRISEDB/autobackup/2017_08_26/                                                                                        o1_mf_s_953049400_dt3z58tf_.bkp
.....
Finished restore at 26-08-17 16:48:44

Starting recover at 26-08-17 16:48:44

starting media recovery

archived log for thread 1 with sequence 408 is already on disk as file /oracle/a                                                                                        pp/oracle/oradata/archivelog/1_408_904775628.dbf
.....
media recovery complete, elapsed time: 00:00:02
Finished recover at 26-08-17 16:48:48

database opened
released channel: d1
released channel: d2

此時已經完成復原,回到刪除TABLESPACE 之前,而在刪除前INSERT的紀錄也因為復原而消失
檢查 V$LOG,確認 LOG SEQUENCE 是否已經重置

Block Recovery

一般而言,RMAN在備份時若遇到BLOCK ERROR時,會中斷備份。若在備份前設定BLOCK ERROR的可容許範圍,則在發生BLOCK ERROR時,RMAN不會被中斷。

若是出現BLOCK ERROR時,可採取的步驟如下:

RMAN> run {
  set maxcorrupt for datafile 7 to 100;
  backup datafile 7;
  }
  
  RMAN> block recover datafile 7 block 5;
  
  RMAN> block recover datafile 7 blocck 5,6,7 datafile 9 block 21,25;
  -- 一次復原數個BLOCK
  RMAN> block recover datafile 7 block 5 from backupset 1093;
  
  RMAN> block reselect * from v$database_block_corruption;

  RMAN> block recover corruption list until time sysdate - 7;

要查詢有哪些 BLOCK 有損壞,可透過下列 VIEW 來查詢

select * from v$database_block_corruption;

select * from v$backup_corruption;

select * from v$copy_corruption;

管理 RMAN 的PROCESS

RMAN的 PROCESS 可以由 V$PROCESS 和 V$SESSION 兩個VIEW做JOIN 來查詢

select sid, spid, client_info
from v$process p join v$session s on (p.addr = s.paddr)
where client_info like '%rman%';
但是上述查詢要透過欄位 CLIENT_INFO來查詢

透過 SET COMMAND ID 可以將欄位寫入註解資訊以利查詢

run
{
   set command id to 'bkup database';
   backup as compressed backupset database delete all input;
}
插入COMMAND ID 以後,可用下列方式查詢

select sid, spid, client_info
from v$process p join v$session s on (p.addr = s.paddr)
where client_info like '%id=%';
查詢顯示如下
另外一個可以查詢的動態VIEW 是 V$SESSION_LONGOPS,但在查詢以前,必須將參數 STATISTICS_LEVEL 設為 TYPICAL 或是 ALL

設為上述 LEVEL 以後,透過上述設定 RMAN 的 COMMAND ID,在執行RMAN的同時,可以查詢執行結果

select sid, serial#, opname, sofar, totalwork
from v$session_longops
where opname like 'RMAN%'
and sofar <> totalwork;
結果如下