How to handle the case when select statement don't return any record in SYS_REFCURSOR ?
I am using dynamic sql generation process using bind variables.
create or replace
procedure test_dynamic_sql
(
last_name varchar2,
rc out sys_refcursor,
jobid varchar2,
sdate date,
edate date,
status out varchar2,
message out varchar2 )
is
q long;
lname varchar2(240);
begin
q := 'select employee_id
from employees e
where 1=1';
if last_name is not null then
q := q || 'and (e.LAST_NAME = :LAST_NAME)';
else
q := q || 'and (1=1 or :LAST_NAME is null)';
end if;
if jobid is not null then
q := q || 'and (e.JOB_ID = :JOBID)';
else
q := q || 'and (1=1 or :JOBID is null)';
end if;
if sdate is not null then
q := q || 'and (e.hire_date >= :sdate)';
else
q := q || 'and (1=1 or :sdate is null)';
end if;
if edate is not null then
q := q || 'and (e.hire_date <= :edate)';
else
q := q || 'and (1=1 or :edate is null)';
end if;
open rc for q using last_name, jobid, sdate, edate;
/*
IF rc%NOTFOUND THEN
STATUS := 'NR';
MESSAGE := 'Not Found';
ELSE
STATUS := 'S';
MESSAGE := 'Found';
END IF;
*/
exception
when others then
STATUS :='E';
message := sqlcode||'-->'||sqlerrm;
end;
I have tried %NOTFOUND and %FOUND attributes but its not working.
I have also tried NO_DATA_FOUND exception but its also not working.
I need to return status as 'S', 'E', 'NR'
S --> SUCCESS (when records found)
E --> ERROR (when any error occured)
NR--> NO RECORDS (when 0 records)
Thanks!
This answer is to address the issues you are having while working with Ref Cursors as Out Parameters. The below code calls your test_dynamic_sql() Procedure. Within this Proc we OPEN the cursor, FETCH the data it is pointing to and we DO NOT CLOSE the cursor as we are immediately using that Cursor again outside of the test_dynamic_sql() Procedure. One thing to NOTE - When FETCH is used that Cursor will no longer provide you with the data and must be opened again. Since your Cursor is utilizing Dynamic SQL we must Declare our Dynamic 'Query' in the same place we are declaring the rest of our Global Variables.
"Cursors are NOT designed to be re-used: you read them once, keep moving forward and as you're doing so you're discarding any previously scanned rows." This fact was stolen from this SO Post: Oracle. Reuse cursor as parameter in two procedures.
Outside of this procedure we first must check if the Cursor was successfully initialized by using an IF statement to check if the Cursor exists: IF (g_rc IS NOT NULL) THEN.
Full Code Sample below:
DECLARE
/* g for Global */
g_status VARCHAR2(5);
g_message VARCHAR2(100);
g_rc SYS_REFCURSOR;
/* Store Dynamic SQL Query */
g_SQL VARCHAR2(200);
/* Bind Variables */
g_jobid NUMBER;
g_last_name VARCHAR2(240);
/* Declare Global Record used to FETCH data into */
g_rec_employee employees%ROWTYPE;
PROCEDURE test_dynamic_sql(pv_last_name VARCHAR2,
p_rc OUT SYS_REFCURSOR,
pv_jobid VARCHAR2,
pv_status OUT VARCHAR2,
pv_message OUT VARCHAR2,
pv_q OUT VARCHAR2)
AS
/* Declare Record used to FETCH data into */
rec_employee employees%ROWTYPE;
/* Bind Variables */
jobid NUMBER := to_number(pv_jobid);
last_name VARCHAR2(240) := pv_last_name;
BEGIN
/* Reset/Initialize Cursor Output Variable */
p_rc := NULL;
/* Dynamic SQL statement with placeholder: */
pv_q := 'SELECT * FROM employees WHERE 1=1';
IF last_name IS NOT NULL
THEN pv_q := pv_q || ' AND (lastname = :LAST_NAME)';
ELSE pv_q := pv_q || ' AND (1=1 or :LAST_NAME is null)';
END IF;
IF jobid IS NOT NULL
THEN pv_q := pv_q || ' AND (ID = :JOBID)';
ELSE pv_q := pv_q || ' AND (1=1 or :JOBID is null)';
END IF;
/* Open cursor & specify bind argument in USING clause: */
OPEN p_rc FOR pv_q USING last_name, jobid;
LOOP
/* In order to work with any Data that a Cursor 'points' to we must FETCH the data. This allows us to use %ROWCOUNT and %NOTFOUND */
FETCH p_rc INTO rec_employee;
EXIT WHEN p_rc%NOTFOUND;
END LOOP;
IF p_rc%ROWCOUNT = 0 THEN
pv_status := 'NR';
pv_message := 'Not Found';
--EXIT;
ELSIF p_rc%ROWCOUNT = 1 THEN
pv_status := 'S';
pv_message := 'Found';
--EXIT;
ELSE
pv_status := 'MU';
pv_message := 'Multiple Records';
END IF;
--dbms_output.put_line('Final Count: ' || p_rc%ROWCOUNT);
/* Close Cursor - We don't close the Cursor here as we want to use this cursor as an OUT Parameter outside of this Proc */
CLOSE p_rc;
EXCEPTION
WHEN OTHERS THEN
pv_status :='E';
pv_message := sqlcode||'-->'||sqlerrm;
dbms_output.put_line('STATUS: ' || pv_status);
dbms_output.put_line('MESSAGE: ' || pv_message);
CLOSE p_rc;
END test_dynamic_sql;
BEGIN
g_jobid := null;
g_last_name := 'Loch';
test_dynamic_sql(pv_last_name => g_last_name,
p_rc => g_rc, /* Out Parameter */
pv_jobid => g_jobid,
pv_status => g_status, /* Out Parameter */
pv_message => g_message, /* Out Parameter */
pv_q => g_SQL /* Out Parameter */
);
/* Output OUT Variables aka Provide Output to User */
dbms_output.put_line('STATUS: ' || g_status);
dbms_output.put_line('MESSAGE: ' || g_message);
IF (g_rc IS NOT NULL) THEN
dbms_output.put_line('We have something here. Fetching Data Now:');
OPEN g_rc FOR g_sql USING g_last_name, g_jobid;
LOOP
FETCH g_rc INTO g_rec_employee;
EXIT WHEN g_rc%NOTFOUND;
/* Print the Job ID just to show it is working */
dbms_output.put_line('Job_ID: ' || g_rec_employee.id || ' is the id');
END LOOP;
dbms_output.put_line('Total of: '|| g_rc%ROWCOUNT || ' records Fetched.');
CLOSE g_rc;
ELSE
dbms_output.put_line('null');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM);
CLOSE g_rc;
END;
The above works with the same Employees table data as in my first Answer to this SO Question.
There are a few things you are missing if you wish to work with a cursor in the manner you are attempting regardless of the implicit Ref Cursor due to the Dynamic SQL or an explicit Cursor.
In order to make use of %ROWCOUNT or %NOTFOUND you must first FETCH the Cursor. This Link "PL/SQL 101 : Understanding Ref Cursors" that provides a ton of information on this topic but all that is needed to help with answering your question is to know that you must FETCH the data first.
Below is an image showing the data in my Employees table. NOTE that there are two employees with the last name 'Loch'.
The below Code is all within it's own anonymous block but can easily be converted into a Procedure/Function. It has all of your required Status's and Messages. In order to handle searches that have more than one result I added an additional Status/Message to tell the user that Multiple Records were returned. Last, just to make it easier to work with your code I took out all but two of your Parameters. NOTE: If the Procedure's Parameters are all passed in as NULL the Dynamic SQL That is generated will query the entire table as it basically removes all of the Filters in the WHERE clause.
DECLARE
/* Parameters */
rc SYS_REFCURSOR;
q VARCHAR2(200);
status VARCHAR2(5);
message VARCHAR2(100);
/* Declare Record used to FETCH data into */
rec_employee employees%ROWTYPE;
/* Bind Variables */
jobid NUMBER := null;
last_name VARCHAR2(240) := 'Loch';
BEGIN
/* Dynamic SQL statement with placeholder: */
q := 'SELECT * FROM employees WHERE 1=1';
IF last_name IS NOT NULL
THEN q := q || ' AND (lastname = :LAST_NAME)';
ELSE q := q || ' AND (1=1 or :LAST_NAME is null)';
END IF;
IF jobid IS NOT NULL
THEN q := q || ' AND (ID = :JOBID)';
ELSE q := q || ' AND (1=1 or :JOBID is null)';
END IF;
/* Open cursor & specify bind argument in USING clause: */
OPEN rc FOR q USING last_name, jobid;
LOOP
/* In order to work with any Data that a Cursor 'points' to we must FETCH the data. This allows us to use %ROWCOUNT and %NOTFOUND */
FETCH rc INTO rec_employee;
EXIT WHEN rc%NOTFOUND;
END LOOP;
IF rc%ROWCOUNT = 0 THEN
STATUS := 'NR';
MESSAGE := 'Not Found';
--EXIT;
ELSIF rc%ROWCOUNT = 1 THEN
STATUS := 'S';
MESSAGE := 'Found';
--EXIT;
ELSE
STATUS := 'MU';
MESSAGE := 'Multiple Records';
END IF;
dbms_output.put_line('Final Count: ' || rc%ROWCOUNT);
/* Close Cursor */
CLOSE rc;
/* Return Variables or Provide Output to User */
dbms_output.put_line('STATUS: ' || STATUS);
dbms_output.put_line('MESSAGE: ' || MESSAGE);
EXCEPTION
WHEN OTHERS THEN
STATUS :='E';
message := sqlcode||'-->'||sqlerrm;
dbms_output.put_line('STATUS: ' || STATUS);
dbms_output.put_line('MESSAGE: ' || MESSAGE);
END;
Oracle Function Giving Me Error while i execute this function . it will give an error like character buffer value is too small at line no 20
CREATE OR REPLACE FUNCTION Fn_Lov_Purchase_Return (vComp_Id NUMBER,
vLoc_Id NUMBER,
vLIne_No NUMBER,
vParam NUMBER,
vAtt_Para NUMBER,
vLot_No VARCHAR2,
vDept_Id NUMBER)
RETURN VARCHAR2
AS
vPURC_NO VARCHAR2 (20000);
CURSOR c1
IS
SELECT M.DOC_NO AS PURC_NO
FROM RJN_GRN_TRANS_ITEMS I, RJN_GRN_TRANS_MASTER M
WHERE I.TRANS_ID = M.TRANS_ID
AND M.TRANS_TYPE = 'GRN'
AND M.COMP_ID = vComp_Id
AND M.COMP_ID = vLoc_Id
AND I.LINE_NO = vLIne_No
AND I.PARAMETER_CODE = vParam
AND I.ATT_PARAMETER_CODE = vAtt_Para
--AND I.LOT_NO= vLot_No
AND M.DEPT_ID = VDept_Id;
c1_Rec c1%ROWTYPE;
BEGIN
vPURC_NO := ' ';
FOR c1_Rec IN c1
LOOP
IF (vPURC_NO) = ' '
THEN
vPURC_NO := c1_Rec.PURC_NO;
ELSE
vPURC_NO := vPURC_NO || ', ' || c1_rec.PURC_NO;
END IF;
END LOOP;
RETURN (vPURC_NO);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
vPURC_NO := 'No-Records';
RETURN vPURC_NO;
END;
/
If vPURC_NO VARCHAR2 (20000); isn't large enough, either make it larger, or use a CLOB data type.
Here is my procedure code :
create or replace Procedure SP_CUSTOMER_PRODUCT_QRY
( PARENT_PARTYID IN varchar2,PRODUCT_ID IN varchar2,PRODUCT_STATUS IN varchar2,PAGING IN varchar2,OFFSET IN varchar2,LIST_OF_PARTIES OUT VARCHAR2 )
AS
V_SQL_WHERE varchar2(10000):= null;
V_SQL varchar2(10000):='SELECT PARTYID FROM TABLE1';
V_check varchar2(10000) := '' ;
BEGIN
IF(PARENT_PARTYID IS NOT NULL) THEN
V_SQL_WHERE := V_SQL_WHERE || ' PARENT_PARTYID=:1';
END IF;
IF(PRODUCT_ID IS NOT NULL) THEN
IF (V_SQL_WHERE is not null) THEN
V_SQL_WHERE := V_SQL_WHERE || ' AND ';
END IF;
V_SQL_WHERE := V_SQL_WHERE || ' PRODUCT_ID=:2';
END IF;
IF(PRODUCT_STATUS IS NOT NULL) THEN
IF (V_SQL_WHERE is not null) THEN
V_SQL_WHERE := V_SQL_WHERE || ' AND ';
END IF;
V_SQL_WHERE := V_SQL_WHERE || ' PRODUCT_STATUS=:3';
END IF;
IF (V_SQL_WHERE is not null) then
V_SQL := V_SQL || ' WHERE ' || V_SQL_WHERE;
end if;
dbms_output.put_line(V_SQL);
EXECUTE IMMEDIATE V_SQL INTO LIST_OF_PARTIES;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_check:= '' ;
END;
I have used bind variables ,unable to understand the issue.Is it mandatory to bind all parameters even if they are not used in procedure
When I am passing any input I am getting error as
java.sql.SQLException: ORA-01008: not all variables bound
In this case there is no need for using dynamic plsql. Try to do this this way:
CREATE OR REPLACE PROCEDURE SP_CUSTOMER_PRODUCT_QRY
( P_PARENT_PARTYID IN VARCHAR2,
P_PRODUCT_ID IN VARCHAR2,
P_PRODUCT_STATUS IN VARCHAR2,
P_PAGING IN VARCHAR2,
P_OFFSET IN VARCHAR2,
P_LIST_OF_PARTIES OUT VARCHAR2 )
AS
CURSOR CUR_PARTYID
IS
SELECT PARTYID
FROM TABLE1
WHERE (P_PARENT_PARTYID IS NULL
OR PARENT_PARTYID = P_PARENT_PARTYID)
AND (P_PRODUCT_ID IS NULL
OR PRODUCT_ID = P_PRODUCT_ID)
AND (P_PRODUCT_STATUS IS NULL
OR PRODUCT_STATUS = P_PRODUCT_STATUS);
BEGIN
OPEN CUR_PARTYID;
FETCH CUR_PARTYID INTO P_LIST_OF_PARTIES;
CLOSE CUR_PARTYID;
END;
/
You need the "using" clause with your execute immediate for the 3 variables in the where clause.
for example:
EXECUTE IMMEDIATE 'DELETE FROM dept WHERE deptno = :num' USING dept_id;
For some reason, I have certain fields in a table, that are collections of chars. Chars of different length. Example:
create or replace type t_charray_1 as varray(5) of char(1);
create or replace type t_charray_2 as varray(5) of char(2);
create or replace type t_charray_3 as varray(5) of char(3);
create or replace type t_charray_4 as varray(5) of char(4);
create table mytable (
field1 number,
field2 t_charray_1,
field3 t_charray_3,
Also, I have a function that returns a (fixed length) string representation of a mytable record. This function calls other functions that are returning the string representation of a given collection-typed field. Examples:
function to_chr(
p_array in t_charray_1,
pad_length in number,
p_list_length in number
) return char as
v_res varchar2(255) := '';
begin
for i in 1 .. p_list_length loop
if p_array is not null and p_array.exists(i) and p_array(i) is not null then
v_res := v_res || rpad(p_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
------------------------------------------------------------------------------
function to_chr(
p_array in t_charray_2,
pad_length in number,
p_list_length in number
) return char as
v_res varchar2(255) := '';
begin
for i in 1 .. p_list_length loop
if p_array is not null and p_array.exists(i) and p_array(i) is not null then
v_res := v_res || rpad(p_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
Note that these functions are overloaded versions of each other. The only difference in their signature is the type of the p_array argument.
Please also note that the bodies of these functions are identical.
Motivation
I want to eliminate duplicate code. What are my choices?
EDIT I have heard of sys.anydata but never used it. Can it be a solution?
You could write a procedure that takes the largest type, and explicitly CAST the smaller types to the larger type before passing them. Note that CAST can only be used in SQL
DECLARE
x t_charray_1 := t_charray_1();
y t_charray_2 := t_charray_2();
PROCEDURE foo( p_foo t_charray_2 )
AS
BEGIN
FOR i IN p_foo.FIRST..p_foo.LAST loop
dbms_output.put_line( p_foo(i) );
END LOOP;
END;
BEGIN
x.EXTEND;
x.EXTEND;
x(1) := 'A';
x(2) := 'B';
y.EXTEND;
y.EXTEND;
y(1) := 'AA';
y(2) := 'BB';
foo(y);
SELECT CAST(x AS t_charray_2) INTO y FROM dual;
foo(y);
END;
/
Try:
create or replace function to_chr(p_array in anydata,
pad_length in number,
p_list_length in number) return char as
v_res varchar2(255) := '';
x number;
v_array t_charray_4;
v_array1 t_charray_1;
v_array2 t_charray_2;
v_array3 t_charray_3;
begin
dbms_output.put_line(p_array.GetTypeName);
case p_array.GetTypeName
when '<schema>.T_CHARRAY_1' then
x := p_array.GetCollection(v_array1);
select cast(v_array1 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_2' then
x := p_array.GetCollection(v_array2);
select cast(v_array2 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_3' then
x := p_array.GetCollection(v_array3);
select cast(v_array3 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_4' then
x := p_array.GetCollection(v_array);
end case;
for i in 1 .. p_list_length loop
if v_array is not null and v_array.exists(i) and v_array(i) is not null then
v_res := v_res || rpad(v_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
you can run it like this:
declare
p_array anydata;
v_array t_charray_3 := new t_charray_3('aaa', 'bbb');
v_res varchar2(255);
begin
p_array := anydata.convertcollection(v_array);
v_res := to_chr(p_array => p_array, pad_length => 2, p_list_length => 3);
end;
I am trying to concatenate clobs in a PL/SQL loop and it has been returning null whilst when using DBMS_OUTPUT prints out the loop values and when executing each result of the clobs gives an output as well.
The system is meant to execute an already stored SQL in a table based on the report name passed into it. This particular report has many report names; hence the concatenation of each of the reports. The arguments passed are the report name, version of the report you're interested in, the kind of separator you want, and an argument list for the unknowns in the SQL if any. There are also two main types of SQL; 1 that needs the table_name be replaced with a temp table_name and another that needs an ID be appended to a table_name in the SQL.
please find below the code for the REPREF1 function.
CREATE OR REPLACE FUNCTION REPREF1(P_VER IN VARCHAR2 DEFAULT 'LATEST',
P_SEPARATOR IN VARCHAR2 DEFAULT ', ',
P_ARGLIST IN VAR DEFAULT NULL) RETURN CLOB IS
L_CLOB CLOB;
FUNCTION GET_CLOB(P_REPNAM IN VARCHAR2,
P_VER IN VARCHAR2 DEFAULT 'LATEST',
P_SEPARATOR IN VARCHAR2 DEFAULT ', ',
P_ARGLIST IN VAR DEFAULT NULL) RETURN CLOB IS
---------------------------------------------------------------------------------
-- TITLE - GET_CLOB beta - b.0 DATE 2010Mar12
--
-- DESCRIPTION - A function that return a report based on the report name put in
--
-- USAGE - select get_clob(p_repnam,p_ver, p_separator, var(varay(val_1,...val_n), varay(val_1,...val_n))) FROM dual
-----------------------------------------------------------------------------------------------------------------------------
V_SQL VARCHAR2(32767);
L_RESULT CLOB;
V_TITLE VARCHAR2(4000);
V_REPDATE VARCHAR2(30);
V_CNT NUMBER(2);
V_NUMARG NUMBER(3);
V_CDCRU NUMBER(3);
V_BCNT NUMBER(3);
V_NEWTABDAT VARCHAR2(30);
V_NEWTABLIN VARCHAR2(30);
L_COLLIST VARAY;
V_VER VARCHAR2(6);
N PLS_INTEGER;
V_CNTTAB NUMBER(3);
-- EXEC_SQL_CLOB
FUNCTION EXEC_SQL_CLOB(P_SQL IN VARCHAR2,
P_NUMARG IN NUMBER,
P_COLLIST IN VARAY DEFAULT NULL,
P_ARGLIST IN VARAY DEFAULT NULL,
P_SEPARATOR IN VARCHAR2 DEFAULT '') RETURN CLOB IS
------------------------------------------------------------------------------------------------------
-- TITLE - EXEC_SQL_CLOB beta - b.0 DATE 2010Mar22
--
-- DESCRIPTION - A function that returns a clob value after executing the sql query that is passed into it
--
-- USAGE - select exec_sql_clob(p_sql, p_numarg, var(varay(val_1, val_2,...val_n), varay(val_1, val_2,...val_n))) FROM dual
---------------------------------------------------------------------------------------------------------------
L_CUR INTEGER DEFAULT DBMS_SQL.OPEN_CURSOR;
L_STATUS INTEGER;
V_COL VARCHAR2(4000);
L_RESULT CLOB;
L_COLCNT NUMBER DEFAULT 0;
L_SEPARATOR VARCHAR2(10) DEFAULT '';
V_NUMARG NUMBER(3);
BEGIN
-- parse the query for the report
DBMS_SQL.PARSE(L_CUR, P_SQL, DBMS_SQL.NATIVE);
-- whilst it is not more than 255 per line
FOR I IN 1 .. 255
LOOP
BEGIN
-- define each column in the select list
DBMS_SQL.DEFINE_COLUMN(L_CUR, I, V_COL, 2000);
L_COLCNT := I;
EXCEPTION
WHEN OTHERS THEN
IF (SQLCODE = -1007) THEN
EXIT;
ELSE
RAISE;
END IF;
END;
END LOOP;
-- If query has no bind variables
IF (P_ARGLIST IS NULL) THEN
IF (P_NUMARG = 0) THEN
-- Execute the query in the cursor
L_STATUS := DBMS_SQL.EXECUTE(L_CUR);
LOOP
-- Exit loop when fetch is complete
EXIT WHEN(DBMS_SQL.FETCH_ROWS(L_CUR) <= 0);
L_SEPARATOR := '';
FOR I IN 1 .. L_COLCNT
LOOP
DBMS_SQL.COLUMN_VALUE(L_CUR, I, V_COL);
L_RESULT := L_RESULT || L_SEPARATOR || V_COL;
L_RESULT := REPLACE(REPLACE(L_RESULT, CHR(13) || CHR(10), ' '), CHR(10), ' ');
L_SEPARATOR := P_SEPARATOR;
END LOOP;
L_RESULT := L_RESULT || CHR(13);
END LOOP;
ELSE
RAISE_APPLICATION_ERROR(-20011, ' INCORRECT NUMBER OF ARGUMENTS PASSED IN LIST ');
END IF;
-- Query has bind variables
ELSE
-- Check if the numarg passed is the same has stored in the table
SELECT NUMARG
INTO V_NUMARG
FROM REPVER
WHERE REPCODE = P_SQL;
-- If number of arguments is greater than 0
IF (V_NUMARG > 0) THEN
-- Check if the number of arguments are the same
IF (P_NUMARG = V_NUMARG) THEN
-- Replace the bind variables in the query
FOR J IN 1 .. P_ARGLIST.COUNT
LOOP
DBMS_SQL.BIND_VARIABLE(L_CUR, P_COLLIST(J), P_ARGLIST(J));
END LOOP;
-- Execute the query in the cursor
L_STATUS := DBMS_SQL.EXECUTE(L_CUR);
LOOP
-- Exit loop when fetch is complete
EXIT WHEN(DBMS_SQL.FETCH_ROWS(L_CUR) <= 0);
L_SEPARATOR := '';
FOR I IN 1 .. L_COLCNT
LOOP
DBMS_SQL.COLUMN_VALUE(L_CUR, I, V_COL);
L_RESULT := L_RESULT || L_SEPARATOR || V_COL;
L_RESULT := REPLACE(REPLACE(L_RESULT, CHR(13) || CHR(10), ' '), CHR(10), ' ');
L_SEPARATOR := P_SEPARATOR;
END LOOP;
L_RESULT := L_RESULT || CHR(13);
END LOOP;
ELSE
RAISE_APPLICATION_ERROR(-20011, ' INCORRECT NUMBER OF ARGUMENTS PASSED IN LIST ');
END IF;
ELSE
-- If the number of argument is equal to 0
IF (P_NUMARG = 0) THEN
-- Execute the query in the cursor
L_STATUS := DBMS_SQL.EXECUTE(L_CUR);
LOOP
-- Exit loop when fetch is complete
EXIT WHEN(DBMS_SQL.FETCH_ROWS(L_CUR) <= 0);
L_SEPARATOR := '';
FOR I IN 1 .. L_COLCNT
LOOP
DBMS_SQL.COLUMN_VALUE(L_CUR, I, V_COL);
L_RESULT := L_RESULT || L_SEPARATOR || V_COL;
L_RESULT := REPLACE(REPLACE(L_RESULT, CHR(13) || CHR(10), ' '), CHR(10), ' ');
L_SEPARATOR := P_SEPARATOR;
END LOOP;
L_RESULT := L_RESULT || CHR(13);
END LOOP;
ELSE
RAISE_APPLICATION_ERROR(-20011, ' INCORRECT NUMBER OF ARGUMENTS PASSED IN LIST ');
END IF;
END IF;
END IF;
-- Close cursor
DBMS_SQL.CLOSE_CURSOR(L_CUR);
RETURN L_RESULT;
END EXEC_SQL_CLOB;
BEGIN
-- Check if the version entered is null or latest
IF (P_VER IS NULL)
OR (UPPER(P_VER) = UPPER('LATEST')) THEN
SELECT MAX(VER)
INTO V_VER
FROM REPORT B, REPVER R
WHERE UPPER(REPNAM) = UPPER(P_REPNAM)
AND B.REPREF = R.REPREF;
ELSE
V_VER := P_VER;
END IF;
-- Check if the repname and version entered exists
SELECT COUNT(*)
INTO V_CNT
FROM REPORT B, REPVER R
WHERE UPPER(REPNAM) = UPPER(P_REPNAM)
AND VER = V_VER
AND B.REPREF = R.REPREF;
IF (V_CNT > 0) THEN
-- Store the SQL statement, title and number of arguments of the report name passed.
SELECT REPCODE, REPTITLE, NUMARG, COLLIST
INTO V_SQL, V_TITLE, V_NUMARG, L_COLLIST
FROM REPVER R, REPORT B
WHERE UPPER(REPNAM) = UPPER(P_REPNAM)
AND B.REPREF = R.REPREF
AND VER = V_VER;
V_REPDATE := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI');
L_RESULT := V_TITLE || ' (' || P_REPNAM || ' version ' || V_VER || ') generated ' || V_REPDATE || CHR(13) || CHR(13);
-- Check for some specific type of queries
SELECT COUNT(*)
INTO V_CDCRU
FROM REPVER R, REPORT B
WHERE CTDDATA = 'Y'
AND UPPER(REPNAM) = UPPER(P_REPNAM)
AND B.REPREF = R.REPREF
AND VER = V_VER;
SELECT COUNT(*)
INTO V_BCNT
FROM REPVER R, BODCREPS B
WHERE BENLIST = 'Y'
AND UPPER(REPNAM) = UPPER(P_REPNAM)
AND B.REPREF = R.REPREF
AND VER = V_VER;
IF (V_CDCRU > 0) THEN
V_NEWTABDATA := 'CT_' || 'DAT_' || P_ARGLIST(1) (P_ARGLIST(1).FIRST);
V_NEWTABLINK := 'CT_' || 'LIN_' || P_ARGLIST(1) (P_ARGLIST(1).FIRST);
-- Check if the tables exist
SELECT COUNT(*)
INTO V_CNTTAB
FROM ALL_TABLES
WHERE TABLE_NAME = V_NEWTABDAT
OR TABLE_NAME = V_NEWTABLIN
AND OWNER = 'SCOTT';
IF (V_CNTTAB > 0) THEN
V_SQL := UPPER(V_SQL);
V_SQL := REPLACE(V_SQL, 'CT_DAT_CRU', V_NEWTABDAT);
V_SQL := REPLACE(V_SQL, 'CT_LIN_CRU', V_NEWTABLIN);
ELSE
V_SQL := 'SELECT ''THE TABLE NOT CREATED YET''
FROM DUAL';
END IF;
END IF;
IF (V_BCNT > 0) THEN
V_SQL := UPPER(V_SQL);
V_SQL := REPLACE(V_SQL, 'LIST', P_ARGLIST(1) (P_ARGLIST(1).LAST));
END IF;
IF (P_ARGLIST IS NULL) THEN
-- execute the query
L_RESULT := L_RESULT || EXEC_SQL_CLOB(V_SQL, V_NUMARG, L_COLLIST, NULL, P_SEPARATOR);
ELSE
N := P_ARGLIST.COUNT;
-- execute the query
L_RESULT := L_RESULT || EXEC_SQL_CLOB(V_SQL, V_NUMARG, L_COLLIST, P_ARGLIST(N), P_SEPARATOR);
END IF;
RETURN L_RESULT;
ELSE
RAISE_APPLICATION_ERROR(-20012, P_REPNAM || ' or ' || P_VER || ' DOES NOT EXIST ');
END IF;
END GET_CLOB;
BEGIN
FOR I IN (SELECT REPNAM
FROM REPORT
WHERE REPREF NOT IN ('R01', 'R02', 'R03', 'R04'))
LOOP
SELECT CONCAT_CLOB(GET_CLOB(I.REPNAM, P_VER, P_SEPARATOR, P_ARGLIST))
INTO L_CLOB
FROM DUAL;
DBMS_OUTPUT.PUT_LINE(I.REPNAM);
-- DBMS_OUTPUT.PUT_LINE (COUNT(i.REPNAM));
END LOOP;
RETURN L_CLOB;
END REPREF1;
/
Cheers,
Tunde
Many thanks APC for making the code look better.
#Robert, the last loop in the code returns null even with the CONCAT_CLOB aggregate function that concatenates clobs.
FOR I IN (SELECT REPNAM
FROM REPORT
WHERE REPREF NOT IN ('R01', 'R02', 'R03', 'R04'))
LOOP
SELECT CONCAT_CLOB(GET_CLOB(I.REPNAM, P_VER, P_SEPARATOR, P_ARGLIST))
INTO L_CLOB
FROM DUAL;
DBMS_OUTPUT.PUT_LINE(I.REPNAM);
END LOOP;
when I try this,
FOR I IN (SELECT REPNAM
FROM REPORT
WHERE REPREF NOT IN ('R01', 'R02', 'R03', 'R04'))
LOOP
L_CLOB := L_CLOB || CHR(13) || GET_CLOB(I.REPNAM, P_VER, P_SEPARATOR, P_ARGLIST);
DBMS_OUTPUT.PUT_LINE(I.REPNAM);
END LOOP;
It also gives null; but this time the dbms output for the repnam are not complete.
Don't know about your code. Here is how it works for me:
Whenever I create a function returning a clob value I do this:
function foo return clob is
l_clob clob;
begin
dbms_lob.createtemporary(lob_loc => l_clob, cache => true, dur => dbms_lob.call);
...
return l_clob;
end;
When concatenating values into a clob I use a function:
procedure add_string_to_clob(p_lob in out nocopy clob
,p_string varchar2) is
begin
dbms_lob.writeappend(lob_loc => p_lob, amount => length(p_string), buffer => p_string);
end;
You have to use
dbms_lob.substr(your clob parameter,start position, length)
e.g
dbms_output('my clob value:' || dbms_lob.substr(your clob parameter,start position, length);
But you can print in a string max 4000 character, you can then use this in a looping function to print 4000 characters in each line.