I want to create a dynamic cursor, but my code does not bring me the correct data. What am I doing wrong?
DECLARE
VAR1 VARCHAR2(500);
CURSOR CUR1 IS
SELECT T.COL1 FROM TABLE1 T WHERE T.COL1 IN (VAR1);
BEGIN
VAR1 := q'['V1','V2']';
FOR REG IN CUR1 LOOP
DBMS_OUTPUT.PUT_LINE(REG.COL1);
END LOOP;
END;
In short, IN clause doesn't support bind variables.. It supports for only value,in the way you used.. You need to specify it like IN (var1, var2);
Without knowing you , you have used bind variables. One workaround is use REFCURSOR By forming a query string dynamically.
DECLARE
VAR1 VARCHAR2(500);
CUR1 SYs_REFCURSOR;
QUERY_STRING VARCHAR2(2000) := 'SELECT T.COL1 FROM TABLE1 T WHERE T.COL1 IN';
MYREC IS RECORD
(
COL1 VARCHAR(1000);
);
myrecord MYREC;
BEGIN
VAR1 := q'['V1','V2']';
QUERY_STRING:= QUERY_STRING||'('||VAR1||')';
OPEN CUR1 FOR QUERy_STRING;
LOOP
FETCH CUR1 INTO myrecord;
DBMS_OUTPUT.PUT_LINE(myrecord.COL1);
EXIT WHEN v_my_ref_cursor%NOTFOUND;
..
-- your processing
END LOOP;
CLOSE CUR1;
END;
One of my other answer also has other way using collections, for bigger IN clause list.
--I USED A TABLE IN OUR DB ORACLE 11G R2
--there are some syntax issues above that I corrected
--I moved the QUERY_STRING after the BEGIN and added output
DECLARE
VAR1 VARCHAR2(500);
CUR1 SYS_REFCURSOR;
QUERY_STRING VARCHAR2(2000);
TYPE MYREC IS RECORD
(
WO_NBR VARCHAR(1000)
);
myrecord MYREC;
BEGIN
VAR1 := q'['45466','45432']';
QUERY_STRING := 'SELECT T.WO_NBR FROM WO_SCHED T WHERE T.WO_NBR IN ('||VAR1||')';
DBMS_OUTPUT.PUT_LINE(VAR1);
DBMS_OUTPUT.PUT_LINE(QUERY_STRING);
OPEN CUR1 FOR QUERY_STRING;
LOOP
FETCH CUR1 INTO myrecord;
DBMS_OUTPUT.PUT_LINE(myrecord.WO_NBR);
EXIT WHEN CUR1%NOTFOUND;
-- your processing
END LOOP;
CLOSE CUR1;
END;
Related
How can I do something like this in oracle SQL developer?
DECLARE
p_name products.product_name%TYPE;
BEGIN
p_name := 'Strawberry';
SELECT * FROM products WHERE product_name = p_name;
END;
Use a cursor:
DECLARE
p_name VARCHAR2(100);
p_cur SYS_REFCURSOR;
BEGIN
p_name := 'Strawberry';
OPEN p_cur FOR
SELECT * FROM products where product_name=p_name;
-- do something with the cursor.
END;
/
Or use a SQL/Plus-style bind variable declaration:
VARIABLE p_name VARCHAR2;
BEGIN
:p_name := 'Strawberry';
END;
/
SELECT * FROM products where product_name=:p_name;
If only one row will ever be returned from your query (i.e. product_name is UNIQUE) then you can use SELECT ... INTO ...:
DECLARE
p_name VARCHAR2(100);
p_value1 products.value1%TYPE;
p_value2 products.value2%TYPE;
p_value3 products.value3%TYPE;
BEGIN
p_name := 'Strawberry';
SELECT value1, value2, value3
INTO p_value1, p_value2, p_value3
FROM products
WHERE product_name=p_name;
-- do something with the values.
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
-- Handle the exception
WHEN TOO_MANY_ROWS THEN
NULL;
-- Handle the exception
END;
/
You need to give the SELECT statement somewhere to put the results of the query:
DECLARE
p_name VARCHAR2(100);
aProducts_row PRODUCTS%ROWTYPE;
BEGIN
p_name := 'Strawberry';
SELECT *
INTO aProducts_row
FROM products
where product_name=p_name;
-- Add code to manipulate data in aProducts_row here, as in...
DBMS_OUTPUT.PUT_LINE('PRODUCT_NAME = ''' ||
aProducts_row.PRODUCT_NAME || '''');
END;
Or if you're expecting more than one row to be returned you can use a cursor:
DECLARE
p_name VARCHAR2(100);
BEGIN
p_name := 'Strawberry';
FOR aProducts_row IN (SELECT *
FROM products
where product_name=p_name)
LOOP
-- Add code to manipulate data in aProducts_row here, as in...
DBMS_OUTPUT.PUT_LINE('PRODUCT_NAME = ''' ||
aProducts_row.PRODUCT_NAME || '''');
END LOOP;
END;
If the question is not about setting variables but is actually about output from a PL/SQL anonymous block then it is already answered here.
declare
rc sys_refcursor;
begin
open rc for select 'Hello' as test from dual;
dbms_sql.return_result(rc);
end;
You can set any variable you want using the normal PL/SQL assignment syntax. If there is some part of the documentation that is not clear then please provide an example of what you are trying to do.
If we have a column in a table of type number, how can we store the result of select query on that column in an array ?
This sample uses a list (table of numbers) to achieve this, because i find
those lists much more handy:
CREATE OR REPLACE TYPE numberlist AS TABLE OF NUMBER;
DECLARE
v_numberlist numberlist;
BEGIN
SELECT intval numbercolumn
BULK COLLECT INTO v_numberlist
FROM lookup;
FOR i IN 1..v_numberlist.count
LOOP
dbms_output.put_line( v_numberlist(i) );
END LOOP;
END;
Create a type which store number:-
CREATE OR REPLACE TYPE varray is table of number;
--write your select query inside for loop () where i am extracting through level
declare
p varray := varray();
BEGIN
for i in (select level from dual connect by level <= 10) loop
p.extend;
p(p.count) := i.level;
end loop;
for xarr in (select column_value from table(cast(p as varray))) loop
dbms_output.put_line(xarr.column_value);
end loop;
END;
output:-
1
2
3
4
5
6
7
8
9
10
Just an option to use some native SQL datatype. Hope it helps.
SET SERVEROUTPUT ON;
DECLARE
lv_num_tab DBMS_SQL.NUMBER_TABLE;
BEGIN
SELECT LEVEL BULK COLLECT INTO lv_num_tab FROM DUAL CONNECT BY LEVEL < 10;
FOR I IN lv_num_tab.FIRST..lv_num_tab.LAST
LOOP
dbms_output.put_line(lv_num_tab(i));
END LOOP;
END;
You may also want to put the whole select in a table. You can use a BULK COLLECT to an array:
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
PROCEDURE get_tables(p_owner in varchar2)
as
v_res t_my_list;
v_qry varchar2(4000) := '';
begin
v_qry := ' SELECT table_name from all_tables where owner='''||p_owner||'''';
dbms_output.put_line(v_qry);
-- all at once in the table
execute immediate v_qry bulk collect into v_res;
FOR I in 1..v_res.count
loop
dbms_output.put_line(v_res(i));
end loop;
exception
when others then
raise;
end get_tables;
/
begin
get_tables('E') ;
end;
/
Please, explain me how to use cursor for loop in oracle.
If I use next code, all is fine.
for rec in (select id, name from students) loop
-- do anything
end loop;
But if I define variable for this sql statement, it doesn't work.
v_sql := 'select id, name from students';
for rec in v_sql loop
-- do anything
end loop;
Error: PLS-00103
To address issues associated with the second approach in your question you need to use
cursor variable and explicit way of opening a cursor and fetching data. It is not
allowed to use cursor variables in the FOR loop:
declare
l_sql varchar2(123); -- variable that contains a query
l_c sys_refcursor; -- cursor variable(weak cursor).
l_res your_table%rowtype; -- variable containing fetching data
begin
l_sql := 'select * from your_table';
-- Open the cursor and fetching data explicitly
-- in the LOOP.
open l_c for l_sql;
loop
fetch l_c into l_res;
exit when l_c%notfound; -- Exit the loop if there is nothing to fetch.
-- process fetched data
end loop;
close l_c; -- close the cursor
end;
Find out more
try this :
cursor v_sql is
select id, name from students;
for rec in v_sql
loop
-- do anything
end loop;
then no need to open, fetch or close the cursor.
You're not executing that sql string anywhere. Simply do this
v_sql := 'select id, name from students';
open cur for v_sql;
for rec in cur loop
-- do anything
end loop;
Or you can do this
cursor cur is select id, name from students;
open cur;
for rec in cur loop
-- do anything
end loop;
Or you can do this
for rec in (select id, name from students) loop
-- do anything
end loop
You have to use Refcursor if you are making the query at runtime. Actually refcursors are pointers to the query they wont take up any space for the rows fetched.
Normal Cursors will not work for it.
declare
v_sql varchar2(200);
rec sys_refcursor;
BEGIN
v_sql := 'select id, name from students';
open rec for v_sql
loop
fetch
exit when....
-- do anything
end loop;
I have a table A which has column A which holds table names as values.
All these tables have a common column C. I need maximum value of this column for each table.
I tried this using dynamic SQL but I'm getting errors. Please suggest.
DECLARE
query1 VARCHAR2(100);
c_table VARCHAR2(40);
c_obj VARCHAR2(20);
Cursor cursor_a IS
SELECT a FROM A;
BEGIN
Open cursor_a;
LOOP
Fetch cursor_a INTO c_table2;
EXIT WHEN cursor_a%notfound;
query1 := 'SELECT max(object_ref) AS "c_obj" FROM c_table' ;
EXECUTE IMMEDIATE query1;
dbms_output.put_line('Maximum value: '|| c_table || c_obj);
END LOOP;
Close cursor_a;
END;
Dynamic SQL can't see your PL/SQL variable: you need to pass it a string which can be executed in the scope of the SQL engine. So you need to concatenate the table name with the statement's boilerplate text:
query1 := 'SELECT max(c) FROM ' || variable_name;
You also need to return the result of the query into a variable.
Here is how it works (I've stripped out some of the unnecessary code from your example):
DECLARE
c_table VARCHAR2(40);
c_obj VARCHAR2(20);
BEGIN
for lrec in ( select a as tab_name from A )
LOOP
EXECUTE IMMEDIATE 'SELECT max(object_ref) FROM ' || lrec.tab_name
into c_obj ;
dbms_output.put_line('Maximum value: '|| lrec.tab_name
|| '='|| c_obj);
END LOOP;
END;
There is some miss match in veriables that you had used i.e.
declared as "c_table" but accessing as "c_table2"
Each table common column name is "C" but accessing as "object_ref"
In dynamic query use INTO keyword to store the value to your varibale
Suggestions
Use concat() function to prepare the query dynamically i.e. something like:
SET #SQL := CONCAT('SELECT max(c) INTO ', c_obj, ' FROM ',c_table);
Steps of implementing dynamic query is:
SET #SQL = <your dynamic query>
PREPARE stmt FROM #SQL;
EXECUTE stmt;
Sample code:
DECLARE
query1 VARCHAR2(100);
c_table VARCHAR2(40);
c_obj VARCHAR2(20);
CURSOR cursor_a IS
SELECT a FROM A;
BEGIN
OPEN cursor_a;
LOOP
FETCH cursor_a INTO c_table;
EXIT WHEN cursor_a%notfound;
SET #SQL := CONCAT('SELECT max(object_ref) AS c_obj INTO ', c_obj, ' FROM ',c_table);
PREPARE stmt FROM #SQL;
EXECUTE stmt;
dbms_output.put_line('Maximum value: '|| c_table || c_obj);
END LOOP;
CLOSE cursor_a;
END;
DECLARE
TYPE EmpList IS TABLE OF varchar2(50) INDEX BY BINARY_INTEGER;
temp SYS_REFCURSOR;
v_temp varchar2(50);
v_emp EmpList;
BEGIN
v_emp (1) := 'gaurav';
v_emp (2) := 'manu';
open temp for select v_emp(level) from dual connect by level<=2;
loop
fetch temp into v_temp;
exit when temp%notfound;
DBMS_OUTPUT.put_line (v_temp);
end loop;
close temp;
--the below part works, then why not the above part dint works
for i in v_emp.first..v_emp.last
loop
dbms_output.put_line(v_emp(i));
end loop;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;
Can anyone please tell me why this collection dint work here??,It is giving me no_data_found exception.
the reason for failure is that
v_emp(level)
is evaluated (as its a variable) at runtime so would actually evaluate on all rows to
v_emp(0);
you could see this if you changed your array to
v_emp (0) := 'gaurav';
v_emp (1) := 'manu';
the proper way (in case you were not aware) is:
create TYPE EmpList IS TABLE OF varchar2(50);
/
and then :
v_emp := EmpList('gaurav', 'manu');
open temp for select column_value from table(v_emp);
Try This:
DECLARE
TYPE EmpList IS TABLE OF varchar2(50) INDEX BY BINARY_INTEGER;
temp SYS_REFCURSOR;
v_temp varchar2(50);
v_emp EmpList;
BEGIN
v_emp (0) := 'gaurav';
v_emp (1) := 'manu';
open temp for select v_emp(level) from dual connect by level<=2;
loop
fetch temp into v_temp;
exit when temp%notfound;
DBMS_OUTPUT.put_line ('v_temp' || v_temp);
end loop;
close temp;
--the below part works, then why not the above part dint works
for i in v_emp.first..v_emp.last
loop
dbms_output.put_line(v_emp(i));
end loop;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;