2016年8月8日 星期一

學習初級Oracle PL/SQL (六)

Single Row Processing

Updates
現在,我開始用PL/SQL更新數據庫數據的一行或多行.讓我們先從一個簡單的update開始.
以下例子使用NO_DATA_FOUND if then else 的一段:
..
begin
 update emp set sal=n_sal where empno=n_empno;
 n_updated := sql%rowcount;
exception
 when others then raise_application_error(-20007, SQLERRM||' update record');
end;

UPDATE的語句中,你必須用WHERE子句作條件,否則將會全表更新.



使用SQL來執行複雜的更新
SQL UPDATE語句是非常強大的, 它在一個語句可以更新一個或多個列. 為什麼我花了這麼多時間說明和了解SQL? 因為,PL/SQL的全部目的是控制何時執行適當的SQL語句.它並不是用表現不佳的PL/SQL語句替換SQL.

在以下例子,你可以在SQL UPDATE語句一次更新多個column.
select c1.ename,c1.job,c2.ename mgr,c1.hiredate,c1.sal,c1.comm,c3.dname from emp c1 join emp c2 on c1.mgr=c2.empno join dept c3 on c1.deptno=c3.deptno where c1.job='SALESMAN' order by ename;
ENAME      JOB       MGR        HIREDATE         SAL       COMM DNAME
---------- --------- ---------- --------- ---------- ---------- --------------
ALLEN      SALESMAN  BLAKE      20-FEB-81       1600        300 SALES
KELLY      SALESMAN  KING       22-JUL-16       1400        200 SALES
MARTIN     SALESMAN  BLAKE      28-SEP-81       1250       1400 SALES
SALLY      SALESMAN  BLAKE      29-JUL-16       1500        300 SALES
TURNER     SALESMAN  BLAKE      08-SEP-81       1500          0 SALES
WARD       SALESMAN  BLAKE      22-FEB-81       1250        500 SALES
update emp c1 set (c1.mgr,c1.sal,c1.deptno) =(
select c2.mgr,c1.sal+5, c3.deptno from emp c2 join emp c4 on c2.mgr=c4.empno cross join dept c3
where c4.ename = case when c1.job='SALESMAN' then 'CLARK' else 'BLAKE' end
and c3.dname = case when c1.job='SALESMAN' then 'OPERATIONS' else 'SALES' end)
where c1.job='SALESMAN';
6 rows updated.
select c1.ename,c1.job,c2.ename mgr,c1.hiredate,c1.sal,c1.comm,c3.dname from emp c1 join emp c2 on c1.mgr=c2.empno join dept c3 on c1.deptno=c3.deptno where c1.job='SALESMAN' order by ename;

ENAME      JOB       MGR        HIREDATE         SAL       COMM DNAME
---------- --------- ---------- --------- ---------- ---------- --------------
ALLEN      SALESMAN  CLARK      20-FEB-81       1605        300 OPERATIONS
KELLY      SALESMAN  CLARK      22-JUL-16       1405        200 OPERATIONS
MARTIN     SALESMAN  CLARK      28-SEP-81       1255       1400 OPERATIONS
SALLY      SALESMAN  CLARK      29-JUL-16       1505        300 OPERATIONS
TURNER     SALESMAN  CLARK      08-SEP-81       1505          0 OPERATIONS
WARD       SALESMAN  CLARK      22-FEB-81       1255        500 OPERATIONS

以上例子用了SQLself-join, cross join, multi-update, sub-query case來更新了MGR,SALDEPTNO column.
使用update語句更新多項column,必須使用sub-query. MGRdeptnoEMP表中是以id顯示的,當然,我們不會記得id代表什麼.所以,我們必須用self-join,empno連接到mgr,cross join連接到dept 以顯示emp + dept 的各種可能性. 最後用上 2CASE,如果是SALESMANMGR=CLARK否則MGR= BLAKE. 如果DNAMESALESDNAME=OPERATIONS否則DNAME=SALES.



Deletes
在練習中, SQL DELETE 指令比起 insert, update, select 比較少用.然而,對於完整性,讓我們來看看如何在PL/SQL中使用它.

set serveroutput on size 1000000;
declare

-- n_count儲存delete指令的結果
n_count            number;
v_mgr           emp.ename%type := 'KING';
begin

 begin
  delete from emp c1 where c1.ename='ALAN' and c1.mgr=
  (select c2.empno from emp c2 where c2.ename=v_mgr);
   n_count := sql%rowcount;

 exception
  when OTHERS then raise_application_error(-20001, SQLERRM||' on delete WORKERS');
 end;

sys.dbms_output.put_line(to_char(n_count)||' record(s) delete.');
end;
/
最後, 讓我們來看看SELECT指令.



Selects
SQL SELECT語句在PL/SQL最常用的SQL語句,在這次練習中,將討論單SELECT語句的應該返回一行的結果.多數的PL/SQL stored procedures你會寫一個或多個的select語句.所以你可以使用PL/SQL. 現在我們回到最初的select例子,如下:
..
begin
  v_ename := 'SALLY';
  v_job     := 'SALESMAN';
  v_mgr    := 'BLAKE';
  n_sal     := 1500;
  n_comm        := 300;
        begin
         select count(1) into n_count from emp c1 cross join emp c2 where
         c1.ename=v_ename and c1.job=v_job and c2.ename=v_mgr and c1.sal=n_sal and c1.comm=n_comm;
        exception
         when others then raise_application_error(-20005, SQLERRM||' on detect duplicate');
        end;
        if n_count = 0 then

        begin
         select empno into n_mgr from emp where ename='BLAKE';
        exception
         when others then raise_application_error(-20002, SQLERRM||' get mgr number');
        end;
 end;
…..

紅色的statement用來偵測有沒有出現where內的條件,用果有就count(empno)並儲存到n_count, 如果沒有n_count = 0就進行下一步. 假設如果你需要update table, 但是沒有找到內容或內容多於一個,哪怎麼處理呢?



No Data Found
如之前介紹exception中有2個常用的exception.其中一個使用最多的就是NO_DATA_FOUND.當使用SELECT語句找不到WHERE的條件時,便使用NO_DATA_FOUNDexception.例子如下:
..
        begin
         select count(1) into n_count from emp c1 cross join emp c2 where
         c1.ename=v_ename and c1.job=v_job and c2.ename=v_mgr and c1.sal=n_sal and c1.comm=n_comm;
         n_selected := sql%rowcount;
        exception
         when NO_DATA_FOUND then
         n_selected := sql%rowcount;
         sys.dbms_output.put_line('Caught raised exception NO_DATA_FOUND.')
         when others then raise_application_error(-20002, SQLERRM||' get mgr number');
        end;



Too Many Rows
假若SELECT內容多於一個則用TOO_MANY_ROWS, 這裡注意,由於我們初學的SELECT PL/SQL只能返回一行結果,所以多於一行是會出現錯誤的.
declare
v_ename emp.ename%type;
v_job       emp.job%type;
 begin
  v_job := 'MANAGER';

   begin
    select ename into v_ename from emp where job=v_job;
   exception
    when others then raise_application_error(-20002, SQLERRM||' select');
   end;

  sys.dbms_output.put_line(v_ename);
end;
/

declare
*
ERROR at line 1:
ORA-20002: ORA-01422: exact fetch returns more than requested number of rows select
ORA-06512: at line 12
-- exception加入TOO_MANY_ROWS作處理   
when TOO_MANY_ROWS then sys.dbms_output.put_line('Database found more than one record');
v_ename := NULL;

結果:
Database found more than one record

PL/SQL procedure successfully completed.



以下是一個糟糕的Loop方案來用single row方式顯示select語句的多行結果
declare
 v_ename emp.ename%type;
 n_empno emp.empno%type;

function job(
 a1_num in out         emp.empno%type,
 a2_job in         emp.job%type)
return emp.ename%type as
v_ename   emp.ename%type;

begin
 select empno, ename into a1_num,v_ename from emp where empno > a1_num and job like a2_job and rownum=1;
 return v_ename;
exception
 when no_data_found then return v_ename;
 when others then raise_application_error(-20001, SQLERRM);
end job;

begin
 n_empno := 0;
loop
 v_ename := job(n_empno,'MANAGER');
if v_ename is null then exit;
end if;
sys.dbms_output.put_line(v_ename);
end loop;
end;
/

結果:
JONES
BLAKE
CLARK

PL/SQL procedure successfully completed.

總結,目前為止,應該明白到的是,一個singleton只能返回一個行的SQL語句的結果.



2016年8月5日 星期五

OCP 11gR2: Administration II 練習筆記 (四)

Lesson 04
=========================================================================
01. RMAN to perform recovery
non-critical:
- recovery tablespace (complete recovery)
 When tbs lost, offline it, restore and recover tbs then online tbs

Critical:
- datafile (system, undo, sysaux)
 Shutdown DB, startup mount restore and recover tbs then open DB



02. Recover image copy
- incremental backup update image copy SCN;
 after perform backup and apply archivelog to copy for fast switch, below command:
        RMAN> recovery copy of datafile ['n' | 'file_name'];
        e.g.
                run {
                allocate channel ch1 device type disk format 'location/name';
                backup as copy tablespace 'name'; }

        to fast swtich
                run {
                sql 'alter tablespace 'name' offline immediate';
                set newname for datafile 'number' to 'location/image_name';
                switch datafile all;
                recover tablespace 'tbs_name';
                sql 'alter tablespace 'name' online'; }
        






03. restore & recover in noarchivelog mode
- in noarchivelog mode need restore entire database
- incremental backup to recover a database in noarchivelog mode
        e.g.
        startup force nomount;
        restore controlfile;
        alter database mount;
        restore database;
        recover database;
        alter database open resetlogs



04. Normal Restore Point
A normal restore point enables you to flash the database back to a restore point within the time period determined by the DB_FLASHBACK_RETENTION_TARGET initialization parameter. The database automatically manages normal restore points. When the maximum number of restore points is reached, according to the rules described restore_point, the database automatically drops the oldest restore point. However, you can explicitly drop a normal restore point using the DROP RESTORE POINT statement.
        Command:
                RMAN> list restore point all;
                RMAN> create restore point 'name';
                RMAN> create restore point 'name' as of SCN 'number';
                SQL> select * from v$restore_point;



05. Point in time recovery
- Define the restore clause by SCN / TIME / SEQUENCE NUMBER
- In run block set until, restore, recover
- Open database in readonly mode for verify and below command to open.
        SQL> alter database open resetlogs;
- After restlogs, database should perform backup immediate.
- Controlfile only can keep 7 days SCN by default
- If need to restore over 7 days, need to restore old controlfile from backup first.



06. Loss of SPfile and controlfile
SPfile can copy alert.log parameter to pfile or restore from autobackup
        e.g.
        restore spfile:
                SQL> startup force nomount
                RMAN> restore spfile from autobackup;
                SQL> startup force

        Restore controlfile:
                SQL> startup nomount
                RMAN> restore controlfile from autoback;
                SQL> startup mount;
                SQL> alter database open resetlogs;



07. RMAN monitoring and tuning
Monitor RMAN backup jobs by view
- v$process join v$session where client_info like 'rman%' or run block "set command id to 'name';"
- v$session_longops for progress (make sure statistics_level parameter not in basic)
- use debug option to trace the logs
        Command: "rman target / catalog username@rcat debug trace trace.log" (also for MML troubleshoot)

RMAN tuning
Tuning RMAN need to find the bottleneck and RMAN process with below three phases
- Read Phase: production files --> Read Buffer  <-- it limited by maxopenfiles
- Copy Phase: Read buffer copy to output buffer  <-- memory to memory, no tuning
- Write Phase: output buffer --> write to media  <-- it limited by filesperset
- Parallelization of Backup Sets
 For performanace, you can allocate muliple channels and assign datafiles to specific channels or auto assign
        Datafiles 1,3,5 to channel 1 to device
        Datafiles 2,4,6 to channel 2 to device
        Datafiles 7,8,9 to channel 3 to device
  You can also use filesperset option to limit the number for datafile in backupset.

- Multiplexing:
RMAN can at the same time read multiple files from disk and then write their blocks into the same backup set. For example, RMAN can read from two datafiles simultaneously, and then combine the blocks from these datafiles into a single backup piece.

 Each channel allocates 4 output buffers of size 1 MB each
Multiplexing Level
Allocation Rule
Level <= 4
1 MB buffers are allocated; the total buffer size for all input files is 16 MB.
4 < Level <= 8
512 KB are allocated, the total buffer size for all files is less than 16 MB.
Level > 8
RMAN allocates four 128 KB disk buffers per channel for each file, the total size is 512 KB per channel for each file
 Multiplexing = Min (Min (DATAFILES,FILEPERSET) , MAXOPENFILES)
 maxopenfiles in allocate channel, filesperset in backup command.
 Filesperset = number per datafile in same backupset or channel.

- Defaults: MAXOPENFILES=8, FILESPERSET=64,
a) Channel=1, Datafiles=6, MAXOPENFILES=8, FILESPERSET=64   | Level of multiplex is 6 -only 6 datafiles
For example, assume that you backup 6 datafiles in 1 channel. Set filesperset to 64 and maxopenfiles to 8, in this case the multiplexing is 6 = [Min(Min(DATAFILES=6,FILEPERSET=64),MAXOPENFILES=8)]. When RMAN backup from disk, Each channel allocates 4 output buffers of size 512KB each, if DBWR_IO_SLAVES is set to 0. The best recovery performance do not set filesperset to greater than 24 (6 x 4 ), and 12 MB per channel ( 6x512x4/1024 )

b) Channel=1, Datafiles=12, MAXOPENFILES=8, FILESPERSET=64   | Level of multiplex is 8 -limited by maxopenfiles
c) Channel=1, Datafiles=12, MAXOPENFILES=8, FILESPERSET=6     | Level of multiplex is 6 -limited by filesperset
d) Channel=1, Datafiles=12, MAXOPENFILES=8, FILESPERSET=10   | Level of multiplex is 8 -limited by maxopenfiles
command example:
        run { allocate channel ch1 device type disk format='location' maxopenfiles 1;
        backup database filesperset 64; }
       
        run { allocate channel ch1 device type disk format='location' maxopenfiles 20;
        backup database filesperset 5; }

- Allocating Tape buffers
From SGA := BACKUP_TAPE_IO_SLAVES is True = Asynchronous (faster, use more memory from large pool)
From PGA := BACKUP_TAPE_IO_SLAVES is False = Synchronous (default is false)
Oracle recommends BACKUP_TAPE_IO_SLAVES to true and run with parallelism and multi device.
        -> if BACKUP_TAPE_IO_SLAVES is True, also need to define DBWR_IO_SLAVES (it depend on channels)
        -> Check async bottleneck in v$backup_async_io (column: long_waits / io_count)
            ( type='INPUT' means froms database to memory)

            ( type='OUTPUT' means from memory to external destination (tape or disk))

        -> Check sync bottleneck on discrete_bytes_per_second from v$backup_sync_io


- Channel Tuning
Prevent RMAN to consuming too much disk bandwidth
        1. set by RATE=1500K (1.5MB/sec)
        2. set by allocate channel
                run { allocate channel ch1 device type sbt;
                        allocate channel ch2 device type sbt;
                        allocate channel ch3 device type sbt;
                        backup (datafile 1,2,5 channel ch1) (datafile 4,6 channel ch2) (datafile 3,7,8 channel ch3);
                        backup database not backed up;}

- Backup duration
e.g. RMAN> backup duration 04:00 partial minimize load database filesperset 1;
        [minimize time | minimize load] [partial] *if no partial keyword and not enough time backup, all backupset failed.
        Time = the backup runs as far as possible (may combine with set rate)
        Load = The backup attempts to use the full amount of time. (It reduces load on the system)

- Validate
Validate may cause bottleneck because read tapes

Backup validate rman command create no output. It scan the files for content or block corruption, if any block corruption is found, it populates to the v$database_block_corruption.

To verify a consistent backup by below command:
RESTORE DATABASE PREVIEW ;
RESTORE DATABASE VALIDATE;
RESTORE ARCHIVELOG FROM sequence xx UNTIL SEQUENCE yy THREAD nn VALIDATE;
RESTORE CONTROLFILE VALIDATE;
RESTORE SPFILE VALIDATE;


Delete older backup by days
RMAN> delete backup completed before 'sysdate - 6';