Below is the code which has to execute the exception block when no data is found in the main block. However the exception block is not getting executed even when the query doesn't return any record. The query joins with another table and when the result matches with the query it will pick the 1st record else it has to assign null values to the fields and return the cursor to UI. The count below for the cursor is always 0 and the exception block isn't getting executed .
BEGIN
wf_common.write_debug(vprocname, ' 111=', ts);
OPEN cinfo FOR SELECT
name,
phone,
alt_phone,
email
FROM
abc
WHERE
id IN (
SELECT
id
FROM
xyz
WHERE
emp_no = vempno
)
AND name IS NOT NULL
AND ROWNUM = 1;
wf_common.write_debug(vprocname, 'COUNT' || cinfo%rowcount, ts);
EXCEPTION
WHEN no_data_found THEN
wf_common.write_debug(vprocname, '222=', ts);
OPEN cinfo FOR SELECT
'' name,
'' phone,
'' alt_phone,
'' email
FROM
dual;
wf_common.write_debug(vprocname, 'COUNT' || cinfo%rowcount, ts);
END;
Replace WHEN NO_DATA_FOUND with
WHEN OTHERS
This is a cursor. It doesn't raise no_data_found. How will you know whether it'll return something or not? You won't until you
open it
fetch from it
Then you'll know.
So - unfortunately - you'll first have to check, and then decide what to do. For example (based on Scott's sample schema):
SQL> create or replace function f_test (par_deptno in emp.deptno%type)
2 return sys_refcursor
3 is
4 type cinfot is record (empno emp.empno%type,
5 ename emp.ename%type,
6 job emp.job%type);
7 cinfor cinfot;
8 cinfo sys_refcursor;
9 begin
10 -- in order to test whether it returns something, open & fetch
11 open cinfo for 'select empno, ename, job
12 from emp
13 where deptno = :a' using par_deptno;
14 fetch cinfo into cinfor;
15
16 -- has anything been found?
17 if cinfo%found then
18 -- something has been found - process it
19 close cinfo;
20 open cinfo for 'select empno, ename, job
21 from emp
22 where deptno = :a' using par_deptno;
23
24 else
25 -- nothing has been found; open another cursor
26 close cinfo;
27 open cinfo for select deptno, dname, loc
28 from dept;
29 end if;
30
31 return cinfo;
32 end;
33 /
Function created.
Testing: department 10 exists in EMP table so - return its contents:
SQL> select f_test(10) From dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB
---------- ---------- ---------
7782 CLARK MANAGER
7839 KING PRESIDENT
7934 MILLER CLERK
Department 50 doesn't exist so - return DEPT table's contents:
SQL> select f_test(50) From dual;
F_TEST(50)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Related
I have two tables , HISTORY_DATA and CURRENT_DATA , I have an input parameter REPORT_DATE ,as per following Condition , I have to fetch data :
IF(REPORT_DATE<=TRUNC(SYSDATE-40))THEN
SCHD_TABLE:='HISTORY_DATA' ;
ELSE
SCHD_TABLE:='CURRENT_DATA';
END IF ;
I tried using dynamic SQL , but it is giving error as invalid table name .
SELECT * FROM ''||SCHD_TABLE||'' ;
Used CASE STATEMENT but it also gave Syntax error :
SELECT * FROM (CASE WHEN REPORT_DATE<=TRUNC(SYSDATE-40) THEN HISTORY_DATA
ELSE CURRENT_DATA
END)
Please guide how to resolve this .
Assuming that history_data and current_data have equivalent columns, you can use UNION ALL and two queries. Each query SELECTs everything from a table if and only if the condition depending on you variable in the WHERE clause is true. Since the conditions in the WHERE clauses are negations of each other, one query will return an empty set when the other returns something. So you'll only get the result from the table you want according to your condition.
SELECT *
FROM history_data
WHERE report_date <= trunc(sysdate - 40)
UNION ALL
SELECT *
FROM current_data
WHERE report_date > trunc(sysdate - 40);
And by the way, it's a CASE expression not a statement you have there.
One option is to use a function that returns refcursor. Here's an example.
First, sample tables:
SQL> create table history_data as
2 select deptno, ename, job from emp where deptno = 10;
Table created.
SQL> create table current_data as
2 select deptno, ename, job from emp where deptno = 20;
Table created.
Function returns data depending on par_report_date:
SQL> create or replace function f_test (par_report_date in date)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 if par_report_date <= trunc(sysdate) - 40 then
7 open rc for select * from history_data;
8 else
9 open rc for select * from current_data;
10 end if;
11 return rc;
12 end;
13 /
Function created.
Testing (history data):
SQL> select f_test(date '2021-02-15') history_data from dual;
HISTORY_DATA
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB
---------- ---------- ---------
10 CLARK MANAGER
10 KING PRESIDENT
10 MILLER CLERK
Testing (current data):
SQL> select f_test(sysdate) current_data from dual;
CURRENT_DATA
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB
---------- ---------- ---------
20 SMITH CLERK
20 JONES MANAGER
20 SCOTT ANALYST
20 ADAMS CLERK
20 FORD ANALYST
SQL>
Based on your comment (about dynamic SQL): I'm not sure what benefit you expect from it. Yes, you can "concatenate" appropriate table name to the select statement, but the outcome is just the same. For example:
SQL> create or replace function f_test (par_report_date in date)
2 return sys_refcursor
3 is
4 l_str varchar2(200);
5 rc sys_refcursor;
6 begin
7 l_str := 'select * from ' ||
8 case when par_report_date <= trunc(sysdate) - 40 then 'history_data'
9 else 'current_data'
10 end;
11 open rc for l_str;
12 return rc;
13 end;
14 /
Function created.
SQL> select f_test(date '2021-02-15') history_data from dual;
HISTORY_DATA
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO ENAME JOB
---------- ---------- ---------
10 CLARK MANAGER
10 KING PRESIDENT
10 MILLER CLERK
SQL>
I'm using a DB 'A' to output a list of numbers :
123455
123456
123457
And I'm looking to build a dynamic statement to look into a DB 'B' with those results as a filter
a. Build an array with the values from DB 'A'
SELECT * FROM my_table
WHERE number in &array;
How can I achieve this ?
The DB 'B' is an Oracle DB.
Thanks
Hm. Looks like your "background" is not Oracle, because there are no "DB"s there. I mean, there are, but not in a context you're using them. If "DB" stands for a "Database", to me it looks as if you're actually talking about tables here.
Also, I don't understand what
The DB 'B' is in PLSQL means.
If "database" is a table, how is it in PL/SQL?
Anyway, to get you started: I'm fetching some data from Scott's EMP and DEPT tables. For collections, I'm using Oracle's built-in types.
These are employees in departments 10 and 20:
SQL> select deptno, ename
2 from emp
3 where deptno in (10, 20)
4 order by deptno, ename;
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
8 rows selected.
PL/SQL procedure which does something with them (the way I understood the question):
SQL> declare
2 l_a sys.odcinumberlist;
3 l_b sys.odcivarchar2list;
4 begin
5 select deptno
6 bulk collect into l_a
7 from dept
8 where deptno in (10, 20);
9
10 select ename
11 bulk collect into l_b
12 from emp
13 where deptno in (select * from table(l_a))
14 order by ename;
15
16 for i in l_b.first .. l_b.last loop
17 dbms_output.put_line(l_b(i));
18 end loop;
19 end;
20 /
ADAMS
CLARK
FORD
JONES
KING
MILLER
SCOTT
SMITH
PL/SQL procedure successfully completed.
SQL>
Lines #1 - 3 - declaration section
lines #5 - 8 - inserting values (departments) into l_a collection
Lines #10 - 14 - inserting values (employees) into l_b collection, based on values stored in l_a
Lines #16 - 18 - displaying contents of l_b
See if it helps.
[EDIT] After seeing your comment: as far as I can tell, you can't do what you wanted, not as simple as you'd want it to. This is how it works - you enter a comma-separated values as a parameter (that's your "array"), split it into rows and use the result as a subquery:
SQL> SELECT *
2 FROM dept
3 WHERE deptno IN ( SELECT REGEXP_SUBSTR ( '&&par_depts',
4 '[^,]+',
5 1,
6 LEVEL)
7 FROM DUAL
8 CONNECT BY LEVEL <= REGEXP_COUNT ( '&&par_depts', ',') + 1);
Enter value for par_depts: 10,20
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL>
This is a SQL*Plus example; you'll probably have to substitute '&&par_depts' with :par_depts (depending on a tool you use).
Use the MEMBER operator.
First create a collection type in SQL:
CREATE TYPE int_list IS TABLE OF INT;
Then just use it in an SQL statement:
SELECT *
FROM my_table
WHERE value MEMBER OF int_list(123455, 123456, 123457);
Which, for the sample data:
CREATE TABLE my_table ( id, value ) AS
SELECT LEVEL, 123453 + LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID
VALUE
2
123455
3
123456
4
123457
If you want it in PL/SQL then:
DECLARE
items int_list := int_list(123455, 123456, 123457);
BEGIN
FOR row IN (
SELECT *
FROM my_table
WHERE value MEMBER OF items
)
LOOP
DBMS_OUTPUT.PUT_LINE( row.id || ', ' || row.value );
END LOOP;
END;
/
Which, for the same data, outputs:
2, 123455
3, 123456
4, 123457
db<>fiddle here
However, if you just want to connect two databases then setup a database link.
I do not want to use cursor in my select query directly but I could not find any alternative ways to use.
My first query is
select 'Joe' as Name,'Last' as LastName, cursor(select * from temp) as Details from dual;
Table Temp:
A
B
C
1
2
3
2
3
4
OUTPUT:
Name
LastName
Details
Joe
Last
{<A=1,B=2,C=3>,<A=2,B=3,C=4>,}
I do not want to use cursor in select query directly. Is there any different usage of "cursor(select * from temp) as details"?
If it is possible in Declare begin end statments, it will be okay too. I am getting error when I try below way.
DECLARE
cursor cur is (select * from temp);
x cur%rowtype
BEGIN
open cur;
loop
fetch cur into x;
end loop;
close cur;
SELECT NAME,LASTNAME,CUR FROM DUAL;
END;
Error:
Expression is of wrong type
Well, what do you want to get as a result?
This is what you have now:
SQL> select 'Joe','Last', cursor(select * from dept) as details from dual;
'JO 'LAS DETAILS
--- ---- --------------------
Joe Last CURSOR STATEMENT : 3
CURSOR STATEMENT : 3
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Would this be OK for you?
SQL> select 'Joe', 'Last', d.deptno, d.dname, d.loc from dept d;
'JO 'LAS DEPTNO DNAME LOC
--- ---- ---------- -------------- -------------
Joe Last 10 ACCOUNTING NEW YORK
Joe Last 20 RESEARCH DALLAS
Joe Last 30 SALES CHICAGO
Joe Last 40 OPERATIONS BOSTON
SQL>
[EDIT]: a function that returns refcursor
SQL> create or replace function f_temp return sys_refcursor is
2 l_rc sys_refcursor;
3 begin
4 open l_rc for select * from temp;
5 return l_rc;
6 end;
7 /
Function created.
SQL> select 'Joe','Last', f_temp
2 from dual;
'JO 'LAS F_TEMP
--- ---- --------------------
Joe Last CURSOR STATEMENT : 3
CURSOR STATEMENT : 3
A B C
---------- ---------- ----------
1 2 3
2 3 4
SQL>
You can wrap your cursor into xmltype() , for example:
select
'Joe'
,'Last'
,xmltype (cursor(select * from temp)) as details
from dual
You may create a function that returns desired string to you, and then invoke the function in your SELECT
select 'Joe' as Name,'Last' as LastName, funcDesiredString() as Details from dual;
The function's logic would be something like this -
desiredDetails := '{';
for rec in (Select A, B, C from Temp)
Loop
desiredDetails := desiredDetails || '<A=' || rec.A || ',B=' || rec.B || ',C=' || rec.C || '>,';
End Loop;
desiredDetails := TRIM(',' FROM desiredDetails);
desiredDetails := desiredDetails || '}';
I have a table T_FUNCTION in HR schema of Oracle.
FUN_ID FUN_CMD
------ ---------------------------------
1 substr(FIRST_NAME,2,2)
2 FIRST_NAME || ' ' || LAST_NAME
I need a query which will select FUN_CMD column data from EMPLOYEES table.
Example-1: for FUN_ID = 1, I need to get result of following SQL:
select substr(FIRST_NAME,2,2) from EMPLOYEES;
Example-2: for FUN_ID = 2, I need to get result of following SQL:
select FIRST_NAME || ' ' || LAST_NAME from EMPLOYEES;
That smells like a dynamic SQL or, maybe a better choice, a procedure that returns refcursor. Here's an example:
SQL> SELECT * FROM t_function;
FUN_ID FUN_CMD
---------- ----------------------------------------
1 substr(ename, 2, 2)
2 ename ||' '|| job
SQL>
SQL> CREATE OR REPLACE PROCEDURE p_fun (
2 par_fun_id IN t_function.fun_id%TYPE,
3 p_out OUT SYS_REFCURSOR)
4 IS
5 l_cmd t_function.fun_cmd%TYPE;
6 l_str VARCHAR2 (200);
7 BEGIN
8 SELECT fun_cmd
9 INTO l_cmd
10 FROM t_function
11 WHERE fun_id = par_fun_id;
12
13 l_str := 'select ' || l_cmd || ' from emp';
14
15 OPEN p_out FOR l_str;
16 END;
17 /
Procedure created.
Testing:
SQL> var l_out refcursor
SQL> exec p_fun(1, :l_out);
PL/SQL procedure successfully completed.
SQL> print l_out
SU
--
MI
LL
AR
ON
AR
LA
LA
CO
IN
UR
DA
AM
OR
IL
14 rows selected.
And another one:
SQL> exec p_fun(2, :l_out);
PL/SQL procedure successfully completed.
SQL> print l_out
ENAME||''||JOB
--------------------
SMITH CLERK
ALLEN SALESMAN
WARD SALESMAN
JONES MANAGER
MARTIN SALESMAN
BLAKE MANAGER
CLARK MANAGER
SCOTT ANALYST
KING PRESIDENT
TURNER SALESMAN
ADAMS CLERK
JAMES CLERK
FORD ANALYST
MILLER CLERK
14 rows selected.
SQL>
CASE expression is going to be useful for your case:
select
(CASE FUN_ID WHEN 1 THEN substr(FIRST_NAME,2,2)
ELSE FIRST_NAME || ' ' || LAST_NAME END) from EMPLOYEES;
I am returning a ref cursor(P_CUR_SUMMARY) and then taking count of the same query to return a string (WARN# / ERROR). I want to eliminate the Count query.
This proc is using an existing framework so I cant return the string with the ref cursor.
I dont want to create a temp table and store data. This will remove the Count query but increase IO.
Any other option to eliminate the Count query? I just want to know if the ref cursor has ANY records. Not interested in actual count.
Procedure:
V_MAIN_TITLE and P_CUR_SUMMARY - OUT parameters
...
OPEN P_CUR_SUMMARY FOR
select * from emp...
...
select count(1) into V_CNT from emp...
...
IF V_CNT > 0 then
SELECT 'WARN#' INTO V_MAIN_TITLE FROM dual;
ELSE
SELECT 'ERROR' INTO V_MAIN_TITLE FROM dual;
END IF;
As you were told, you'll have to fetch from a cursor in order to find out what it contains (use %NOTFOUND or %FOUND cursor attributes for such a purpose). If there's something to be returned, re-open the cursor (otherwise you'd lose the first row, the one you used to check what's in there).
Here's an example:
SQL> create or replace procedure p_test
2 (par_deptno in number, par_main_title out varchar2, par_cur_summary out sys_refcursor)
3 is
4 r_emp emp%rowtype;
5 begin
6 open par_cur_summary for
7 select *
8 from emp
9 where deptno = par_deptno;
10
11 -- fetch in order to find out whether there's something returned by the cursor
12 fetch par_cur_summary into r_emp;
13 if par_cur_summary%notfound then
14 par_main_title := 'ERROR';
15 else
16 par_main_title := 'WARN#';
17
18 -- now, re-open the cursor; otherwise, you'd lose the first row, the one
19 -- that has been fetched for "testing" purpose
20 open par_cur_summary for
21 select *
22 from emp
23 where deptno = par_deptno;
24 end if;
25 end;
26 /
Procedure created.
Let's test it:
SQL> var l_cur_sum refcursor
SQL> var l_mai_tit varchar2(30)
SQL> exec p_test(37, :l_mai_tit, :l_cur_sum);
PL/SQL procedure successfully completed.
SQL> print l_mai_tit
L_MAI_TIT
---------------------------------------------------------------------------------------
ERROR
SQL> print l_cur_sum
ERROR:
ORA-01002: fetch out of sequence
no rows selected
SQL> exec p_test(10, :l_mai_tit, :l_cur_sum);
PL/SQL procedure successfully completed.
SQL> print l_mai_tit
L_MAI_TIT
---------------------------------------------------------------------------------------
WARN#
SQL> print l_cur_sum
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ---------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.1981 2450 10
7839 KING PRESIDENT 17.11.1981 5000 10
7934 MILLER CLERK 7782 23.01.1982 1300 10
SQL>
Look what happens if you don't re-open the cursor (by commenting those lines out):
if par_cur_summary%notfound then
par_main_title := 'ERROR';
else
par_main_title := 'WARN#';
/*
open par_cur_summary for
select *
from emp
where deptno = par_deptno;
*/
end if;
SQL> exec p_test(10, :l_mai_tit, :l_cur_sum);
PL/SQL procedure successfully completed.
SQL> print l_mai_tit
L_MAI_TIT
----------------------------------------------------------------------------------------
WARN#
SQL> print l_cur_sum
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ---------- ---------- ---------- ----------
7839 KING PRESIDENT 17.11.1981 5000 10
7934 MILLER CLERK 7782 23.01.1982 1300 10
SQL>
See? CLARK is lost.
The number of rows that will be returned is not known until the query is executed (i.e. when rows are fetched). When the cursor has merely been opened that is still in the future.
Perhaps it makes more sense for the client to handle the condition where no rows are returned and raise its own errors.