What can we use in Select Query instead of cursor - sql

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 || '}';

Related

Query results in header of query in Oracle SQL

I would like to rename column names:
Main Query:
SELECT
gg.value1,
ff.attribute1,
ff.attribute2,
ff.start_date,
ff.end_date
FROM
table1 ff,
table2 gg
WHERE ff.id = gg.id
Current result:
value1 attribute1 attribute2 start_date end_date
------- ----------- -------------- ------------ --------
London GB city 1.1.1900 1.1.2000
Query from which I would like to rename header of main table:
SELECT
column_name,
column_promt
FROM
table3
WHERE column_context = 'ABC'
Result table3:
column_name column_promt
------------ -------------
attribute1 state
attribute2 what is it
My goal:
value1 state what is it start_date end_date
------- ----------- -------------- ------------ --------
London GB city 1.1.1900 1.1.2000
You'll need dynamic SQL for that. Here's an example.
Instead of join of two tables in the 1st query you posted, I simplified it and created only one sample table; it doesn't really matter for what you're asking.
SQL> select * from table1;
VALUE1 ATTRIBUTE1 ATTRIBUTE2 START_DATE END_DATE
------ ------------ --------------- ---------- ----------
London GB city 01.01.1900 01.01.2000
SQL> select * from table3;
COLUMN_NAME COLUMN_PROMPT COLUMN_CONTEXT
------------ --------------- ---------------
attribute1 state ABC
attribute2 what is it ABC
SQL>
I chose to create a function that returns ref cursor. It composes a SELECT statement by outer joining user_tab_columns with table3 as we have to create columns' aliases. To make it simpler, I'm creating aliases for all columns, not only the ones contained in table3.
dbms_output.put_line call isn't necessary, it is here just to emphasize that - when creating dynamic query - it is a good habit to first display statement you composed to make sure it is correct - and then actually run it because dynamic SQL is difficult to debug.
SQL> set serveroutput on
SQL> create or replace function f_test return sys_refcursor is
2 l_str varchar2(1000);
3 rc sys_refcursor;
4 begin
5 for cur_r in
6 (select utc.column_name, nvl(c.column_prompt, utc.column_name) column_alias
7 from user_tab_columns utc left join table3 c on upper(c.column_name) = utc.column_name
8 and c.column_context = 'ABC'
9 where utc.table_name = 'TABLE1'
10 order by utc.column_id
11 )
12 loop
13 l_str := l_str || cur_r.column_name ||' as "'|| cur_r.column_alias || '",';
14 end loop;
15 l_str := 'select ' || rtrim(l_str, ',') || ' from table1';
16 dbms_output.put_line(l_str);
17
18 open rc for l_str;
19 return rc;
20 end;
21 /
Function created.
SQL>
Testing: result comes first; trailing select statement is result of previously mentioned dbms_output.put_line call.
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
VALUE1 state what is it START_DATE END_DATE
------ ---------- --------------- ---------- ----------
London GB city 01.01.1900 01.01.2000
select VALUE1 as "VALUE1",ATTRIBUTE1 as "state",ATTRIBUTE2 as "what is
it",START_DATE as "START_DATE",END_DATE as "END_DATE" from table1
SQL>
As you can see, that's not trivial at all, but that's the price you have to pay when having such requirements.

How to use CASE statement in FROM Clause to decide which table to be used for fetching data in Oracle?

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>

Writing a PLSQL select in array

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.

SQL no data found exception not getting executed

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>

How to call a function in a package

I'm doing the following but it doesnt work
select package_name.function_name(param,param) from dual
I'm calling a function that returns a cursor so im guessing "from dual" is the problem
is there another way of doing it?
I presume you mean a Ref Cursor. This is a PL/SQL construct which acts as a pointer to a set of records returned by a query. This means it has to be interpreted by the client which runs the query. For instance, we can map a Ref Cursor to a JDBC or ODBC ResultSet.
There is certainly nothing wrong with your basic statement. Here is a function similar to your own:
SQL> desc get_emps
FUNCTION get_emps RETURNS REF CURSOR
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
P_DNO NUMBER(2) IN
P_SORT_COL VARCHAR2 IN DEFAULT
P_ASC_DESC VARCHAR2 IN DEFAULT
SQL>
I can easily call this in a wider PL/SQL block:
SQL> declare
2 rc sys_refcursor;
3 begin
4 rc := get_emps(50);
5 end;
6 /
PL/SQL procedure successfully completed.
SQL>
However, SQL*PLus can handle CURSOR constructs natively:
SQL> select get_emps(50) from dual
2 /
GET_EMPS(50)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
8060 VERREYNNE PLUMBER 8061 08-APR-08 4000 50
8061 FEUERSTEIN PLUMBER 7839 27-FEB-10 4500 50
8085 TRICHLER PLUMBER 8061 08-APR-10 3500 50
8100 PODER PLUMBER 8061 3750 50
SQL>
This statement also runs in SQL Developer, although the result set is laid out in an ugly fashion.
So, if you are having problems with your function, the questions are:
What client environment are you using?
In what precise fashion does it "not work"? Please describe the observed behaviour, including any error messages?
Also give us environment details such as the version of the database, the OS, etc.
Having read your other question on this topic I thought the problem might be due to the use of a User-Defined Ref Cursor (rather than the built-in). However, that doesn't make any difference. This packaged function:
SQL> create or replace package emp_rc_utils as
2
3 type emp_rc is ref cursor return emp%rowtype;
4
5 function get_emps
6 ( p_dno in emp.deptno%type
7 )
8 return emp_rc;
9 end;
10 /
Package created.
SQL> create or replace package body emp_rc_utils as
2
3 function get_emps
4 ( p_dno in emp.deptno%type
5 )
6 return emp_rc
7 is
8 return_value emp_rc_utils.emp_rc;
9 begin
10
11 open return_value for select * from emp where deptno = p_dno;
12
13 return return_value;
14 end get_emps;
15
16 end emp_rc_utils;
17 /
Package body created.
SQL>
Still works...
SQL> declare
2 rc sys_refcursor;
3 begin
4 rc := emp_rc_utils.get_emps(50);
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> select emp_rc_utils.get_emps(50) from dual
2 /
EMP_RC_UTILS.GET_EMP
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
8085 TRICHLER PLUMBER 8061 08-APR-10 3500 50
8060 VERREYNNE PLUMBER 8061 08-APR-08 4000 50
8061 FEUERSTEIN PLUMBER 7839 27-FEB-10 4500 50
8100 PODER PLUMBER 8061 3750 50
SQL>
You can do this via a refcursor call or populate a user defined table and return it as follows:
create or replace
function getRef return sys_refcursor
is
l_ref sys_refcursor;
begin
open l_ref for
select 1 a, 'a' c from dual
union all
select 2 a, 'b' c from dual
union all
select 3 a, 'c' c from dual
union all
select 4 a, 'd' c from dual;
return l_ref;
end getRef;
/
select getref() from dual;
GETREF()
--------
A C
---------------------- -
1 a
2 b
3 c
4 d
--you'll notice this isn't the most user-friendly result set if you look at it in SQL Developer or whatno
--drop function getRef;
you can also use the 'table' if you are passing back a table collection as such
create or replace type lookup_row as
object ( a number, c varchar2(20) );
/
create or replace type lookups_tab as
table of lookup_row;
/
create or replace
function getUserDefinedTableType return lookups_tab
is
lTestTypeTable lookups_tab;
begin
SELECT lookup_row(a,c)
bulk collect INTO lTestTypeTable
from
(select 1 a, 'a' c from dual
union all
select 2 a, 'b' c from dual
union all
select 3 a, 'c' c from dual
union all
select 4 a, 'd' c from dual);
return lTestTypeTable;
end getUserDefinedTableType;
/
select * from table(getUserDefinedTableType());
--this returns it in a more user friendly manner
--http://www.oreillynet.com/pub/a/network/2003/01/22/feuerstein.html?page=2
--http://stackoverflow.com/questions/3150137/converting-oracle-query-into-user-defined-types-in-pl-sql/3152885#3152885
A C
---------------------- --------------------
1 a
2 b
3 c
4 d
have you tried:
myCursor := package_name.function_name(param,param);
this would have to be from within a test block or a stored procedure.