Cursor in pl/sql - oracle

I have ORACLE class airport_t that has data fields for
Rank,
Airport,
Location,
Country,
Code_IATA,
Code_ICAO,
Total_passenger,
Rank_change,
Percent_Change
I created table AIRPORTS2017OO that has one single column AIRPORT, which can store an object of type airport_t. I have 50 rows of data in Airport2017 table which I to insert into airport column of table AIRPORTS2017OO using cursor.
I am able to iterate through airports2017 table through cursor given below, but I do not know how to insert data into AIRPORTS2017OO table.
Here is my solution of getting all info from airports2017 table
DECLARE
rank1 NUMBER;
airports1 VARCHAR2 (80);
location1 VARCHAR (40);
country1 VARCHAR (30);
iata1 VARCHAR (3);
icao1 VARCHAR (4);
total_pass NUMBER;
rank_c NUMBER;
p_change NUMBER;
CURSOR display
IS
SELECT * FROM airports2017;
BEGIN
OPEN display;
LOOP
FETCH display
INTO rank1,
airports1,
location1,
country1,
iata1,
icao1,
total_pass,
rank_c,
p_change;
IF display%FOUND
THEN
DBMS_OUTPUT.put_line (airports1);
ELSE
EXIT;
END IF;
END LOOP;
CLOSE display;
END;
/

You do know that you can do:
INSERT INTO AIRPORTS2017OO select * from airports2017;
instead of all this cursor code right?
Anyway here's how to do it:
DECLARE
a_data ARRAY;
CURSOR display
IS
SELECT * FROM airports2017;
BEGIN
OPEN display;
LOOP
FETCH display BULK COLLECT INTO a_data;
FORALL i IN 1..a_data.COUNT
INSERT INTO AIRPORTS2017OO VALUES a_data(i);
IF display%FOUND
THEN
DBMS_OUTPUT.put_line (airports1);
ELSE
EXIT;
END IF;
END LOOP;
CLOSE display;
END;
/

Related

How to store a column of result of select query in an array?

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;
/

Are cursors necessary for queries in a procedure?

I'm rather new to Oracle and I was asked to write a procedure to query some data from a table. I built it with 2 arguments, a cursor and a number. Essentially I have:
PROCEDURE PROC_NAME (
cursor_name IN OUT NOCOPY MY_DEFINED_CURSOR_TYPE,
a_number IN NUMBER);
AS
BEGIN
OPEN CURSOR_NAME FOR
SELECT
column
FROM
table
WHERE
table.dat_value > (SYSDATE - a_number);
END PROC_NAME;
It works like a charm, and I'm able to fetch the column from the cursor. My problem is that the requester doesn't want to pass in a cursor, they just want to pass in the number. I've never created a procedure that doesn't use a cursor to return the values of a query and the examples I have seen only ever do it that way. Is this possible?
You can use a collection:
CREATE PROCEDURE PROC_NAME (
a_number IN NUMBER,
numbers OUT SYS.ODCINUMBERLIST
)
AS
BEGIN
SELECT number_value
BULK COLLECT INTO numbers
FROM table_name
WHERE date_value > (SYSDATE - a_number);
END PROC_NAME;
Also, if you don't want to pass in a cursor then you can just pass one out:
CREATE OR REPLACE PROCEDURE PROC_NAME (
a_number IN NUMBER,
numbers OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN numbers FOR
SELECT number_value
FROM table_name
WHERE date_value > (SYSDATE - a_number);
END PROC_NAME;
Use a function instead ? But it's just a "stylistic" difference compared to procedure out parameter. Anyway the returned value have to be implicitly passed (unlike in SQL Server as noted by #ShannonSeverance).
function f(
p_days in number
) return my_defined_cursor_type is
v_cur my_defined_cursor_type;
begin
open v_cur for
select
column
from
table
where
table.dat_value > (sysdate - p_days);
return v_cur;
end;
/
Usage
declare
v_cur my_defined_cursor_type := f(42);
begin
-- use v_cur as you like
end;
If you want to apply some PL/SQL logic, but remain using select for querying the data (i.e not pass in a cursor - use pipelined functions.
You need to define the TYPEs of the result row and table; FETCH the cursor and PIPE the results in the function.
CREATE or replace type MY_DEFINED_ROW_TYPE as object
(
txt VARCHAR2(30)
);
/
create or replace type MY_DEFINED_TABLE_TYPE as table of MY_DEFINED_ROW_TYPE
/
create or replace function FUN_NAME( a_number IN NUMBER) return
MY_DEFINED_TABLE_TYPE
PIPELINED
as
cur MY_DEFINED_CURSOR_TYPE;
v_txt varchar2(30);
begin
OPEN cur
FOR
SELECT
column
FROM table
WHERE table.dat_value > (SYSDATE - a_number);
LOOP
FETCH cur INTO v_txt;
EXIT WHEN cur%NOTFOUND;
pipe row(v_txt);
END LOOP;
return;
end;
/
The usage:
select * from table (FUN_NAME(2));

how to fetch cursor values into an object

I want to fetch values from a cursor and store them in an object....
I tried doing the same with Record i got the output
DECLARE
CURSOR lc_emp_fetch
IS
SELECT emp_no,emp_name FROM maniemp;
TYPE r_emp_record IS RECORD (
eno maniemp.emp_no%TYPE,
ename maniemp.emp_name%TYPE
);
TYPE t_emp IS TABLE OF r_emp_record;
lt_emp_rcd t_emp;
BEGIN
OPEN lc_emp_fetch;
LOOP
FETCH lc_emp_fetch BULK COLLECT INTO lt_emp_rcd LIMIT 5;
EXIT WHEN lt_emp_rcd.COUNT=0;
FOR indx IN 1..lt_emp_rcd.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(lt_emp_rcd(indx).eno||lt_emp_rcd(indx).ename);
END LOOP;
END LOOP;
CLOSE lc_emp_fetch;
END;
/
but when i try doing the same in an object its not working... i surfed all the websites but didn't get proper example program. This is my object:
CREATE OR REPLACE TYPE Typename3 AS OBJECT (
eno number,
ename varchar2(500),
esal number);
SHOW ERRORS;
I am new to this i don't know how to do this can someone help me with this
If you want to try the above example with an object and type then you should create both are at schema level it means
CREATE OR REPLACE type R_EMP_OBJECT as object(
eno number,
ename varchar2(30)
);
and
`create or replace type t_emp IS TABLE OF r_emp_object`;
then
DECLARE
lt_emp_rcd t_emp;
BEGIN
select r_emp_object (emp,ename) bulk collect into lt_emp_rcd
FROM emp;
FOR indx IN 1..lt_emp_rcd.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(lt_emp_rcd(indx).eno||lt_emp_rcd(indx).ename);
END LOOP;
END;
Edit I have tried with cursors, the below code is working fine
DECLARE
CURSOR C1
IS
SELECT emp_no,emp_name FROM maniemp;
C2 C1%ROWTYPE;
LT_EMP_RCD T_EMP;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO C2 ;
SELECT R_EMP_OBJECT(C2.EMP_NO,C2.EMP_NAME) BULK COLLECT INTO LT_EMP_RCD FROM DUAL;
EXIT WHEN C1%NOTFOUND;
FOR INDX IN 1..LT_EMP_RCD.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(LT_EMP_RCD(INDX).ENO||' '||LT_EMP_RCD(INDX).ENAME);
END LOOP;
END LOOP;
CLOSE C1;
END;

Oracle PL/SQL using cursor with for loop for data retrieving

Hi I am trying to use for loop for retrieving data in oracle pl/sql but I am getting an error can someone help me
SET SERVEROUTPUT ON
DECLARE
fname employees.first_name%TYPE;
empid employees.employee_id%TYPE;
CURSOR emp_cursor IS
SELECT employee_id,first_name from employees ORDER BY employee_id;
BEGIN
open emp_cursor;
FOR empid IN emp_cursor loop
DBMS_OUTPUT.PUT_LINE('employee id is ' ||empid || 'first name is '||fname);
end LOOP;
END;
I am getting an error on the DBMS output line something like the number or type of variables not right. I am trying to retrieve employee id and first name from employees table in oracle sample schema.
Can someone please guide me
USING FOR..LOOP in CURSORS.
SET SERVEROUTPUT ON
DECLARE
/* DECLARE Cursor */
CURSOR emp_cursor IS
SELECT employee_id,first_name from employees ORDER BY employee_id;
BEGIN
FOR empid IN emp_cursor loop
/* empid already has the row details, you don't need to have any other variables */
DBMS_OUTPUT.PUT_LINE('employee id is ' ||empid.employee_id || 'first name is '||empid.first_name);
end LOOP;
END;
/
USING OPEN FETCH and CLOSE
SET SERVEROUTPUT ON
DECLARE
fname employees.first_name%TYPE;
empid employees.employee_id%TYPE;
CURSOR emp_cursor IS
SELECT employee_id,first_name from employees ORDER BY employee_id;
BEGIN
open emp_cursor;
/* LOOP until the cursor is empty */
LOOP
FETCH emp_cursor INTO empid,fname;
/* Now we fetch data from cursor, and put it into our variables */
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('employee id is ' ||empid || 'first name is '||fname);
END LOOP;
/* As we OPEN'ed the cursor manually, we have to CLOSE it without fail */
CLOSE emp_cursor;
END;
/

Oracle: Insert rowtype data into another table

I have one table called event, and created another global temp table tmp_event with the same columns and definition with event. Is it possible to insert records in event to tmp_event using this ?
DECLARE
v_record event%rowtype;
BEGIN
Insert into tmp_event values v_record;
END;
There are too many columns in event table, I want to try this because I don't want to list all the columns.
Forget to mention: I will use this in the trigger, can this v_record be the object :new after insert on EVENT table ?
To insert one row-
DECLARE
v_record event%rowtype;
BEGIN
SELECT * INTO v_record from event where rownum=1; --or whatever where clause
Insert into tmp_event values v_record;
END;
Or a more elaborate version to insert all rows from event-
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF event%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS
SELECT *
FROM event;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
-- Process contents of collection here.
Insert into tmp_event values v_record;
END LOOP;
CLOSE c_data;
END;
/
In a trigger, yes it is possible but its like the chicken or the egg. You have to initialize every field of the rowtype with the :new column values like-
v_record.col1 := :new.col1;
v_record.col2 := :new.col2;
v_record.col3 := :new.col3;
....
Apparently, the PLSQL examples above cannot be used in a trigger since it would throw a mutating trigger error. And there is no other way for you to get the entire row in the trigger other than accessing each column separately as I explain above, so if you do all this why not directly use :new.col in the INSERT into temp_event itself, will save you a lot of work.
Also since you say it's a lot of work to mention all the columns, (in Oracle 11gR2) here's a quick way of doing that by generating the INSERT statement and executing it dynamically (although not tested for performance).
CREATE OR REPLACE TRIGGER event_air --air stands for "after insert of row"
AFTER INSERT ON EVENT
FOR EACH ROW
L_query varchar2(2000); --size it appropriately
BEGIN
SELECT 'INSERT INTO tmp_event VALUES ('|| listagg (':new.'||column_name, ',')
WITHIN GROUP (ORDER BY column_name) ||')'
INTO l_query
FROM all_tab_columns
WHERE table_name='EVENT';
EXECUTE IMMEDIATE l_query;
EXCEPTION
WHEN OTHERS THEN
--Meaningful exception handling here
END;
There is a way to insert multiple rows into table with %Rowtype.
checkout below example.
DECLARE
TYPE v_test IS TABLE OF TEST_TAB%rowtype;
v_test_tab v_test ;
EXECUTE immediate ' SELECT * FROM TEST_TAB ' bulk collect INTO v_test_tab ;
dbms_output.put_line('v_test_tab.count -->'||v_test_tab.count);
FOR i IN 1..v_test_tab.count
LOOP
INSERT INTO TEST_TAB_1 VALUES v_test_tab
(i
) ;
END LOOP;
END;
sum up to full working excample ...
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF event%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS SELECT * FROM event;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
FORALL i IN 1..l_tab.count
Insert into tmp_event values l_tab(i);
commit;
END LOOP;
CLOSE c_data;
END;
/

Resources