2014年12月16日 星期二

ORA-02070: database does not support in this context

現象:
insert into remote_table@dblink(columns.....)
select columns..... from local_view ;
==> ORA-02070: database  does not support  in this context

解決方式:
1. 將 select columns..... from local_view 換成 select columns..... from local_table.....,不要使用view,改成table
2. 將procedure 由 local DB執行改為 remote DB執行,例如
    insert into local_table(columns...) select columns... from remote_view@dblink ;
3. 將 procedure 改成 cursor 運作

參考來源:
ORA-2070 during an remote Insert over Database link while selecting from local dictionary view (Doc ID 744219.1)

APPLIES TO:

Oracle Database - Enterprise Edition - Version 7.2.3.0 to 11.2.0.2 [Release 7.2.3 to 11.2]
Information in this document applies to any platform.

SYMPTOMS

ORA-2070 Error occurs when inserting into remote Table with Select from local Dictionary Views.
For Example :"Ora10g" is Local Database and "Dblink" is name of Database link that connects to Remote Database where Table T2 exists..T2 has same DDL structure as V$instance.
On executing below Remote Insert Statement, ORA-02070 Error occurs
insert into t2@ora10g(select *from v$instance)
*
ERROR at line 1:
ORA-02070: database does not support in this context


CHANGES

CAUSE


02070, 00000, "database %s%s does not support %s in this context"
// *Cause: The remote database does not support the named capability in
// the context in which it is used.
// *Action: Simplify the SQL statement.

//
The operation is unsupported which is clear from Bug 469264 "INSERT INTO REMOTE TABLE SELECT FROM LOCAL DICTIONARY VIEW GIVES 2070"
"The error is not a bug, but I've changed the message to print the name ofthe operation (in this case we will print "USERENV" instead of "operator 105") to make diagnosing possible."



SOLUTION

Workaround
You can use any one of 2 Solutions as shown below to resolve the issue:

1) Use Insert Statement in Remote Database and use Database links in Select statements .
   
insert into t2 (select * from v$instance@Dblink);
commit;

   Here Dblink is the name of Database Link that connects to Database where  information of table V$instance are taken.

2) Use a PL/Sql cursor in local Database to fetch Data from Local Database and then Insert into remote Database as shown in below Example :


declare
cursor c1 is select * from V$instance;


begin
for i in c1 loop
insert into T2@Dblink values(i.INSTANCE_NUMBER ,
i.INSTANCE_NAME ,
i.HOST_NAME,
i.VERSION ,
i.STARTUP_TIME ,
i.STATUS ,
i.PARALLEL ,
i.THREAD# ,
i.ARCHIVER ,
i.LOG_SWITCH_WAIT ,
i.LOGINS ,
i.SHUTDOWN_PENDING,
i.DATABASE_STATUS ,
i.INSTANCE_ROLE ,
i.ACTIVE_STATE ,
i.BLOCKED ) ;
end loop;
end;
/
commit;

Here "Dblink" is the name of Database link that connects
from Local database (where information on V$instance is taken)
to Remote database(where table T2 exists).

2014年12月11日 星期四

SAP Note: 88346 - Changing the RFC destination if IP changes are made

Symptom
After you change the IP addresses of a host, the RFC destinations for which this host is defined as the target host no longer work.

Other Terms
RFC, destination, IP, DNS, host name, target host

Reason and Prerequisites
If you enter the host name (for example hs0311) as the target host when you maintain the RFC destination, the host name is stored internally in the database table RFCDES (for the destination administration) as its corresponding IP address. Among other things, this should reduce the time required to set up the RFC connection. As a result, the conversion of the target host name into its IP address is no longer required. Furthermore, the host name (as a Domain Name Server, among other things) can assume any length. This is not suitable for storage in the database table RFCDES.

This will create problems if the IP addresses of the target hosts are changed. After this action has been carried out, you must therefore amend the affected RFC destinations that use the IP addresses accordingly in transaction SM59.

Note:
  • If you want to prevent the name of the target host from being stored as its respective IP address in the database table RFCDES, you must enter the host name as a SAP router name (/H/hs0311 instead of hs0311). In this case, the host name is stored as such in the database table RFCDES.

SAP Note: 403708 - Changing an IP address

Symptom
You want to change an IP address.

Other Terms
IP address, IP configuration, network, TCP/IP ,ipconfig

Reason and Prerequisites
This note provides information about configuring the operating system.

Solution
Internally, SAP systems use host names to communicate. As a result, you should experience no problems when you change an IP address.

Theoretically, you only need to make changes at operating system level:
- /etc/hosts
- /etc/services
- DNS entries
- Routing tables

However, you should err on the side of caution and check the following in the SAP system:
- Transaction SM59, table RFCDES, also see
  Note 88346 for additional information
- Transaction SM55, if you are using IP addresses here, you must adjust them
- Transaction SM51, check for consistent host names
- Transaction SMLG, check whether the entries for the server groups have been created with IP addresses

Also ensure that changes are not only made in the affected system but in all SAP systems, clients, and external programs that communicate with the affected system in any way.

Outside the SAP system:
- saprouter, adjust the saprouttab to the changes
* If the saprouter receives a new address:
  You should inform us of this change (for sapserv3).
  Open a message on the 'xx-ser-net-chg' component
  and inform us of the address change.
- Adjust the IP addresses so that the service connection continues to function
- If you use an APO system, see Note
  154997

Depending on your system landscape and configuration, additional
relevant points may be added. This note does not claim to be complete.

2014年12月8日 星期一

RMAN Backup and Recovery (Oracle 10g on AIX)

  • Install Oracle 10g software for RMAN catalog DB

Environment:
OS Level = AIX 5.3
Oracle DB Version = 10.2.0.3
Oracle Catalog DB SID = CATDB
Oracle RAC DB SID = PROD
Oracle RAC DB SID1 = PROD1
Oracle RAC DB SID2 = PROD2
TDPO node name = dbsrv_oracle


1. Install 10.2.0.1 software DB
login as root
cd /source/Disc1/cdrom/database/rootpre
./rootpre.sh
login as oracle
vi .profile
export TMP=/oracle/tmp
export TMPDIR=/oracle/tmp
export ORACLE_HOME=/oracle/10.2.0/db
export PATH=$ORACLE_HOME/bin:$ORACLE_HOME/OPatch:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
export ORACLE_SID=catdb
cd /source/Disc1/cdrom/database
export DISPLAY=catdbsrv:1.0
mkdir /oracle/tmp
./runInstaller
install software only

2. Apply 10.2.0.2 patchset
cd /source/patch/Disk1
./runInstaller

3. Create catalog database
dbca (SID=catdb)

4. Create Listener
netca

5. Create alias for PROD db
netca

  • Configure Catalog Database


1. Creating tablespace to store backup information

SQL> create tablespace rmancat datafile

'/rman/catdb/rmancat01.dbf' size 4g;

2. Creating the Recovery Catalog Owner

SQL> CREATE USER rman IDENTIFIED BY cat

TEMPORARY TABLESPACE temp

DEFAULT TABLESPACE rmancat

QUOTA UNLIMITED ON rmancat;

SQL> grant CONNECT,RESOURCE,RECOVERY_CATALOG_OWNER to rman;

3. Creating the Recovery Catalog
oracle> rman catalog rman/cat

RMAN> CREATE CATALOG;

4. Verify the user

sqlplus rman/cat

SQL> SELECT TABLE_NAME FROM USER_TABLES;


  • Configure Production Database


1. Login to db server using oracle account

2. Create tnsnames alias for catdb

netca

3. Link TSM Data Protection for Oracle

ln -s /usr/lib/libobk64.a $ORACLE_HOME/lib/libobk.a

4. Define Data Protection for Oracle options

login root

vi /usr/tivoli/tsm/client/oracle/bin64/tdpo.opt
dsmi_orc_config     /usr/tivoli/tsm/client/oracle/bin64/dsm.opt
dsmi_log     /usr/tivoli/tsm/client/oracle/bin64
tdpo_node     dbsrv_oracle
tdpo_pswdpath     /usr/tivoli/tsm/client/oracle/bin64

vi /usr/tivoli/tsm/client/oracle/bin64/dsm.opt
SErvername tdpo

vi /usr/tivoli/tsm/client/ba/bin/dsm.sys
SErvername     tdpo
CHANGINGRETRIES     0
NODENAME     dbsrv_oracle
PASSWORDACCESS     prompt
COMMMethod     TCPip
TCPPort     1600
TCPServeraddress     xx.xx.xx.xx

cd /usr/tivoli/tsm/client/api/bin64

ln -s /usr/tivoli/tsm/client/ba/bin/dsm.sys  dsm.sys

login root

dsmadmc

REG NODE   dbsrv_oracle   oracleprod maxnummp=2   passexp=0

cd /usr/tivoli/tsm/client/oracle/bin64

tdpoconf password (enter <password> three times)

tdpoconf   showenvironment   (confirm the configuration)

rm /usr/tivoli/tsm/client/oracle/bin64/tdpoerror.log


  • Managing Target Database in the Recovery Catalog


1. Registering a Database in the Recovery Catalog

login oracle

rman TARGET / CATALOG rman/cat@catdb

RMAN> REGISTER DATABASE;

RMAN> REPORT SCHEMA;

2. Configure Default RMAN Setting

RMAN> CONFIGURE DEVICE TYPE sbt PARALLELISM 2;

RMAN> CONFIGURE CHANNEL DEVICE TYPE sbt 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)';

RMAN> CONFIGURE CHANNEL 1 DEVICE TYPE sbt 

CONNECT 'SYS/<pwd>@PROD1' 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)';

RMAN> CONFIGURE CHANNEL 2 DEVICE TYPE sbt 

CONNECT 'SYS/<pwd>@PROD2' 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)';

RMAN> CONFIGURE CONTROLFILE AUTOBACKUP ON;

RMAN> crosscheck archivelog all;


  • PROD Database Backup Scripts


1. Level 0 Backup

rman target / catalog rman/cat@catdb

RMAN> backup incremental level 0

filesperset 50 

format "PROD_df_level0_%t_%s_%p" database

plus archivelog format "PROD_arch_%t_%s_%h"

delete all input;

RMAN> delete noprompt obosolete;

(The script is create under $ORACLE_HOME/scripts/rman_bck_level0.sh)

2. Level 1 Backup

rman target / catalog rman/cat@catdb

RMAN> backup incremental level 1 cumulative

filesperset 50

format "PROD_df_level1_%t_%s_%p" database

plus archivelog format "PROD_arch_%t_%s_%h”

delete all input;

(The script is create under $ORACLE_HOME/scripts/rman_bck_level1.sh)

3. Archive Log Files Backup

rman target / catalog rman/cat@catdb

RMAN> backup

filesperset 10

format "PROD_arch_%t_%s_%h" archivelog all delete all input;



  • Catalog Database Backup Scripts(Use DataPump)


1. Create Directory Definition for DataPump

sqlplus “/ as sysdba”

SQL> create directory dpump_dir as ‘/rman/bck_catdb’;

SQL> grant READ, WRITE ON DIRECTORY dpump_dir TO rman;

2. Use DataPump Export to backup the catalog

expdp rman/cat DUMPFILE=dpump_dir:exp_rman.dmp \

logfile=dpump_dir:exp_rman.log

  • Restore & Recovery Scripts


1. Loss all database files, but control files, and online redo log files 

are not loss

rman target / catalog rman/cat@catdb

RMAN> run {

startup mount;

restore database;

recover database;

alter database open;

}

2. Loss all database files, control files, and online redo log files

For this situation, you need to perform incomplete recovery. Please try to 

find the latest archived log file information in the alert file, and check the 

timing information

rman target / catalog rman/cat@catdb

RMAN> run {

startup nomount;

ALLOCATE CHANNEL T1 DEVICE TYPE sbt 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)'; 

ALLOCATE CHANNEL T2 DEVICE TYPE sbt 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)';

restore controlfile from autobackup;

alter database mount;

set until time = “to_date(‘<TIME_INFO>’,’YYYY/MM/DD HH24:MI:SS’)”;

restore database;

recover database;

alter database open resetlogs;

}


If the above recovery failed due to no latest archivelog file to be applied, then perform the following statements to open the database.

RMAN> shutdown immediate;

RMAN> startup mount;

RMAN> run { alter database open resetlogs; }

Remove all 2PC pending transactions

Generate the scripts first if there are many 2PC pending transactions, then edit the scripts 

to add ‘commit;’ statement for every DBMS_TRANSACTION statement

sqlplus “/ as sysdba”

set linesize 120

set pages 24

set head off

spool remove_2pc_pending.sql

select 'exec DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY ('''||

LOCAL_TRAN_ID||''');' from DBA_2PC_PENDING;

spool off

Remove all expired archivelogs

$ cd /prodarch

remove restored archivelogs

RMAN> crosscheck archivelog all;

RMAN> delete noprompt expired archivelog all;

3. All the machines crash, need to find a new machine to startup the database

Install Oracle 10g Software on the new machine

Setup TSM and Data Protection for Oracle

Please refer the “Configure Production Database” Section

Create initial parameter initPROD1.ora with the following setting

background_dump_dest =/oracle/10.2.0/db/admin/PROD1/bdump

core_dump_dest =/oracle/10.2.0/db/admin/PROD1/cdump

user_dump_dest =/oracle/10.2.0/db/admin/PROD1/udump

db_name ="PROD"

instance_name =PROD1

control_files =('/prodora/control01.ctl','/proddata1/control02.ctl','/prodindx1/control03.ctl')

#Set the below to the same as the production target

undo_management =AUTO

undo_tablespace =APPS_UNDOTBS1

db_block_size = 8192

log_archive_format=%t_%s_%r.arc

log_archive_dest_1=’location=/prodarch/’

log_archive_format=%t_%s_%r.arc

sga_target=8G

db_cache_size=4G

shared_pool_size=2G

pga_aggregate_target=16G

compatible = 10.2.0.

# To clone RAC db to non-RAC db need to add this parameter

_no_recovery_through_resetlogs=TRUE

Perform the Recovery

rman target / catalog rman/cat@catdb

RMAN> run {

startup nomount;

ALLOCATE CHANNEL T1 DEVICE TYPE sbt 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)';

ALLOCATE CHANNEL T2 DEVICE TYPE sbt 

PARMS='ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)';

restore controlfile from autobackup;

alter database mount;

set until time = “to_date(‘<TIME_INFO>’,’YYYY/MM/DD HH24:MI:SS’)”;

restore database;

recover database;

alter database open resetlogs;

}

If the above recovery failed due to no latest archivelog file to be applied, then perform the 

following statements to open the database.

RMAN> shutdown immediate;

RMAN> startup mount;

RMAN> run { alter database open resetlogs; }

Remove all 2PC pending transactions

Generate the scripts first if there are many 2PC pending transactions, then edit the scripts 

to add ‘commit;’ statement for every DBMS_TRANSACTION statement

sqlplus “/ as sysdba”

set linesize 120

set pages 24

set head off

spool remove_2pc_pending.sql

select 'exec DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY ('''||

LOCAL_TRAN_ID||''');' from DBA_2PC_PENDING;

spool off

Remove all expired archivelogs

$ cd /prodarch

remove restored archivelogs

RMAN> crosscheck archivelog all;

RMAN> delete noprompt expired archivelog all;

  • Duplicate Database


1. create an alias entry in tnsnames.ora file for PROD and catdb(optional)

2. confirm the connection to PROD and catdb(optional) working

PROD =

(DESCRIPTION =

(ADDRESS = (PROTOCOL = tcp)(HOST = db1srv_vip.domain)(PORT = 1521))

(CONNECT_DATA =

(SERVICE_NAME = PROD)

(INSTANCE_NAME = PROD1)

)

)

CATDB =

(DESCRIPTION =

(ADDRESS_LIST =

(ADDRESS = (PROTOCOL = TCP)(HOST = catdbsrv.domain)(PORT = 1521))

)

(CONNECT_DATA =

(SERVICE_NAME = catdb)

)

)

sqlplus system/<pwd>@prod

sqlplus rman/cat@catdb

3. determine the files location and space to be restored

this information is used to set ‘db_file_name_convert’ parameter in 

init<SID>.ora file

4. create the init<SID>.ora parameter file and administration directories 

for the duplicate database, the parameter file should have the following 

setting:

background_dump_dest =/newprod/10.2.0/db/admin/NEWPROD_otsrv/bdump

core_dump_dest =/newprod/10.2.0/db/adminNEWPROD_otsrv/cdump

user_dump_dest =/newprod/10.2.0/db/admin/NEWPROD_otsrv/udump

db_name ="NEWPROD"

instance_name =NEWPROD

control_files =('/newprod/data/control01.ctl','/newprod/data /control02.ctl','/newprod/data /control03.ctl')

db_file_name_convert =("/proddata1/","/newprod/data/", "/prodindx1/","/newprod/data/", "/prodindx1/","/
newprod/data/", "/prodora/","/newprod/data/")

log_file_name_convert =("/prodora/PROD/", "/newprod/data/",”/proddata1/”,”/newprod/data/”)

#Set the below to the same as the production target

undo_management =AUTO

undo_tablespace =APPS_UNDOTBS1

db_block_size = 8192

log_archive_format=%t_%s_%r.arc

log_archive_dest_1=’location=/arch/’

compatible = 10.2.0.

# To clone RAC db to non-RAC db need to add this parameter

_no_recovery_through_resetlogs=TRUE

5. Startup the duplicate database instance

Sqlplus “/ as sysdba”

SQL> startup nomount;

6. Prepare RMAN duplicate script

run {

allocate auxiliary channel t1 type sbt

parms="ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)";

allocate auxiliary channel t2 type sbt

parms="ENV=(TDPO_OPTFILE=/usr/tivoli/tsm/client/oracle/bin64/tdpo.opt)";

set until time "to_date('<the_time_within_RMAN_backup>','YYYY/MM/DD 

HH24:MI:SS')";

duplicate target database to NEWPROD;

}

7. run RMAN to duplicate the database

rman target sys/<pwd>@prod nocatalog auxiliary /

RMAN> @duplicate.script

2014年11月4日 星期二

Oracle 資料庫轉換字元集工具:csscan

# 主題
CSSCAN (Character Set Scanner) 


# 適用版本
Oracle Database - Enterprise Edition - Version 8.1.7.4 and later 
Oracle Database - Standard Edition - Version 8.1.7.4 and later 
Information in this document applies to any platform. 


# 現象/目的 
在從前建置Oracle DB時,我們會將字元集設定成BIG5,但是網路促成的無國界情境使得種種軟體包含資料庫本身都必須容納多國語系,所以公司內部的資料庫大多早早已轉換成UTF8,但是受限於商業軟體,或者是其他考量,少部分資料庫還是停留在BIG5,倘若有朝一日當資料庫需要做轉換時,如果來源端跟目的端的資料庫字元集不一致時,必須先使用csscan 工具來確認對資料的影響性。


# 解決方式/內容
1. 準備動作
  1.1 確認目前字元集: 
         SQL> select value 
                    from NLS_DATABASE_PARAMETERS 
                    where parameter='NLS_CHARACTERSET'; 

NLS_CHARACTERSET 定義存放在資料庫的是何種字元,而非由 NLS_LANGUAGE 或是 NLS_TERRITORY 決定的 

  1.2 DB Version小於10.2.0.4或是11.1.0.6,impdp會有資料毀損狀況,expdp不受影響,apply patch 5874989可解決此bug。這問題在10.2.0.4 and 11.1.0.7 patch set,或是11.2.0.1 以上版本被解決。

  1.3 清除recyclebin
        $ sqlplus / as sysdba


        SQL> SELECT OWNER, ORIGINAL_NAME, OBJECT_NAME, TYPE 
                   FROM dba_recyclebin ORDER BY 1,2;

        SQL> purge dba_recyclebin;

  1.4 compile invalid objects
        $ sqlplus / as sysdba

        SQL> SELECT owner,object_name,object_type,status
                   FROM dba_objects
                   WHERE status ='INVALID';


        SQL> @?/rdbms/admin/utlrp.sql

  1.5 可以移除sample schema:'HR', 'OE', 'SH', 'PM', 'IX', 'BI' and 'SCOTT',如果沒有用到APEX / HTML DB也可移除FLOWS_XXX 以及APEX_XXX Users。

2. 參考 Note 745809.1 安裝必要物件 (10g / 11g)
$ export ORACLE_SID=ORCL 
$ sqlplus /nolog 
SQL> conn / as sysdba 
SQL> set termout on 
SQL> set echo on 
SQL> spool csminst.log 
SQL> -- note the drop user 
SQL> drop user csmig cascade; 
SQL> @?/rdbms/admin/csminst.sql 

3. 執行 csscan
$ csscan \"system/password as sysdba\" USER=schema TOCHAR=UTF8 ARRAY=1024000 PROCESS=3 LOG=csscan_log

4. 檢查產出的報表並修正
  4.1  csscan_log.out ==> 執行csscan的過程
  4.2  csscan_log.txt ==> tables / column 有無異常的總表
  4.3  csscan_log.err ==> tables / column 有異常的資料明細
  4.4  通常BIG5轉UTF8欄位大小是除以2乘以3:column_size / 2 * 3 (因為一個中文字在BIG5中佔2個bytes,在UTF8中佔3個bytes)


# 參考文件
Note 225912.1 
Changing Or Choosing the Database Character Set ( NLS_CHARACTERSET ) 

Note 458122.1 
Installing and Configuring Csscan in 8i and 9i (Database Character Set Scanner) 

Note 745809.1 
Installing and configuring Csscan in 10g and 11g (Database Character Set Scanner) 

Note 444701.1 
Csscan output explained

(Doc ID 260192.1)
Changing the NLS_CHARACTERSET to AL32UTF8 / UTF8 (Unicode) in 8i, 9i , 10g and 11g

2014年10月30日 星期四

執行Procedure 對Remote Database 做DML 動作時發生ORA-2069 錯誤訊息

Ora-02069 When Using a Local Function While Updating a Remote Table (Doc ID 342320.1) 這篇文章有提到:無法使用 Local Function 針對 Remote Database做 DML ( insert / update / delete) 動作,否則就會發生 ORA-2069 錯誤訊息。
暫時解決方式如下:
1. 將 session level 的 global_name 設成 true:alter session set global_names=true
2. 將 function 建在 Remote Database 上面
3. PL/SQL Code 中,盡可能的不使用function
4. 先將要處理的資料放在Local Table / Local Temp Table 中,當對 Remove Database 做DML 時,再拿出來引用


PS:
Doc ID 342320.1:

APPLIES TO:

Oracle Server - Enterprise Edition - Version 9.2.0.1 to 11.2.0.2 [Release 9.2 to 11.2]
Information in this document applies to any platform.

SYMPTOMS

In Oracle Server executing the following update:
update t1
set c1=c1
where c2=pkg_date.Fn_GetDate;

where pkg_date.Fn_GetDate is a function of a local package that returns a sysdate,
and t1 is a synonym that points to a remote table, defined through a database link.

returns
"ORA-02069: global_names parameter must be set to TRUE for this operation"
on the function pkg_date.Fn_GetDate on the where condition.

CHANGES

CAUSE

Because of a limitation, it is not possible to use a local function when doing a dml operation on a remote table .When this is attempted, the ora-2069 is raised.

SOLUTION

These are possible workaround to avoid the ora-2069 error in the described scenario
1. Use global_names=true.  This can be done on a session basis: "alter
    session set global_names=true".  If you are having problems getting this
    to work, it is no doubt a configuration problem.  Please open a separate
    TAR for this if you can't figure it out.
2. Put the function to be used at the remote site.
3. Put a wrapper function at the remote site which calls the actual function
    over a database link back to the local site.
4. Include in the "from" the "dual" table. You'll have a Cartesian product (with dual) and the functions will be applied in the calling side, hence some performance issues can be raised.

REFERENCES

BUG:671775 - GET A ORA-2069 OR ORA-2019 WHEN TRYING TO USE A FUNCTION WITH A DB_LINK

2014年9月16日 星期二

定期刪除table過期資料

1. 建立執行清除資料的 Table 候選清單
    create table "zdba_del_tab"
2. 建立 Procedure 只處理 delete 動作
    create procedure "zdba_delete_commit"
3. 建立 Procedure 呼叫 "zdba_delete_commit" 處理 Table清單 "zdba_del_tab" 中的 Table
    create procedure "zdba_del_tab_p""zdba_delete_commit" 做刪除動作
4. 建立 Job 定期自動執行


1.
CREATE TABLE CMO.ZDBA_DEL_TAB
(
  OWNER         VARCHAR2(30 BYTE),
  SEGMENT_NAME  VARCHAR2(81 BYTE),
  KEYCOL        VARCHAR2(30 BYTE),
  CONDITION     VARCHAR2(10 BYTE),
  KEYCOLVAL     VARCHAR2(100 BYTE),
  ENABLED       VARCHAR2(1 BYTE),
  STIME         DATE,
  ETIME         DATE,
  CONDITION2    VARCHAR2(500 BYTE),
  SHRINK        VARCHAR2(1 BYTE),
  STATUS        VARCHAR2(20 BYTE)
)
TABLESPACE <tablespace_name>;

COMMENT ON TABLE ZDBA_DEL_TAB IS
'Purpose: Table List for Delete Expired Table Data  
Created by: DBA  
Keep days: Always  
Purge key: NA  
Desc:  
  enabled=A :簡單型, 每日1:00執 delete  by crontab
  enabled=B :簡單型, 每日5:00執 delete  by DB job
  enabled=C :複雜型, 每日1:00執 delete  by crontab in
  enabled=X :不執行 delete';

COMMENT ON COLUMN ZDBA_DEL_TAB.SHRINK IS '是否做shrink動作';


CREATE UNIQUE INDEX ZDBA_DEL_TAB_U01 ON ZDBA_DEL_TAB
(OWNER, SEGMENT_NAME)
LOGGING
TABLESPACE <tablespace_name>;


ALTER TABLE ZDBA_DEL_TAB ADD (
  CONSTRAINT ZDBA_DEL_TAB_U01
 UNIQUE (OWNER, SEGMENT_NAME)
    USING INDEX
    TABLESPACE <tablespace_name>);


2.
CREATE OR REPLACE PROCEDURE zdba_delete_commit (
   p_statement           IN   VARCHAR2,
   p_commit_batch_size   IN   NUMBER DEFAULT 10000
)
IS
/* ----------------------------------
Purpose: Delete Table in Batch Mode
Created by: DBA
Date:
structure:
  1. define sql statement
  2. open cursor
  3. execute cursor
  4. close cursor
updated:
---------------------------------- */
   cid                 INTEGER;
   changed_statement   VARCHAR2 (2000);
   finished            BOOLEAN;
   nofrows             INTEGER;
   lrowid              ROWID;
   rowcnt              INTEGER;
   errpsn              INTEGER;
   sqlfcd              INTEGER;
   errc                INTEGER;
   errm                VARCHAR2 (2000);
BEGIN
-- If the actual statement contains a WHERE clause, then append a
-- rownum < n clause after that using AND, else use WHERE rownum < n clause
   IF (UPPER (p_statement) LIKE '% WHERE %')
   THEN
      changed_statement :=
         p_statement || ' AND rownum < ' || TO_CHAR (p_commit_batch_size + 1);
   ELSE
      changed_statement :=
         p_statement || ' WHERE rownum < '
         || TO_CHAR (p_commit_batch_size + 1);
   END IF;

   BEGIN
      cid := DBMS_SQL.open_cursor;              -- Open a cursor for the task
      DBMS_SQL.parse (cid, changed_statement, DBMS_SQL.native);
                                                         -- parse the cursor.
      rowcnt := DBMS_SQL.last_row_count;
   -- store for some future reporting
   EXCEPTION
      WHEN OTHERS
      THEN
         errpsn := DBMS_SQL.last_error_position;
                               -- gives the error position in the changed sql
                               -- delete statement if anything happens
         sqlfcd := DBMS_SQL.last_sql_function_code;
                                     -- function code can be found in the OCI
                                     -- manual
         lrowid := DBMS_SQL.last_row_id;
         -- store all these values for error reporting. However
         -- all these are really useful in a stand-alone proc
         -- execution for DBMS_OUTPUT to be successful, not
         -- possible when called from a form or front-end tool.
         errc := SQLCODE;
         errm := SQLERRM;
         DBMS_OUTPUT.put_line (   'Error:'
                               || TO_CHAR (errc)
                               || ' Posn:'
                               || TO_CHAR (errpsn)
                               || 'SQL fCode '
                               || TO_CHAR (sqlfcd)
                               || ' rowid'
                               || ROWIDTOCHAR (lrowid)
                              );
         raise_application_error (-20000, errm);
                             -- it'll ensure the display of at least the error
                             -- message if something happens.
   END;

   finished := FALSE;

   WHILE NOT (finished)
   LOOP      -- keep on executing the cursor till there is no more to process.
      BEGIN
         nofrows := DBMS_SQL.EXECUTE (cid);
         rowcnt := DBMS_SQL.last_row_count;
      EXCEPTION
         WHEN OTHERS
         THEN
            errpsn := DBMS_SQL.last_error_position;
            sqlfcd := DBMS_SQL.last_sql_function_code;
            lrowid := DBMS_SQL.last_row_id;
            errc := SQLCODE;
            errm := SQLERRM;
            DBMS_OUTPUT.put_line (   'Error:'
                                  || TO_CHAR (errc)
                                  || 'Posn:'
                                  || TO_CHAR (errpsn)
                                  || 'SQL fCode '
                                  || TO_CHAR (sqlfcd)
                                  || ' rowid'
                                  || ROWIDTOCHAR (lrowid)
                                 );
            raise_application_error (-20000, errm);
      END;

      IF nofrows = 0
      THEN
         finished := TRUE;
      ELSE
         finished := FALSE;
      END IF;

      COMMIT;
   END LOOP;

   BEGIN
      DBMS_SQL.close_cursor (cid);     -- close the cursor for a clean finish
   EXCEPTION
      WHEN OTHERS
      THEN
         errpsn := DBMS_SQL.last_error_position;
         sqlfcd := DBMS_SQL.last_sql_function_code;
         lrowid := DBMS_SQL.last_row_id;
         errc := SQLCODE;
         errm := SQLERRM;
         DBMS_OUTPUT.put_line (   'Error:'
                               || TO_CHAR (errc)
                               || ' Posn:'
                               || TO_CHAR (errpsn)
                               || 'SQL fCode '
                               || TO_CHAR (sqlfcd)
                               || ' rowid'
                               || ROWIDTOCHAR (lrowid)
                              );
         raise_application_error (-20000, errm);
   END;
END;
/



3.
CREATE OR REPLACE PROCEDURE zdba_del_tab_p (p_enabled IN VARCHAR2)
IS
/*
Purpose: Delete Expired Data
Created by: DBA
Desc: 排crontab 每日 1:00執行
Update:
*/
   CURSOR c1
   IS
      SELECT owner, segment_name, keycol, condition, keycolval, condition2
        FROM zdba_del_tab
       WHERE enabled = p_enabled;

   r1           c1%ROWTYPE;
   str          VARCHAR2 (500);
   v_sqlcode    VARCHAR2 (200);
   v_sqlerrm    VARCHAR2 (200);
   v_stime      DATE;
   v_etime      DATE;
   v_difftime   NUMBER;
   v_sender     VARCHAR2 (200) := 'XX System';
   v_maillist   VARCHAR2 (200)
                         := 'dba@domain,dba_backup@domain';
BEGIN
   OPEN c1;

   IF p_enabled = 'A' or p_enabled = 'B'
   THEN
      SELECT SYSDATE
        INTO v_stime
        FROM DUAL;

      LOOP
         FETCH c1
          INTO r1;

         EXIT WHEN c1%NOTFOUND;

         BEGIN
            UPDATE zdba_del_tab
               SET stime = SYSDATE
             WHERE owner = r1.owner AND segment_name = r1.segment_name;

            COMMIT;
            str :=
                  'delete from '
               || r1.owner
               || '.'
               || r1.segment_name
               || ' where '
               || r1.keycol
               || ' '
               || r1.condition
               || ' '
               || r1.keycolval;
            zdba_delete_commit (str);

            UPDATE zdba_del_tab
               SET etime = SYSDATE
             WHERE owner = r1.owner AND segment_name = r1.segment_name;

            COMMIT;
         EXCEPTION
            WHEN OTHERS
            THEN
               raise_application_error (-20001,
                                           'A loop error was encountered '
                                        || SQLCODE
                                        || ' -ERROR- '
                                        || SQLERRM
                                       );
         END;
      END LOOP;

      SELECT SYSDATE
        INTO v_etime
        FROM DUAL;

      v_difftime := trunc((v_etime - v_stime) * 24 * 60,2);
   ELSIF p_enabled = 'C'
   THEN
      SELECT SYSDATE
        INTO v_stime
        FROM DUAL;

      LOOP
         FETCH c1
          INTO r1;

         EXIT WHEN c1%NOTFOUND;

         BEGIN
            UPDATE zdba_del_tab
               SET stime = SYSDATE
             WHERE owner = r1.owner AND segment_name = r1.segment_name;

            COMMIT;
            str :=
                  'delete from '
               || r1.owner
               || '.'
               || r1.segment_name
               || ' where '
               || r1.condition2;
            zdba_delete_commit (str);

            UPDATE zdba_del_tab
               SET etime = SYSDATE
             WHERE owner = r1.owner AND segment_name = r1.segment_name;

            COMMIT;
         EXCEPTION
            WHEN OTHERS
            THEN
               raise_application_error (-20001,
                                           'A loop error was encountered '
                                        || SQLCODE
                                        || ' -ERROR- '
                                        || SQLERRM
                                       );
         END;
      END LOOP;

      SELECT SYSDATE
        INTO v_etime
        FROM DUAL;

      v_difftime := trunc((v_etime - v_stime) * 24 * 60,2);
   ELSE
      NULL;
   END IF;

   CLOSE c1;

   UTL_MAIL.send (sender          => v_sender,
                  recipients      => v_maillist,
                  cc              => NULL,
                  subject         => 'OK...!! Delete Expired Data Completely. Type='||p_enabled||' (apps.zdba_del_tab_p)',
                  MESSAGE         =>    'Total consume '
                                     || v_difftime
                                     || ' min, from '
                                     || TO_CHAR (v_stime,
                                                 'yyyy-mm-dd hh24:mi:ss'
                                                )
                                     || ' to '
                                     || TO_CHAR (v_etime,
                                                 'yyyy-mm-dd hh24:mi:ss'
                                                ),
                  mime_type       => 'text/plain; charset=UTF-8'
                 );
EXCEPTION
   WHEN OTHERS
   THEN
      raise_application_error (-20001,
                                  'A main error was encountered '
                               || SQLCODE
                               || ' -ERROR- '
                               || SQLERRM
                              );
      v_sqlcode := SQLCODE;
      v_sqlerrm := SQLERRM;
      UTL_MAIL.send (sender          => v_sender,
                     recipients      => v_maillist,
                     cc              => NULL,
                     subject         => 'ERROR...!! Delete Expired Data Failed. Type='||p_enabled||' (apps.zdba_del_tab_p)',
                     MESSAGE         =>    'SQLCODE: '
                                        || v_sqlcode
                                        || ' ,SQLERRM: '
                                        || v_sqlerrm,
                     mime_type       => 'text/plain; charset=UTF-8'
                    );
END;
/