Oracle 數據庫復制常用腳本Oracle的數據復制是一個Oracle數據庫產品中比較成熟的一項技術,它是整個分布式計算解決方案的一個重要組成部分。 對于具有復制環境的數據庫系統,和Oracle DBA一樣,同樣要有一個人來專門負責維護Oracle的數據復制問題,稱之為Oracle Replication Administrator(Oracle復制治理員)。本文就Oracle數據復制中復制治理員經常關心的一些關于復制系統的問題,編寫了不同的存儲過程。當然,通過Oracle提供的復制治理器也可以達到這些目的,但在實際應用中,Oracle復制治理器具有不靈活,速度慢,同時我們不能對其查詢結果進行隨意控制等等的缺點。一、查看Oracle8x延遲事務隊列調用及調用個數
推(push)一個大的延遲事務(Deferred Transactions)隊列是非常慢的。一個常見的問題就是一個事務中含有非常多的調用(calls)。假如系統檢測到一個錯誤,如ora-01403(數據未找到錯誤),也就是我們常說的檢測到沖突,而且沒有沖突消除方法,寫到deferror錯誤表和回滾事務的時間就會更長,事務中假如調用calls很多,則消耗在任何一個調用的時間就會以指數形式增長。對于數據復制中每個事務的調用數Oracel強烈建議不超過50個。下面的存儲過程提供了一個快速查看延遲事務隊列中的事務列表以及每個事務中調用數的腳本。輸出是以傳輸順序排列的,這個順序就是事務將要傳播到主節點的順序。這對于確定傳播中的延遲以及掛起等是非常有幫助的。在搭建Oracle數據復制環境中,有一個經驗是值得注重的,那就是一定要設置沖突解決方案,即可使用Oracle系統提供的幾種方案,也可以自己編寫腳本來完成沖突處理。為什么這樣說,一定要設置沖突解決方案,也許我們可以說,我們的復制環境是一個單項復制,不可能出現沖突現象。在這里我講一個自己的親身經歷的例子來說明這個問題,在實際工作中,我搭建了一個具有15個節點的高級復制環境,一個主定義節點,14個主節點,其中14個主節點向主定義節點單向傳遞數據。一般來講這種情況下不會出現沖突。但是在實際應用中,問題就出現了,其中幾個節點在傳輸了一些數據后,就掛(hang)了起來。所有可能查找的原因到查了,始終發現不了問題的根結。最后發現是由于數據傳播到遠程節點后,出現了錯誤,而在該節點又沒有設置沖突解決方案,同時出現錯誤的該事務又具有超過1000條的調用。其中有一條調用出錯了,事務需要回滾,而這個在本地和遠程節點間的回滾時間就會以幾何基數增長。就出現了前面談到的系統掛起(hang)的表現。這也就是說即是在根本不可能出現沖突的系統中,由于偶然的不定的錯誤進程,批量數據的處理以及沒有考慮復制情況下的數據導入都可造成在傳播過程中嚴重的性能問題,最嚴重的情況就是系統掛起,不能完成正常的復制工作。最簡單和有效的辦法就是在多路復制環境下設置一個系統確省的沖突處理方法來避免這種情況的發生。下面這個存儲過程就是在Oracle 8 環境下列出延遲事務隊列中的事務和事務中調用個數的腳本,該腳本對Oracle 7 不支持,這是因為Oracle 8 和Oracle 7 的復制機制發生了變化。存儲過程調用方法:在SQL/PLUS下,首先運行下面的設置,使存儲過程的輸出到屏幕上,SET SERVEROUTPUT ON SIZE 200000
EXEC P_LIST_TRANSACTIONS(ORA_SJJK); 其中,存儲過程的參數as_destination為所要查看的事務傳播到目的節點的延遲隊列,為數據庫聯接(DBLINK)名。假如我們看到一個事務具有很多的調用(超過50個),這個事務很可能就是造成延遲事務隊列推過程延遲甚至是掛起的原因。附:存儲過程腳本:下載該腳本CREATE OR REPLACE PROCEDURE REPADMIN.P_LIST_TRANSACTION(as_destination in VARCHAR2) IS
local_node VARCHAR2(128);
remote_node VARCHAR2(128);
last_scn NUMBER;
last_tid VARCHAR2(22);
last_tdb VARCHAR2(128);
cnt NUMBER;CURSOR c(last_delivered NUMBER, last_tid VARCHAR2, last_tdb VARCHAR2) IS
select cscn, enq_tid,
dscn, DECODE(c.recipient_key, 0, 'D', 'R')
from system.def$_aqcall c where
(c.cscn >= last_delivered)
and ((c.cscn > last_delivered) or (c.enq_tid > last_tid))
and (
( c.recipient_key = 0
and exists ( select /*+ index(cd def$_calldest_primary) */ null
from system.def$_calldest cd
where cd.enq_tid = c.enq_tid
and cd.dblink = remote_node ) )
or ( c.recipient_key > 0
and ( ( exists (
select null from system.repcat$_repprop P
where P.dblink = remote_node
and P.how = 1
and P.recipient_key = c.recipient_key
and ((P.delivery_order is NULL)
or (P.delivery_order < c.cscn))))
or ( exists
( select /*+ ordered use_nl(rp) */ null
from system.def$_aqcall cc, system.repcat$_repprop rp
where cc.enq_tid = c.enq_tid
and cc.cscn is null
and rp.recipient_key = cc.recipient_key
and rp.how = 1
and rp.dblink = remote_node
and ((rp.delivery_order is NULL)
or (rp.delivery_order < c.cscn)))))))
order by c.cscn, c.enq_tid;
BEGIN
SELECT NLS_UPPER(global_name) INTO local_node FROM global_name;
SELECT dblink INTO remote_node from deftrandest
WHERE dblink LIKE UPPER (as_destination'%') AND ROWNUM < 2;
IF (remote_node IS NULL) THEN
DBMS_OUTPUT.PUT_LINE ('不能確定目標節點,輸入參數有誤!');
RETURN;
ELSE
DBMS_OUTPUT.PUT_LINE ('延遲事務目標節點為: 'remote_node);
DBMS_OUTPUT.PUT_LINE ('-------------------------------------------');
END IF;
SELECT last_delivered, last_enq_tid, dblink
INTO last_scn, last_tid, last_tdb
FROM system.def$_destination
WHERE dblink = remote_node;FOR R IN C(last_scn,last_tid,last_tdb) LOOP
SELECT count(*) INTO cnt FROM system.def$_aqcall WHERE enq_tid = r.enq_tid;
DBMS_OUTPUT.PUT_LINE ('延遲事務 ID='r.enq_tid' 調用個數='to_char(cnt));
END LOOP;
END;
/
二、Oracle 8高級復制環境設置問題診斷腳本要保證搭建的一個高級復制環境工作,必須保證所有的復制對象處于正常狀態,對于一個高級復制環境,要檢查一個復制環境中是否所有對象均處于正常工作狀態,需要檢查不同的系統字典對象,包括復制組對象,復制對象,復制方案對象等等。假如搭建的這個高級復制環境包含很多節點,每個節點又包含幾個復制方案(schema)以及每個方案又包含多個復制對象,則完成一遍檢查需要作很多的重復工作,這里針對這個問題,編寫了一個復制設置問題診斷包,只有運行該包中相應的過程,即可完成對上面提到的相關對象的診斷,給出相應診斷結果。運行方法是,在SQL/PLUS環境下,SQL> spool <文件名>
SQL> set serveroutput on
SQL> exec rep_diag.rep_diag; 這里強調一點,運行該包的用戶,必須具有對系統字典表dba_repschema、dba_diagnose、dba_repcat以及dba_repcatlog的檢索(select)權限,當然,復制治理員(RepAdmin)用戶是均有這些權限的。附:高級復制環境設置問題診斷包腳本。下載該腳本CREATE OR REPLACE PACKAGE REP_DIAG IS
PROCEDURE REP_DIAG;
PROCEDURE REP_SCHEMA;
PROCEDURE REP_OBJECT;
PROCEDURE REP_ERROR;
PROCEDURE REP_STAT;
END REP_DIAG;
/CREATE OR REPLACE PACKAGE BODY REP_DIAG IS
PROCEDURE REP_DIAG IS
BEGIN
REP_SCHEMA;
REP_OBJECT;
REP_ERROR;
REP_STAT;
END REP_DIAG;PROCEDURE REP_SCHEMA AS
CURSOR C_SCHEMA IS SELECT SNAME, DBLINK, MASTERDEF
FROM SYS.DBA_REPSCHEMA;
BEGIN
DBMS_OUTPUT.PUT_LINE('復制方案明細信息');
DBMS_OUTPUT.PUT_LINE('-------------------------');
FOR T_SCHEMA IN C_SCHEMA LOOP
DBMS_OUTPUT.PUT_LINE('方案名稱: 'T_SCHEMA.SNAME);
DBMS_OUTPUT.PUT_LINE('是否為主定義節點: 'T_SCHEMA.MASTERDEF);
DBMS_OUTPUT.PUT_LINE('數據庫聯接名稱: 'T_SCHEMA.DBLINK);
DBMS_OUTPUT.PUT_LINE('.');
END LOOP;
END REP_SCHEMA;
PROCEDURE REP_OBJECT AS
CURSOR C_REP_OBJECT IS SELECT SNAME, ONAME, TYPE, STATUS
FROM SYS.DBA_REPOBJECT;
BEGIN
DBMS_OUTPUT.PUT_LINE(' 復制對象 ');
DBMS_OUTPUT.PUT_LINE('----------------------------------');
FOR T_REP_OBJECT IN C_REP_OBJECT LOOP
DBMS_OUTPUT.PUT_LINE('.');
DBMS_OUTPUT.PUT_LINE('屬主: 'T_REP_OBJECT.SNAME);
DBMS_OUTPUT.PUT_LINE('對象名稱: 'T_REP_OBJECT.ONAME);
DBMS_OUTPUT.PUT_LINE('對象類型: 'T_REP_OBJECT.TYPE);
DBMS_OUTPUT.PUT_LINE('狀態: 'T_REP_OBJECT.STATUS);
DBMS_OUTPUT.PUT_LINE('.');
END LOOP;
END REP_OBJECT;PROCEDURE REP_ERROR IS
CURSOR C_REP_ERROR IS SELECT REQUEST, STATUS, MESSAGE, ERRNUM
FROM SYS.DBA_REPCATLOG;
BEGIN
DBMS_OUTPUT.PUT_LINE('復制目錄錯誤信息');
DBMS_OUTPUT.PUT_LINE('---------------');
FOR T_REP_ERROR IN C_REP_ERROR LOOP
DBMS_OUTPUT.PUT_LINE('.');
DBMS_OUTPUT.PUT_LINE('請求: 'T_REP_ERROR.REQUEST);
DBMS_OUTPUT.PUT_LINE('狀態: 'T_REP_ERROR.STATUS);
DBMS_OUTPUT.PUT_LINE('信息: 'T_REP_ERROR.MESSAGE);
DBMS_OUTPUT.PUT_LINE('錯誤: 'T_REP_ERROR.ERRNUM);
DBMS_OUTPUT.PUT_LINE('.');
END LOOP;
END REP_ERROR;
PROCEDURE REP_STAT IS
CURSOR C_REP_STAT IS SELECT SNAME, MASTER, STATUS
FROM SYS.DBA_REPCAT;
BEGIN
DBMS_OUTPUT.PUT_LINE('復制狀態');
DBMS_OUTPUT.PUT_LINE('------------------');
FOR T_REP_STAT IN C_REP_STAT LOOP
DBMS_OUTPUT.PUT_LINE('.');
DBMS_OUTPUT.PUT_LINE('方案: 'T_REP_STAT.SNAME);
DBMS_OUTPUT.PUT_LINE('是否主節點?:'T_REP_STAT.MASTER);
DBMS_OUTPUT.PUT_LINE('狀態: 'T_REP_STAT.STATUS);
DBMS_OUTPUT.PUT_LINE('.');
END LOOP;
END REP_STAT;
END REP_DIAG;
/ 三、列出一個延遲事務的所有調用作為一個復制治理員,我們經常需要查看某個延遲事務中到底包含那些調用,而這些調用的參數又是什么。Oracle復制包中沒有提供相應的腳本來實現該功能,通常我們的做法只能是借助于Oracle的復制治理器來查看,但是假如延遲事務很多,且沒有延遲事務的調用個數也很多的話,Oracle復制治理器非常的慢,而且最重要的是我們根本無法直接操作這些數據。下面這個腳本可以列出延遲隊列中的某個事務的所有調用內容,假如再對這個腳本加以改造的話,甚至可以恢復出延遲事務中的Oracle DDL語句。這對于Oracle復制治理員是非常有用的功能。在對復制環境的治理中,還經常作這樣一個工作,假如復制發生錯誤,將會將錯誤信息寫入錯誤隊列中(deferror視圖),系統會顯示出在一個延遲事務中錯誤的調用號,也可以將下面的程序加以改造,讓其直接輸出某個事務的某個調用。由于在很多情況下,一個事務通常含有很多的調用,將所有的都顯示出來沒有必要,其實我們更關心其中的某個調用。該存儲過程這里就不詳述,其實根據下面的這個過程改造是非常輕易的。有感愛好的也可以和我聯系。存儲過程 p_list_calls可以列出一個延遲事務中的所有調用的參數類型和值,支持所有的復制類型,包括NCHAR, NVARCHAR和所有的LOB.運行方法和前面談到的存儲過程一樣,首先需要將輸出定位到屏幕,set serveroutput on size 200000 其中參數存儲過程的輸入參數T為延遲事務的ID號,可以通過視圖deferror或者defcall得到,下面是一個典型的調用過程例子:SQL> select * from deftran;
DEFERRED_TRAN_ID DELIVERY_ORDER D START_TIME
------------------------------ -------------- - ----------
7.0.3741 65040962 R 25-7月 -01
8.41.3747 65040963 R 25-7月 -01
6.18.3739 65040974 R 25-7月 -01
8.39.3746 65040843 R 25-7月 -01
SQL> set serveroutput on size 1000000
SQL> execute p_list_calls('7.0.3741');
調用順序: 0
操作: DB_ZGXT.PA_REP_JB.P_REP_DJ_NSRXX_U
參數個數: 12
參數 數據類型 值
-------------------- -------------- ----------------------
01 N_NSRNM VARCHAR2 034530001
02 N_PZWH VARCHAR2 (NULL)
03 N_TBRQ DATE (NULL)
04 N_BGRQ DATE 2000-12-28 00:00:00
05 N_JBR VARCHAR2 (NULL)
06 N_FZR VARCHAR2 (NULL)
07 N_SWJGYJ VARCHAR2 (NULL)
08 N_BZ VARCHAR2 (NULL)
09 N_RYDM VARCHAR2 030811
10 N_BGLRRQ DATE 2000-12-28 14:57:01
11 N_ZHWZBM VARCHAR2 13302030000270999999
12 N_KZBZ CHAR 1
PL/SQL 過程已成功完成。
附:存儲過程代碼。下載該腳本CREATE OR REPLACE PROCEDURE P_LIST_CALLS (T IN VARCHAR2) IS
ARGNO NUMBER;
ARGTYP NUMBER;
ARGFORM NUMBER;
CALLNO NUMBER;
TRANID VARCHAR2(30);
TYPDSC CHAR(15);
ROWID_VAL ROWID;
CHAR_VAL VARCHAR2(255);
NCHAR_VAL NVARCHAR2(255);
DATE_VAL DATE;
NUMBER_VAL NUMBER;
VARCHAR2_VAL VARCHAR2(2000);
NVARCHAR2_VAL NVARCHAR2(2000);
RAW_VAL RAW(255);
ARG_NAME VARCHAR2(20);
ARG_NAME_C CHAR(20);
TABLE_NAME VARCHAR2(100);
COL_NAME VARCHAR2(100);
PK_CHAR CHAR(1);-- 延遲隊列光標
CURSOR C_DEFCALL (T VARCHAR2) IS
SELECT CALLNO, DEFERRED_TRAN_ID, SCHEMANAME, PACKAGENAME, PROCNAME,ARGCOUNT
FROM DEFCALL
WHERE DEFERRED_TRAN_ID = T;-- 獲得參數名稱
CURSOR C_ARG_NAME (P_SCHEMA VARCHAR2, P_PROCNAME VARCHAR2,
P_PKGNAME VARCHAR2, P_CALL_COUNT VARCHAR2) IS
SELECT ARGUMENT_NAME
FROM ALL_ARGUMENTS
WHERE OWNER = P_SCHEMA
AND PACKAGE_NAME = P_PKGNAME
AND OBJECT_NAME = P_PROCNAME
AND (OVERLOAD = (SELECT OVRLD.OVERLOAD FROM
(SELECT OVERLOAD, OBJECT_NAME, PACKAGE_NAME, MAX(POSITION) POS
FROM ALL_ARGUMENTS
WHERE OBJECT_NAME = P_PROCNAME
AND PACKAGE_NAME = P_PKGNAME
GROUP BY OVERLOAD, OBJECT_NAME, PACKAGE_NAME
) OVRLD
WHERE P_CALL_COUNT = OVRLD.POS
AND OBJECT_NAME = P_PROCNAME
AND PACKAGE_NAME = P_PKGNAME
)
OR OVERLOAD IS NULL
)
ORDER BY POSITION;
-- 該光標用來獲得某個列是否為該表的主鍵
CURSOR PK_CURSOR (SCHEMA VARCHAR2, T_NAME VARCHAR2, COL_NAME VARCHAR2) IS
SELECT DECODE (COUNT(*),1,'*',' ')
FROM DBA_CONSTRAINTS T1, DBA_CONS_COLUMNS T2
WHERE T1.CONSTRAINT_NAME = T2.CONSTRAINT_NAME
AND T1.OWNER = T2.OWNER
AND T1.OWNER = SCHEMA
AND T1.CONSTRAINT_TYPE = 'P'
AND T1.TABLE_NAME = T_NAME
AND T2.COLUMN_NAME LIKE COL_NAME;BEGINFOR C1REC IN C_DEFCALL (T) LOOP
DBMS_OUTPUT.PUT_LINE('調用順序: ' C1REC.CALLNO);
DBMS_OUTPUT.PUT_LINE('操作: 'C1REC.SCHEMANAME'.'C1REC.PACKAGENAME'.'C1REC.PROCNAME);
DBMS_OUTPUT.PUT_LINE('參數個數: 'C1REC.ARGCOUNT);
DBMS_OUTPUT.PUT_LINE(' 參數 ' ' 數據類型 ' '值');
DBMS_OUTPUT.PUT_LINE(' ---------------- ' '---------------- ' '----------------------');
ARGNO := 1;
CALLNO := C1REC.CALLNO;
TRANID := C1REC.DEFERRED_TRAN_ID;
OPEN C_ARG_NAME (C1REC.SCHEMANAME, C1REC.PROCNAME, C1REC.PACKAGENAME,C1REC.ARGCOUNT);
WHILE TRUE LOOP
IF (ARGNO > C1REC.ARGCOUNT) THEN
CLOSE C_ARG_NAME;
EXIT;
END IF;
ARGTYP := DBMS_DEFER_QUERY.GET_ARG_TYPE(CALLNO, ARGNO, TRANID);
ARGFORM := DBMS_DEFER_QUERY.GET_ARG_FORM(CALLNO, ARGNO, TRANID);
FETCH C_ARG_NAME INTO ARG_NAME;
ARG_NAME_C := ARG_NAME;
TABLE_NAME := SUBSTR(C1REC.PACKAGENAME, 1, INSTR(C1REC.PACKAGENAME, '$') - 1);
COL_NAME := SUBSTR(ARG_NAME, 1, LENGTH(ARG_NAME) - 5) '%';
OPEN PK_CURSOR (C1REC.SCHEMANAME, TABLE_NAME, COL_NAME);
FETCH PK_CURSOR INTO PK_CHAR;
CLOSE PK_CURSOR;
IF (ARGTYP = 1 AND ARGFORM = 1) THEN
TYPDSC := 'VARCHAR2';
VARCHAR2_VAL := DBMS_DEFER_QUERY.GET_VARCHAR2_ARG(CALLNO, ARGNO,TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(VARCHAR2_VAL,'(NULL)'));
ELSIF ARGTYP = 1 AND ARGFORM = 2 THEN
TYPDSC := 'NVARCHAR2';
NVARCHAR2_VAL := DBMS_DEFER_QUERY.GET_NVARCHAR2_ARG(CALLNO, ARGNO,TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(TRANSLATE(NVARCHAR2_VAL USING CHAR_CS),'(NULL)'));
ELSIF ARGTYP = 2 THEN
TYPDSC := 'NUMBER';
NUMBER_VAL := DBMS_DEFER_QUERY.GET_NUMBER_ARG(CALLNO, ARGNO, TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(TO_CHAR(NUMBER_VAL),'(NULL)'));
ELSIF ARGTYP = 11 THEN
TYPDSC := 'ROWID';
ROWID_VAL := DBMS_DEFER_QUERY.GET_ROWID_ARG(CALLNO, ARGNO, TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(ROWID_VAL,'(NULL)'));
ELSIF ARGTYP = 12 THEN
TYPDSC := 'DATE';
DATE_VAL := DBMS_DEFER_QUERY.GET_DATE_ARG(CALLNO, ARGNO, TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(TO_CHAR(DATE_VAL,'YYYY-MM-DD HH24:MI:SS'),'(NULL)'));
ELSIF ARGTYP = 23 THEN
TYPDSC := 'RAW';
RAW_VAL := DBMS_DEFER_QUERY.GET_RAW_ARG(CALLNO, ARGNO, TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(RAW_VAL,'(NULL)'));
ELSIF ARGTYP = 96 AND ARGFORM = 1 THEN
TYPDSC := 'CHAR';
CHAR_VAL := DBMS_DEFER_QUERY.GET_CHAR_ARG(CALLNO, ARGNO, TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(CHAR_VAL,'(NULL)')'');
ELSIF ARGTYP = 96 AND ARGFORM = 2 THEN
TYPDSC := 'NCHAR';
NCHAR_VAL := DBMS_DEFER_QUERY.GET_NCHAR_ARG(CALLNO, ARGNO, TRANID);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(TRANSLATE(NCHAR_VAL USING CHAR_CS),'(NULL)')'');
ELSIF ARGTYP = 113 THEN
TYPDSC := 'BLOB';
VARCHAR2_VAL := DBMS_LOB.SUBSTR(DBMS_DEFER_QUERY.GET_BLOB_ARG(CALLNO,ARGNO, TRANID));
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(VARCHAR2_VAL,'(NULL)'));
ELSIF ARGTYP = 112 AND ARGFORM = 1 THEN
TYPDSC := 'CLOB';
VARCHAR2_VAL := DBMS_LOB.SUBSTR(DBMS_DEFER_QUERY.GET_CLOB_ARG(CALLNO,ARGNO, TRANID));
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(VARCHAR2_VAL,'(NULL)'));
ELSIF ARGTYP = 112 AND ARGFORM = 2 THEN
TYPDSC := 'NCLOB';
NVARCHAR2_VAL := DBMS_LOB.SUBSTR(DBMS_DEFER_QUERY.GET_NCLOB_ARG(CALLNO, ARGNO, TRANID));
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ARGNO,'09')PK_CHARARG_NAME_CTYPDSC' 'NVL(TRANSLATE(NVARCHAR2_VAL USING CHAR_CS),'(NULL)'));
END IF;
ARGNO := ARGNO + 1;
END LOOP;
END LOOP;
END;
/