oracle cursor, concat with loop run figure - oracle

I want to use a cursor to loop through a management reporting chain using the Connect By Prior from Oracle. The order is then reversed so the VP is at the top and the record i started with is at the bottom (A.Corpid).
there's a cursor C1 on top that retrieves corpid following certain criteria
Each Canonical Name returned from that table (say f.i it has 6 rows) i want to store in defined TIER1,TIER2, TIER3 variables.
Somehow i can't figure out how to combine the word 'TIER' with the row number i
DECLARE
cursor c1 is
select distinct corpid, cn from Mytable where Country ='ITA';
master varchar2(50);
Tier1 varchar2(50);
Tier2 varchar2(50);
Tier3 varchar2(50);
Tier4 varchar2(50);
Tier5 varchar2(50);
Tier6 varchar2(50);
Tier7 varchar2(50);
Tier8 varchar2(50);
Begin
for rec in c1
loop
dbms_output.put_line(rec.cn);
DECLARE
Cursor C2 is
SELECT CN FROM Mytable A CONNECT BY PRIOR A.reportsto=A.corpid
START WITH A.corpid=rec.corpid
order by rownum desc;
Begin
open C2;
for i in 1..8 loop
fetch C2 into master;
dbms_output.put_line(master);
'Tier'||to_char(i)) :=master ;
end loop;
end;
if TIER1 is null then
TIER1:='';
end if;
*/ remmed out until the variable assignments work */
-- update mytable set VP_TIER1=TIER1 where corpid=rec.corpid;
end loop;
end;
Oracle complains about the '||'
(ORA-06550: line 33, column 31:
PLS-00103: Encountered the symbol "|" when expecting one of the following:
:= . ( # % ;
)
I've tried as well to concat but that didn't work either
ORA-06550: line 33, column 26:
PLS-00306: wrong number or types of arguments in call to 'CONCAT'

Your concatenation of the literal and variable aren't going to evaluate to the variable TIER1 like you are hoping. Try using arrays instead:
DECLARE
cursor c1
is
select distinct corpid, cn
from Mytable where Country ='ITA';
master varchar2(50);
TYPE Tier_arr_t IS TABLE OF VARCHAR2(50) INDEX BY PLS_INTEGER;
Tier_arr TIER_ARR_T;
Begin
for rec in c1
loop
dbms_output.put_line(rec.cn);
DECLARE
Cursor C2
is
SELECT CN
FROM Mytable A
CONNECT BY PRIOR A.reportsto=A.corpid
START WITH A.corpid=rec.corpid
order by rownum desc;
Begin
open C2;
for i in 1..8
loop
fetch C2 into master;
dbms_output.put_line(master);
Tier_arr(i) :=master ;
end loop;
end;
if TIER1 is null
then
TIER1:='';
end if;
update mytable set VP_TIER1=Tier_arr(1), VP_TIER2=Tier_arr(2) where corpid=rec.corpid;
end loop;
end;
There is also probably a more set-based approach to doing this which would be much preferred, but this should work if it is just a one-time need.

Related

How assigne value from execute immediate into variable

--create table locations_localtab as select * from HR.locations;
SET SERVEROUTPUT ON;
declare
type tLOC_type is table of locations_localtab%rowtype index by binary_integer;
tLOC tLOC_type := tLOC_type();
vPostal_code locations_localtab.postal_code%type;
vCity locations_localtab.city%type;
vLocal varchar2(50);
vSource varchar2(50);
comm long;
begin
select location_id,null,postal_code,city,state_province,country_id
bulk collect into tLOC
from HR.locations;
for ii in (select column_name from (select 'POSTAL_CODE' c1,'CITY' c2, 'COUNTRY_ID' c3 from dual) UNPIVOT (column_name for (name_of_col) in (c1,c2,c3))) loop
for i in 1..tLOC.count loop
comm := 'vSource := tLOC('||i|| ').'||ii.column_name ;
dbms_output.put_line(comm);
execute immediate comm;
-- select city into vLocal from locations_localtab where location_id = tLOC(i).location_id;
dbms_output.put_line('vSOURCE -->'||'.'||vSOURCE);
dbms_output.put_line('vLOCAL -->'||'.'||vLOCAL);
end loop;
end loop;
-- output -->
-- vSource := tLOC(1).POSTAL_CODE;
-- vSource := tLOC(2).POSTAL_CODE;
I would like to make loop by column_name to use this iterator for another loop with table type..
I would like to assign value of tLOC(i).ii.column_name into variable vSource, how can I do this?
I have no idea how I can deal with it.
Thank you in advance for your help.

Put Column Name in Variable and use it in output statement

Here is What i actually wanted to do, Fetch Data From a Table without knowing any columns but i.j.Column_Name gives an error, so i wanted to put it in a variable. After reading the comments i think it's not possible
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
CURSOR C2 IS SELECT Table_Name,Column_Name FROM user_tab_columns
WHERE data_type='VARCHAR2';
v_table Varchar2(256);
v_Col varchar2(200);
BEGIN
FOR i in C1 LOOP
FOR j in (SELECT Column_Name FROM user_tab_columns WHERE
Table_Name='Table_Name') LOOP
dbms_output.put_line(i.j.Column_Name);
END LOOP;
END LOOP;
END;
/
No, There is no Column Named v_Col
You can't refer to a field in a record (which is what the cursor loop is giving you) dynamically. If you need to do flexibly then you can use dbms_sql (possibly adapting this approach), but in the scenario you've shown you could use dynamic SQl to only get the column you want in the cursor:
-- dummy data
create table table_name (id number, column_name varchar2(10), other_col date);
insert into table_name values (1, 'Test 1', sysdate);
insert into table_name values (2, 'Test 2', sysdate);
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
v_Cur sys_refcursor;
v_Col varchar2(200);
v_Val varchar2(4000);
BEGIN
v_Col:= 'Column_Name';
OPEN v_Cur for 'SELECT ' || v_Col || ' FROM Table_Name';
LOOP
FETCH v_Cur INTO v_Val;
EXIT WHEN v_Cur%notfound;
dbms_output.put_line(v_val);
END LOOP;
END;
/
Test 1
Test 2
PL/SQL procedure successfully completed.
The downside of this is that whatever the data type of the target column is, you have to implicitly convert it to a string; but you would be doing that in the dbms_output call anyway. So if you change the column you want to print:
v_Col:= 'Other_Col';
then the output from my dummy data would be:
2018-08-23
2018-08-23
PL/SQL procedure successfully completed.
where the date value is being implicitly formatted as a string using my current NLS session settings.
You could get more advanced by checking the data type in user_tab_columns and changing the dynamic query and/or the fetch and handling, but it isn't clear what you really need to do.

PL/SQL Nested Loops with cursors

I am working with Oracle PL/SQL. There are two cursors, namely c1 and c2.
v_temp VARCHAR(50);
For s1 IN c1
LOOP
--do something
FOR s2 IN c2
LOOP
--do something
v_temp := s1.s2.xxx; --PLS-00302: component 's2' must be declared
END LOOP;
END LOOP;
s2.xxx gives a column name, and with that column name I hope to assign the value of that column from s1 to v_temp.
For example:
In the first iteration, s2.xxx is 'column1',
I would like to assign s1.column1 to v_temp. In the second iteration, s2.xxx is 'column2', I would then like to assign s1.column2 to v_temp.
I got the error:
Error(191,48): PLS-00302: component 's2' must be declared
while trying to compile. I know that s1.s2.xxx is not valid, but is there another way of writing it that can make it work?
You need to fetch from a REF CURSOR and dynamically append the column_name to the select statement while opening the cursor. Here I am fetching all the column names from USER_TAB_COLUMNS for table EMPLOYEES and assigning their corresponding values to v_temp.
SET SERVEROUTPUT ON;
DECLARE
v_temp VARCHAR(50);
query1 VARCHAR2(1000);
c1 SYS_REFCURSOR;
CURSOR c2
IS
SELECT COLUMN_NAME xxx FROM USER_TAB_COLUMNS WHERE TABLE_NAME = 'EMPLOYEES';
BEGIN
FOR s2 IN c2
LOOP
--do something
query1 := 'SELECT ' ||s2.xxx||' FROM EMPLOYEES';
OPEN c1 FOR query1 ;
LOOP
FETCH c1 INTO v_temp;
DBMS_OUTPUT.PUT_LINE('COLUMN:'||s2.xxx||', VALUE:'|| v_temp);
EXIT
WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
END LOOP;
END;
/
Since lengths of all the columns of Employees are < 50 , it is working Fine.The conversion happens implicitly for NUMBER and DATE data types.
Here is a sample Output.
COLUMN:EMPLOYEE_ID, VALUE:100
COLUMN:EMPLOYEE_ID, VALUE:101
COLUMN:EMPLOYEE_ID, VALUE:102
COLUMN:FIRST_NAME, VALUE:Eleni
COLUMN:FIRST_NAME, VALUE:Eleni
COLUMN:LAST_NAME, VALUE:Whalen
COLUMN:LAST_NAME, VALUE:Fay
COLUMN:HIRE_DATE, VALUE:17-06-03
COLUMN:HIRE_DATE, VALUE:21-09-05
I think you need smth like that:
declare
v_temp VARCHAR(50);
v_temp_1 VARCHAR(50);
cursor c2(p VARCHAR) is
SELECT *
FROM tbl
WHERE tbl.column = p;
begin
For s1 IN c1
LOOP
--do something
v_temp_1 := s1.xxx;
FOR s2 IN c2(v_temp_1)
LOOP
--do something
v_temp := s1.xxx;
END LOOP;
END LOOP;
end;

How to declare a cursor after BEGIN?

I want to know if a cursor can be declared after BEGIN.
And how can I export the result of the plsql to an Excel sheet, because I have to run this procedure as a job.
CREATE OR REPLACE PROCEDURE masc(v_amsprogramid VARCHAR2) AS
v_mid VARCHAR2(50);
v_sid VARCHAR2(50);
CURSOR c1 IS
SELECT DISTINCT mid
FROM table_a WHERE aid = v_aid
ORDER BY mid;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_mid;
EXIT WHEN c1%NOTFOUND;
DECLARE
CURSOR c2 IS
SELECT DISTINCT sid INTO v_sid
FROM table_b WHERE mid = v_mid;
BEGIN
OPEN c2;
LOOP
FETCH c1 INTO v_mid;
EXIT WHEN c1%NOTFOUND;
dbms_output.PUT_LINE('MID : ' || v_mid);
dbms_output.PUT_LINE('Sid : ' || v_sid);
END LOOP;
CLOSE c2;
END LOOP;
CLOSE c1;
END masc;
I want to know if i can declare a cursor after begin
Not exactly. But you could use a cursor for loop instead of declaring an explicit cursor.
For example,
FOR i IN (SELECT distinct MID from table_a WHERE AID = V_AID ORDER BY MID)
LOOP
<do something>
END LOOP;
But anyway, this would be slower as row-by-row is slow-by-slow. I don't see a need of procedure at all. If you really need to do it in PL/SQL then consider BULK COLLECT.
And how can i export the result of the plsql to an excel sheet because i ahev to run this procedure as a job.
I don't see a need of PL/SQL in that case. You could simply use SPOOL in SQL*Plus.
For example,
sqlplus user/pass#service_name
<required formatting options>
SPOOL /location/myfile.csv
SELECT distinct MID from table_a WHERE AID = V_AID ORDER BY MID;
SPOOL OFF
Maybe you are looking for this:
create or replace PROCEDURE MASC (V_AMSPROGRAMID VARCHAR2) AS
V_MID VARCHAR2(50);
V_SID VARCHAR2(50);
CURSOR C1 IS
SELECT distinct MID from table_a WHERE AID = V_AID
ORDER BY MID;
CURSOR C2 IS
SELECT DISTINCT SID INTO V_SID FROM table_b WHERE MID = V_MID
ORDER BY MID;
BEGIN
...
or
create or replace PROCEDURE MASC (V_AMSPROGRAMID VARCHAR2) AS
V_MID VARCHAR2(50);
V_SID VARCHAR2(50);
CURSOR C1 IS
SELECT distinct MID from table_a WHERE AID = V_AID
ORDER BY MID;
CURSOR C2(v in NUMBER) IS
SELECT DISTINCT SID INTO V_SID FROM table_b WHERE MID = v
ORDER BY MID;
BEGIN
OPEN C1;
...
OPEN C2(V_MID);
...
U can use reference cursor for this purpose
create or replace PROCEDURE MASC (V_AMSPROGRAMID VARCHAR2) AS
V_MID VARCHAR2(50);
V_SID VARCHAR2(50);
C1 sys_refcursor ;
c2 sys_refcursor ;
BEGIN
OPEN C1 for SELECT distinct MID from table_a WHERE AID = V_AID
ORDER BY MID;
LOOP
FETCH C1 INTO V_MID;
EXIT WHEN C1%NOTFOUND;
open C2 for SELECT DISTINCT SID INTO V_SID FROM table_b WHERE MID = V_MID;
LOOP
FETCH C1 INTO V_MID;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('MID : ' || V_MID);
DBMS_OUTPUT.PUT_LINE('Sid : ' || V_SID);
END LOOP;
CLOSE C2;
CLOSE C1;
END LOOP;
You can declare multiple cursors in the same pl/sql block. There is no need to declare the second cursor after you've opened the first cursor!
You would write something like:
create or replace procedure masc (p_amsprogramid varchar2)
as
v_mid varchar2(50);
v_sid varchar2(50);
cursor c1
is
select distinct mid
from table_a
where aid = p_amsprogramid
order by mid;
cursor c2
is
select distinct sid
from table_b
where mid = v_mid;
begin
open c1;
loop
fetch c1 into v_mid;
exit when c1%notfound;
open c2;
loop
fetch c1 into v_mid;
exit when c1%notfound;
dbms_output.put_line('mid : ' || v_mid);
dbms_output.put_line('sid : ' || v_sid);
end loop;
close c2;
end loop;
close c1;
end masc;
/
However, if you were to replace your open-cursor-loop-fetches as a cursor-for-loop, you could simplify things a bit:
create or replace procedure masc (p_amsprogramid varchar2)
as
cursor c1
is
select distinct mid
from table_a
where aid = p_amsprogramid
order by mid;
cursor c2
is
select distinct sid
from table_b
where mid = v_mid;
begin
for rec1 in c1
loop
for rec2 in c2
loop
dbms_output.put_line('mid : ' || rec1.mid);
dbms_output.put_line('sid : ' || rec2.sid);
end loop;
end loop;
end masc;
/
Looking at that, you've got a nested cursor loop. This screams procedural thinking, rather than set-based thinking, which is pretty much a big no-no when you're working with datasets in the database (ie. it's slow. You're having to constantly switch between the SQL and PL/SQL engines, instead of simply asking the SQL engine to calculate everything before delivering it to the PL/SQL engine).
By doing the nested cursor loop, you're basically reinventing NESTED LOOP joins - something the SQL engine can do far better than you can (not to mention it might not be the most efficient join, and the SQL engine could choose a better way of doing the join!). Any time you see a nested cursor loop, you should IMMEDIATELY stop and look to see if you can combine the queries into a single select statement. (Actually, any time you see a loop you should pause and consider whether you really do need it; sometimes it's necessary, but if you're doing something like selecting a set of results and then going through each row and then doing an update, consider merging the select into the update so that you have a statement that updates all the rows at once. It'll be much faster!)
For example, your original procedure could be rewritten as:
create or replace procedure masc (p_amsprogramid varchar2)
as
cursor c1
is
select distinct a.mid,
b.sid
from table_a a
inner join table_b b on (a.mid = b.mid)
where a.aid = p_amsprogramid
order by mid;
begin
for rec1 in c1
loop
dbms_output.put_line('mid : ' || rec1.mid);
dbms_output.put_line('sid : ' || rec1.sid);
end loop;
end masc;
/
Much simpler to read, understand and maintain, I think you'll agree!
If you're wanting to write the results of the sql query out as a file, you'll need to use UTL_FILE, instead of DBMS_OUTPUT. Bear in mind that the directory the file is written to needs to be something that is mounted/mapped to the server the database sits on. If you write the results as character-delimited, you can then easily import that file into Excel.
You might find this to be of use.

How to fetch the cursor data in oracle stored procedure

create or replace
PROCEDURE get_new
AS
CUST_ID varchar2(100);
ROUTERNAME_N VARCHAR2(100);
BEGIN
CURSOR c1 IS
SELECT TRAFFIC_CUST_ID,ROUTERNAME INTO CUST_ID,ROUTERNAME_N
FROM INTERFACE_ATTLAS
WHERE rownum > 3;
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
LOOP
FETCH c1 INTO my_ename;
FETCH c1 INTO my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
end;
I am new to oracle and stored procedure. I am trying to get the rows using cursor fetch, and getting following error:
PLS-00103: Encountered the symbol "C1" when expecting one of the following:
:= . ( # % ;
Rewrite it like this:
create or replace
PROCEDURE get_new
AS
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
CURSOR c1 IS
SELECT TRAFFIC_CUST_ID,ROUTERNAME
FROM INTERFACE_ATTLAS
WHERE rownum > 3;
BEGIN
open c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
close c1;
end;
Do not forget to open and close cursors. It always will print nothing because of rownum > 3; You wanted to type: rownum < 3;, didn't you?
create or replace
PROCEDURE get_new
AS
CUST_ID varchar2(100);
ROUTERNAME_N VARCHAR2(100);
BEGIN
CURSOR c1 IS
SELECT TRAFFIC_CUST_ID,ROUTERNAME INTO CUST_ID,ROUTERNAME_N
FROM INTERFACE_ATTLAS
WHERE rownum > 3;
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
LOOP
FETCH c1 INTO my_ename;
FETCH c1 INTO my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
end;
Cursor should be declare in the declaration part. Then have to open in begin-end section. In the declaration section you can not assign value to variable. When you are fetching value you can not select randomly value form cursor,
modified code:
create or replace
PROCEDURE get_new
AS
no mean --CUST_ID varchar2(100);
no means -- ROUTERNAME_N VARCHAR2(100);
CURSOR c1 IS
SELECT deptno,job
FROM emp;
my_ename emp.deptno%TYPE;
my_salary emp.job%TYPE;
BEGIN
open c1;
LOOP
fetch c1 into my_ename,my_salary;
-- FETCH c1 INTO my_ename;
--FETCH c1 INTO my_salary;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(my_ename);
end loop;
end;
execute get_new.
You should probably declare your cursor and your variables my_ename and my_salary in the dedicated section, i.e. before the BEGIN, and then open your cursor:
IS
CUST_ID varchar2(100);
ROUTERNAME_N VARCHAR2(100);
C1 sys_refcursor;
my_ename INTERFACE_ATTLAS.TRAFFIC_CUST_ID%TYPE;
my_salary INTERFACE_ATTLAS.ROUTERNAME%TYPE;
BEGIN
OPEN C1 for
SELECT ...
You would have to declare the Cursor before BEGIN. You would use no INTO clause in the cursor declaration. Then you would have to OPEN the cursor. Then you would FETCH INTO my_ename, my_salary, not one after the other (you fetch rows, not columns). WHERE rownum > 3 returns no rows. As you don't want a first row, you will never get a second, third and fourth either.
And you can use an implicit cursor which is easier to deal with (no need to open, fetch and close explicitely):
BEGIN
FOR rec IN
(
select traffic_cust_id, routername
from interface_attlas
where rownum <= 3
) LOOP
DBMS_OUTPUT.PUT_LINE(rec.traffic_cust_id || ': ' || rec.salary);
END LOOP;
END;

Resources