i have a develop a application that contain large no of rows i was using ref cursor but i do not need a cursor. how can achieve that.
here is below code i was using ref cursor please help me out how can avoid it.any idea
create or replace
PROCEDURE remove_emp
(
employee_id in NUMBER,
o_data out sys_refcursor
)
AS
v_account_status help.topic%type;
v_username help.seq%type;
v_lock_date help.info%type;
BEGIN
open o_data for
select topic,seq, info
into v_account_status,v_username,v_lock_date
from help;-- where seq=employee_id ;
DBMS_OUTPUT.put_line ('topic:'||v_account_status);
DBMS_OUTPUT.put_line ('seq:'||v_username);
DBMS_OUTPUT.put_line ('info:'||v_lock_date);
END remove_emp;
Just remove the cursor and just use SELECT ... INTO ...:
Oracle Setup:
CREATE TABLE help ( topic, seq, info ) AS
SELECT 'a', 1, 'aa' FROM DUAL UNION ALL
SELECT 'b', 2, 'bb' FROM DUAL UNION ALL
SELECT 'c', 3, 'cc' FROM DUAL;
Procedure:
create PROCEDURE remove_emp
(
employee_id in help.seq%type
)
AS
v_account_status help.topic%type;
v_username help.seq%type;
v_lock_date help.info%type;
BEGIN
select topic,seq, info
into v_account_status,v_username,v_lock_date
from help where seq=employee_id;
DBMS_OUTPUT.put_line ('topic:'||v_account_status);
DBMS_OUTPUT.put_line ('seq:'||v_username);
DBMS_OUTPUT.put_line ('info:'||v_lock_date);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line ('seq:Not Found!');
END remove_emp;
/
Call the procedure:
BEGIN
remove_emp(1);
END;
/
Output:
topic:a
seq:1
info:aa
db<>fiddle here
Related
I have a table named emp with name, id, salary as column names.
My procedure takes id as input and it can be more than one id also. like('1','2','3',...)
Required: For each and every id the values should be returned as XML.
Supoose: For id=1 and id=2 as input my output should be
<row>
<name>name1</name>
<salary>salary1</salary>
</row>
<row>
<name>name2</name>
<salary>salary2</salary>
</row>
I am able to run the code to separate id's and get the output. But I am getting stuck at sending values as xml.
Code i tried:
procedure get_details(P_ID varchar2) as
begin
select * from emp where id in (
select regexp_substr(P_ID,'[^,]+', 1, level) from dual
connect BY regexp_substr(P_ID, '[^,]+', 1, level)
is not null);
end get_values;
Any input will be appreciated.
Edit regarding solutions: Both the solutions provided in db-fiddle are working perfectly fine.
Solution 1:
CREATE PROCEDURE get_details(
P_IDs IN varchar2
) IS
v_clob CLOB;
BEGIN
SELECT XMLELEMENT(
"root",
XMLAGG(
XMLELEMENT(
"row",
XMLFOREST(
name AS "name",
salary AS "salary"
)
)
)
).getClobVal()
INTO v_clob
FROM emp
where p_ids LIKE '%''' || id || '''%';
DBMS_OUTPUT.PUT_LINE( v_clob );
end get_details;
/
BEGIN
DBMS_OUTPUT.PUT_LINE( 'Your output:' );
get_details( '''1'',''3''' );
END;
/
Solution 2:
create or replace procedure get_details(p_id varchar2)
as
lo_xml_output clob;
begin
select to_clob(xmltype(cursor(select *
from emp
where id in
(select regexp_substr(p_id,'[^,]+', 1, level)
from dual
connect by regexp_substr(P_ID, '[^,]+', 1,level) is
not null))))
into lo_xml_output from dual;
DBMS_OUTPUT.PUT_LINE( lo_xml_output );
end get_details;
/
BEGIN
DBMS_OUTPUT.PUT_LINE('');
get_details('1,3');
END;
/
You can use the XML functions:
CREATE PROCEDURE get_details(
P_IDs IN varchar2
) IS
v_clob CLOB;
BEGIN
SELECT XMLELEMENT(
"root",
XMLAGG(
XMLELEMENT(
"row",
XMLFOREST(
name AS "name",
salary AS "salary"
)
)
)
).getClobVal()
INTO v_clob
FROM emp
where p_ids LIKE '%''' || id || '''%';
DBMS_OUTPUT.PUT_LINE( v_clob );
end get_details;
/
So, for the test data:
CREATE TABLE emp ( id, name, salary ) AS
SELECT 1, 'name1', 1000 FROM DUAL UNION ALL
SELECT 2, 'name2', 2000 FROM DUAL UNION ALL
SELECT 3, 'name3', 3000 FROM DUAL
The anonymous block:
BEGIN
get_details( '''1'',''3''' );
END;
/
Outputs:
<root><row><name>name1</name><salary>1000</salary></row><row><name>name3</name><salary>3000</salary></row></root>
db<>fiddle here
Another way will be by using xmltype,you can pass a query as a cursor to xmltype.
create or replace procedure get_details(p_id varchar2)
as
lo_xml_output clob;
begin
select to_clob(xmltype(cursor(select *
from emp
where id in
(select regexp_substr(p_id,'[^,]+', 1, level)
from dual
connect by regexp_substr(P_ID, '[^,]+', 1,level) is
not null))))
into lo_xml_output from dual;
DBMS_OUTPUT.PUT_LINE( lo_xml_output );
end get_details;
/
-- test
BEGIN
DBMS_OUTPUT.PUT_LINE('');
get_details('1,3');
END;
/
Db<>fiddle for your reference
P.S. we can also usedbms_xmlgen.getxml but then we need to build the SQL dynamically before passing it to dbms_xmlgen.getxml. you can see in the fiddle as well.
I want to initialize variable in pl/sql In the following way. But the value of the variable is null. how can I modify this code?
CREATE OR REPLACE PROCEDURE P_TEMP
(
pList0 OUT SYS_REFCURSOR
)
AS
TEMP_COL1 VARCHAR2(100);
TEMP_COL2 VARCHAR2(100);
BEGIN
OPEN cv_1 FOR
WITH TEMP_TABLE(COLUMN_1, COLUMN_2 ,...., COLUMN_N)
AS (SELECT COL_1, COL_2, ... , COL_N)
SELECT COL_1, COL_2
INTO TEMP_COL1, TEMP_COL2
FROM TEMP_TABLE;
DBMS_OUTPUT.PUT_LINE(TEMP_COL1); -- null!!
DBMS_OUTPUT.PUT_LINE(TEMP_COL2); -- null!!
END;
Don't open a cursor; just use the SELECT:
CREATE OR REPLACE PROCEDURE P_TEMP
(
pList0 OUT SYS_REFCURSOR
)
AS
TEMP_COL1 VARCHAR2(100);
TEMP_COL2 VARCHAR2(100);
BEGIN
WITH TEMP_TABLE(COLUMN_1, COLUMN_2 ,...., COLUMN_N) AS (
SELECT COL_1, COL_2, ... , COL_N
FROM your_table -- Need to select from a table.
)
SELECT COLUMN_1, COLUMN_2 -- Use the aliases you set in the sub-query factoring clause.
INTO TEMP_COL1, TEMP_COL2
FROM TEMP_TABLE
WHERE ROWNUM = 1 -- Only want a single row returned into the variables;
-- Or simpler
SELECT COL_1, COL_2
INTO TEMP_COL1, TEMP_COL2
FROM your_table
WHERE ROWNUM = 1 -- Only want a single row returned into the variables;
DBMS_OUTPUT.PUT_LINE(TEMP_COL1);
DBMS_OUTPUT.PUT_LINE(TEMP_COL2);
END;
/
You are missing the FETCH loop from the cursor. The easiest way to do this to use an implicit cursor:
CREATE OR REPLACE PROCEDURE P_TEMP AS
BEGIN
FOR rec IN
(
WITH temp_table(column_1, column_2 ,...., column_n) AS
(SELECT col_1, col_2, ... , col_n)
SELECT col_1, col_2
FROM temp_table
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.col1);
DBMS_OUTPUT.PUT_LINE(rec.col2);
END LOOP;
END;
If, on the other hand, you only want to return the opened cursor, then you must not fetch from it yourself.
If you want to do both, i.e. fetch data yourself and return the cursor, then you must open the cursor twice for the same query. Once to process it yourself, once to give it to the caller to process it.
CREATE OR REPLACE PROCEDURE P_TEMP
(
out_cursor OUT SYS_REFCURSOR
)
AS
v_col1 VARCHAR2(100);
v_col2 VARCHAR2(100);
v_sql VARCHAR2(1000) := 'WITH temp_table ... SELECT col_1, col_2 FROM temp_table';
BEGIN
OPEN out_cursor FOR v_sql;
LOOP
FETCH out_cursor INTO v_col1, v_col2;
EXIT WHEN out_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('col_1: ' || v_col1);
DBMS_OUTPUT.PUT_LINE('col_2: ' || v_col2);
END LOOP;
OPEN out_cursor FOR v_sql;
END;
How to pass the 'values' of the output cursor o_cur to a further loop?
CREATE OR REPLACE PROCEDURE dyn_cursor (o_cur OUT SYS_REFCURSOR)
IS
script VARCHAR2 (4000);
BEGIN
script := 'select sysdate-1 notnow, sysdate today from dual union all select sysdate+1 notnow, sysdate today from dual';
OPEN o_cur FOR script;
-- the question is related to this block:
for i in o_cur
loop
DBMS_OUTPUT.PUT_LINE(i.notnow);
end loop;
-----------------------
END;
FETCH is usually handled outside the procedure when the refcursor is an OUT variable
CREATE OR REPLACE PROCEDURE dyn_cursor (o_cur OUT SYS_REFCURSOR)
IS
script VARCHAR2 (4000);
BEGIN
script := 'select sysdate-1 notnow, sysdate today from dual union all select sysdate+1 notnow, sysdate today from dual';
OPEN o_cur FOR script;
END;
/
SET SERVEROUTPUT ON
DECLARE
v_cur SYS_REFCURSOR;
v_notnow DATE;
v_today DATE;
BEGIN
dyn_cursor(v_cur);
LOOP
FETCH v_cur INTO
v_notnow,
v_today;
EXIT WHEN v_cur%notfound;
dbms_output.put_line(v_notnow);
END LOOP;
END;
/
02-10-18
04-10-18
PL/SQL procedure successfully completed.
This seems a strange construct. I would create a view you can reuse at different places.
CREATE OR REPLACE VIEW VW_Times AS
SELECT sysdate-1 notnow, sysdate today FROM dual
UNION ALL
SELECT sysdate+1 notnow, sysdate today FROM dual;
and
CREATE OR REPLACE PROCEDURE OutputTime ()
IS
CURSOR o_cur IS
SELECT * FROM VW_Times;
BEGIN
FOR i IN o_cur
LOOP
DBMS_OUTPUT.PUT_LINE(i.notnow);
END LOOP;
END;
Tanks to post of Kaushik Nayak, I found the acceptable solution:
CREATE OR REPLACE PROCEDURE dyn_cursor (o_cur OUT SYS_REFCURSOR)
IS
script VARCHAR2 (4000);
type array is table of VARCHAR2(20) index by binary_integer;
l_datapoint1 array;
l_datapoint2 array;
BEGIN
script := 'select sysdate-1 notnow, sysdate today from dual union all select sysdate+1 notnow, sysdate today from dual';
OPEN o_cur FOR script;
LOOP
FETCH o_cur bulk collect into l_datapoint1, l_datapoint2;
for i in 1 .. l_datapoint1.count
loop
DBMS_OUTPUT.PUT_LINE( l_datapoint1(i) || ', ' || l_datapoint2(i) );
end loop;
exit when o_cur%notfound;
end loop;
close o_cur;
END;
Is it possible to use a ref_cursor with ORACLE WITH CLAUSE. For example, I have the following scenario. First I have a procedure which returns a ref_cursor
PROCEDURE p_return_cursor(p_id IN NUMBER, io_cursor OUT t_cursor)
AS
BEGIN
OPEN io_cursor FOR
SELECT col1, col2
FROM Table1 t
WHERE t.id = p_id;
END;
Second, I have another procedure on which I make a call to p_return_cursor:
PROCEDURE p_test(p_cid IN NUMBER)
AS
l_cursor t_cursor;
l_rec Table1%ROWTYPE;
BEGIN
p_return_cursor(p_id => p_cid, io_cursor => l_cursor);
-- CODE GOES HERE
...
Now, my question is, can I make a temp table using the Oracle's WITH CLAUSE using the cursor; something like:
...
WITH data AS (
LOOP
FETCH l_cursor INTO l_rec;
EXIT WHEN l_cursor%NOTFOUND;
SELECT l_rec.col1, l_rec.col2 FROM DUAL;
END LOOP;
CLOSE l_cursor;
)
You cannot do it directly. You can, however, BULK COLLECT your cursor into a PL/SQL table variable and use that in a WITH clause.
Be careful of memory usage if the cursor contains many rows.
Full example:
CREATE TABLE table1 ( col1 NUMBER, col2 NUMBER );
INSERT INTO table1 ( col1, col2 ) SELECT rownum, 100+rownum FROM DUAL CONNECT BY ROWNUM <= 15;
COMMIT;
CREATE OR REPLACE PACKAGE se_example AS
TYPE t_cursor IS REF CURSOR
RETURN table1%ROWTYPE;
TYPE l_rec_tab IS TABLE OF table1%ROWTYPE;
PROCEDURE p_test (p_cid IN NUMBER);
END se_example;
CREATE OR REPLACE PACKAGE BODY se_example AS
-- private
PROCEDURE p_return_cursor (p_id IN NUMBER, io_cursor OUT t_cursor) AS
BEGIN
OPEN io_cursor FOR
SELECT col1,
col2
FROM table1 t;
--WHERE t.id = p_id; -- I didn't put "id" column in my sample table, sorry...
END p_return_cursor;
PROCEDURE p_test (p_cid IN NUMBER) IS
l_cursor t_cursor;
l_tab l_rec_tab;
l_dummy NUMBER;
BEGIN
p_return_cursor (p_id => p_cid, io_cursor => l_cursor);
FETCH l_cursor BULK COLLECT INTO l_tab;
-- *** instead of this
-- WITH data AS (
-- LOOP
-- FETCH l_cursor INTO l_rec;
-- EXIT WHEN l_cursor%NOTFOUND;
-- SELECT l_rec.col1, l_rec.col2 FROM DUAL;
-- END LOOP;
-- CLOSE l_cursor;
-- )
-- '
--
-- *** do this
WITH data AS
( SELECT col1, col2 FROM TABLE(l_tab) )
SELECT sum(col1 * col2) INTO l_dummy
FROM data;
dbms_output.put_line('result is ' || l_dummy);
END p_test;
END se_example;
begin
se_example.p_test(100);
end;
I have made below code.
CREATE OR REPLACE TYPE CAL IS OBJECT(
EMPLOYEE_NAME VARCHAR2(30),
R_DATE DATE,
COMMENTS VARCHAR2(50)
);
CREATE OR REPLACE TYPE T_REC is table of cal;
create or replace function CALENDAR(v_team_name varchar2)
return t_rec
IS
v_rec t_rec;
v_COMM VARCHAR2(50);
v_name VARCHAR2(30);
BEGIN
FOR i in (Select ID,EMPLOYEE_NAME from EMPLOYEE where TEAM_NAME=v_team_name)
LOOP
v_name:=i.EMPLOYEE_NAME;
FOR k in (select EMP_ID, START_DT,END_DT-START_DT+1 DAYS,NAME,COMMENTS from EMP_ROTA where EMP_ID=i.ID)
LOOP
v_COMM:=k.NAME||', '||k.COMMENTS;
select t_rec(v_name,k.START_DT+level-1,v_COMM)
into v_rec
from dual connect by level < k.DAYS;
END LOOP;
END LOOP;
Return v_rec;
END;
Facing below error in compiling the function.
Compilation failed,line 14 (04:33:54)
PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got CHARCompilation failed,line 14 (04:33:54)
PL/SQL: SQL Statement ignored
While using below query in function
select v_name,k.START_DT + level -1,v_COMM
into v_REC
from dual connect by level < k.DAYS;
Facing below error
PL/SQL: ORA-00947: not enough valuesCompilation failed,line 14 (04:50:49)
Trying small snippet of code stil facing ORA-00932.
create or replace function ROTA_CALENDAR
return t_CAL
IS
v_CAL t_CAL;
BEGIN
select t_cal('v_name',SYSDATE,'NAME')
into v_CAL
from dual;
Return v_CAL;
END;
what am I doing wrong?
I hope below snippet will help. Couple of things to take in consideration.
1 Don't use "" while any nomenclature
2 You have to select from Object type in the SELECT query as mentioned below.
3 Use BULK COLLECT instead of INTO only...
CREATE OR REPLACE
FUNCTION CALENDAR(
V_TEAM_NAME VARCHAR2)
RETURN t_rec
IS
v_rec t_rec;
v_COMM VARCHAR2(50);
v_name VARCHAR2(30);
BEGIN
FOR i IN
(
SELECT
EMPNO,
'AVRAJIT' ENAME
FROM
EMP
WHERE
JOB=v_team_name
)
LOOP
v_name:=i.ENAME;
FOR k IN
(
SELECT
EMPNO,
SYSDATE,
SYSDATE+1 DAYS,
ENAME,
JOB
FROM
EMP_V1
WHERE
EMPNO=i.EMPNO
)
LOOP
v_COMM:=k.ENAME||', '||k.JOB;
SELECT
CAL(V_NAME,SYSDATE+1,V_COMM) BULK COLLECT
INTO
v_rec
FROM
dual
CONNECT BY level < 10;
END LOOP;
END LOOP;
RETURN v_rec;
END;
-------------------------------OUTPUT-------------------------------------------
SELECT OBJECT_NAME,STATUS FROM ALL_OBJECTS
WHERE OBJECT_NAME = 'CALENDAR';
OBJECT_NAME STATUS
CALENDAR VALID
-------------------------------OUTPUT-------------------------------------------