I am reading a lot of stuff about repeating Select statements within a loop but I am having some difficulties as I have not found something clear till now. I want to execute some queries (Select queries) several times, like in a FOR loop. Can anyone help with some example please?
The basic structure of what you are asking can be seen below.
Please provide more information for a more specific code sample.
DECLARE
l_output NUMBER;
BEGIN
FOR i IN 1..10 LOOP
SELECT 1
INTO l_output
FROM dual;
DBMS_OUTPUT.PUT_LINE('Result: ' || l_output);
END LOOP;
END;
PS: If you need to enable output in SQL*Plus, you may need to run the command
SET SERVEROUTPUT ON
UPDATE
To insert your results in another table:
DECLARE
-- Store the SELECT query in a cursor
CURSOR l_cur IS SELECT SYSDATE DT FROM DUAL;
--Create a variable that will hold each result from the cursor
l_cur_rec l_cur%ROWTYPE;
BEGIN
-- Open the Cursor so that we may retrieve results
OPEN l_cur;
LOOP
-- Get a result from the SELECT query and store it in the variable
FETCH l_cur INTO l_cur_rec;
-- EXIT the loop if there are no more results
EXIT WHEN l_cur%NOTFOUND;
-- INSERT INTO another table that has the same structure as your results
INSERT INTO a_table VALUES l_cur_rec;
END LOOP;
-- Close the cursor to release the memory
CLOSE l_cur;
END;
To create a View of your results, see the example below:
CREATE VIEW scott.my_view AS
SELECT * FROM scott.emp;
To view your results using the view:
SELECT * FROM scott.my_view;
Related
I have a table TestTable with columns of col_test1, col_test2, col_test3 ...
and I want to create a loop that accesses each of these columns individually and find the max value and place it in the variable made in the declare block and simply dbms.out.put it.
Declare
my_array sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll('col_test1','col_test2','col_test2');
v_test number(8,0);
Begin
for r in my_array.first..my_array.last
loop
select max(my_array(r)) into v_test from TestTable;
dbms_output.put_line(v_test);
end loop;
End;
/
The output I get is just the string 'col_test1'which should be 50.
This is done through oracle SQL. Is there any way to achieve this?
You could use dynamic SQL for this
Declare
my_array sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll('col_test1','col_test2','col_test2');
v_test number(8,0);
Begin
for r in my_array.first..my_array.last
loop
execute immediate 'select max(' || my_array(r) || ') from TestTable'
into v_test;
dbms_output.put_line(v_test);
end loop;
End;
If you're going to resort to dynamic SQL, however, it would generally make more sense to build a single SQL statement that took that max of all three columns in one pass rather than potentially doing three separate table scans on the same table.
I have table student_list it contains student_id only one column and 100 rows.
i need to fetch 10-10 records concurrently and then perform some operation.
CREATE OR REPLACE FUNCTION loop_fetch()
RETURNS void AS
$BODY$
DECLARE
myrow student_list%rowtype;
cur1 CURSOR FOR SELECT * FROM student_list ;
BEGIN
OPEN cur1;
LOOP
-- i need to fetch rows based on limit
FETCH NEXT 10 FROM cur1 INTO myrow;
exit when myrow IS NULL;
INSERT INTO new_tbl SELECT myrow.student_id ;
END LOOP;
CLOSE cur1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Any suggestion to achive this method - FETCH NEXT 5 FROM cur1 INTO myrow
Your code should not to work. FETCH NEXT 10 takes 10 rows from cursor, but INTO clause takes only first and other are lost. MyRow is composite variable - it can hold only one row.
I got a error:
ERROR: FETCH statement cannot return multiple rows
LINE 6: fetch 10 from r into re;
^
It is correct result.
The short and probably most correct solution is using FOR IN SELECT. This statement uses cursors internally, and it fetch 10 rows in first iteration, and 50 in other iterations.
So:
DECLARE r record;
BEGIN
FOR r IN SELECT * FROM student_list
LOOP
INSERT INTO newtbl VALUES(r.*);
END LOOP;
END;
does almost all what you want.
If you want to use cursors explicitly (and really you want to fetch in block), you need to use more cycles. The following code is little bit strange, and I write it here only for education - don't think so it has any benefit for practical life.
CREATE OR REPLACE FUNCTION loop_fetch()
RETURNS void AS
$BODY$
DECLARE
myrow student_list%rowtype;
cur1 CURSOR FOR SELECT * FROM student_list ;
rows int DEFAULT 0;
BEGIN
LOOP
-- i need to fetch rows based on limit
FOR myrow IN cur1
LOOP
INSERT INTO new_tbl SELECT myrow.student_id ;
END LOOP;
EXIT WHEN NOT FOUND;
rows := rows + 1;
EXIT WHEN rows = 10;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
This code should to work, but it is crazy.
or you can use use FOR IN EXECUTE
do $$
declare
c cursor for select * from generate_series(1,100);
r record;
begin
-- *** UGLY CODE, DON'T DO IT!!! ***
open c;
-- iteration over FETCH is possible only via
-- dynamic SQL. FOR statement uses FETCH internally
-- by default, and is nonsense use FETCH 2x
for r in execute 'fetch 10 from c'
loop
raise notice '%', r;
end loop;
close c;
-- *** UGLY CODE, DON'T DO IT!!! ***
end;
$$;
Internally FOR IN query use a cursor. So FOR IN EXECUTE FETCH is reading via cursor from another cursor, what is performance nonsense and it really ugly code.
Important things - PostgreSQL has not tabular variables - so you cannot to assign more rows to one variable, and you cannot to fill more rows from one variable.
But your request looks like premature optimization. The most fast and most effective is command:
INSERT INTO newtbl SELECT studentId FROM student_list
SQL can process very well massive operations. 1M rows is nothing. Use cursors only when you really really need it.
I'm pretty new to Oracle and Database.
I'm trying to write a stored procedure with cursors. How do I write a select statement inside the cursor loop, and how do I loop through the result set that I get from the select inside that cursor loop?
For example:
Open Curs1;
Exit When Curs1%NotFound;
Loop
Select column1,column2 from table -- Returns multiple records. How to loop through this record set and perform CRUD operations.
End loop;
Close Curs1;
Use a FOR-loop cursor - they are faster and simpler than the open/fetch/close syntax.
begin
for results1 in
(
select ...
) loop
--Do something here
for results2 in
(
select ...
where some_table.some_column = results1.some_column
) loop
--Do something here
end loop;
end loop;
end;
/
Although this literally answers the question, you generally do not want to have loops inside loops like this. If possible, it would be better to combine the two SQL statements with a join and then loop through the results.
Try to use the following example that fetches rows one at a time from the cursor variable emp_cv into the user-defined record emp_rec:
declare
TYPE YourType IS ref cursor return YourTable%rowtype;
tab_cv YourType;
tab_rec YourTable%rowtype;
begin
loop
fetch tab_cv into emp_rec;
exit when tab_cv%notfound;
...
end loop;
end;
The BULK COLLECT clause lets you fetch entire columns from the result set, or the entire result set at once. The following example, retrieves columns from a cursor into a collection:
declare
type NameList IS table of emp.ename%type;
names NameList;
cursor c1 is select ename from emp where job = 'CLERK';
begin
open c1;
fetch c1 bulk collect into names;
...
close c1;
end;
The following example uses the LIMIT clause. With each iteration of the loop, the FETCH statement fetches 100 rows (or less) into index-by table acct_ids. The previous values are overwritten.
declare
type NumList is table of number index by binary_integer;
cursor c1 is select acct_id from accounts;
acct_ids NumList;
rows natural := 100; -- set limit
begin
open c1;
loop
/* The following statement fetches 100 rows (or less). */
fetch c1 bulk collect into acct_ids limit rows;
exit when c1%notfound;
...
end loop;
close c1;
end;
You need to declare the CURSOR and FETCH the records in the loop.
DECLARE
CURSOR curs1
IS
SELECT column1,
column2
FROM yourtable;
v_column1 yourtable.column1%TYPE;
v_column2 yourtable.column2%TYPE;
BEGIN
OPEN curs1;
LOOP
FETCH curs1
INTO v_column1,
v_column2;
EXIT
WHEN curs1%NOTFOUND;
INSERT INTO yourtable2(col1)VALUES( '000'||v_column1 );
-- A sample DML operation.
--Do other operations on individual records here.
END LOOP;
CLOSE curs1;
END;
There is a table contains this kind of data: select to_char(sysdate,'day') from dual in a column. I want to get results of the every query that the table keeps.
My result set should be the result of select to_char(sysdate,'day') from dual query. So in this case it is a tuesday.
SO_SQL_BODY is Varchar2.
I wrote this code but it returns only table data.
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
END LOOP;
END a_proc;
DECLARE
res varchar2(4000);
sql_str varchar2(1000);
BEGIN
FOR r IN
(select SO_SQL_BODY FROM SO_SUB_VARS WHERE SO_SQL_BODY IS NOT NULL
)
LOOP
sql_str := r.SO_SQL_BODY;
EXECUTE immediate sql_str INTO res;
dbms_output.put_line(sql_str);
dbms_output.put_line('***********************');
dbms_output.put_line(res);
dbms_output.put_line('***********************');
END LOOP;
END;
/
Try this - iterate to not null records - execute them and print the result.This script works supposing the fact that SO_SQL_BODY contains a query which projects only one column.Also if the projection is with more than two columns then try to use a refcursor and dbms_sql package
İf var_names(indx).SO_SQL_BODY output is a runnable sql text;
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
EXECUTE IMMEDIATE var_names(indx).SO_SQL_BODY;
END LOOP;
END a_proc;
You don't need a full cursor for this example. An implicit one would make it a lot shorter.
create or replace procedure a_proc is
lReturnValue varchar2(250);
begin
for q in (select so_sql_body from so_sub_vars group by so_sql_body)
loop
execute immediate q.so_sql_body into lReturnValue;
dbms_output.put_line(lReturnValue);
end loop;
end a_proc;
You should add an exception handler that will care for cases where there is a bad SQL query in your table. Also note that executing querys saved in a database table is your entry point to SQL injection.
I'm working with a stored procedure that I didn't write, which is long and contains numerous columns and joins. The procedure returns a cursor, which the application server (.NET, incidentally) picks up and iterates through.
I'm trying to intercept the cursor using SQLPlus and PL/SQL but I'm having a hard time trying to figure out how to set up the script. Here's what I have so far:
DECLARE
cur sys_refcursor;
BEGIN
adv_schema.report_proc('CAL','01-JAN-2011','01-JAN-2012','Y',cur);
OPEN cur FOR --??????
LOOP
FETCH cur INTO column1, column2;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.Put_Line ('First Name: '||column1||' Last Name: '||column2);
END LOOP;
END;
/
What do I put in the OPEN statement? All the examples I've seen of how to do this are overly simplified examples where some table 't' is created right there in the PL/SQL block, then a cursor is opened with a query to that table, to loop over. What about when a procedure returns a cursor to a complex query with multiple tables?
Assuming that the report_proc procedure is returning the cursor (i.e. the fourth parameter is defined as an OUT SYS_REFCURSOR), there is no need to OPEN the cursor in your code. The procedure is already opening it. You just need to fetch from it.
As #Justin has already mentioned, there is no need to open the cursor returned by the report_proc procedure, you only need to fetch from that cursor. Ref cursors are weak cursors(basically cursors that return no type) and in order to fetch from a weakly typed cursor, you need to know what to fetch. When you know what type a cursor returns you can declare a local structure to fetch into, like so:
DECLARE
-- example of record
-- in your case you have to know exactly
-- how many column and of which datatype your ref cursor returns
type T_Record is record(
column_1 number,
column_2 number
);
l_record T_Record;
cur sys_refcursor;
BEGIN
adv_schema.report_proc('CAL','01-JAN-2011','01-JAN-2012','Y',cur);
LOOP
FETCH cur
INTO l_record;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.Put_Line ('First Name: '||l_record.column1
||' Last Name: '||l_record.column2);
END LOOP;
END;
Also, if you simply need to print the content of a ref cursor, you can do it in SQL*Plus as follows:
SQL> variable cur refcursor;
SQL> exec adv_schema.report_proc('CAL','01-JAN-2011','01-JAN-2012','Y', :cur);
And then use print command to print the refcursor cur:
SQL> print cur;
Do I need to fetch every column that the cursor returns, or can I fetch a subset, say the first three.
No, you fetch everything, you cannot be selective in what column to fetch. It's not impossible however, but it will involve using dbms_sql package, specifically dbms_sql.describe_columns procedure to get information about columns for a cursor.
Just for consideration, If you know that a specific column is definitely present in a cursor, you could use xmlsequence() function to fetch a specific column specifying its name in the extract() function:
SQL> declare
2 type T_List is table of varchar2(123);
3 l_names T_List;
4 l_ref_cur sys_refcursor;
5
6 begin
7 open l_ref_cur
8 for select first_name, last_name
9 from employees
10 where rownum <= 5;
11
12 SELECT t.extract('ROW/FIRST_NAME/text()').getstringval()
13 bulk collect into l_names
14 FROM table(xmlsequence(l_ref_cur)) t;
15
16 for indx in l_names.first..l_names.last
17 loop
18 dbms_output.put_line(l_names(indx));
19 end loop;
20 end;
21 /
Result:
Ellen
Sundar
Mozhe
David
Hermann
PL/SQL procedure successfully completed