I have two tables employee and employees
employee
EMPID FIRSTNAME LASTNAME SALARY DEPARTMENT CREDENTIALS
1 ANITHA SHARMA 30000 ASSURANCE
2 EZIO AUDITORE 50000 ASSURANCE
3 CONNOR KENWAY 20000 SUPPORT
4 SAMANTHA JO 25000 ASSURANCE
5 EDWARD MILES 52000 ITIS
employees
EMP_ID EMP_NAME EMP_AGE EMP_SALARY BATHCH
1 charan 20 30000
0 0 0
1 goku 32 1223
2 gokul 24 100000
when i tried to write a pl/sql code to print emp_name,firstname,salary from the above two tables, it prints the records twice.The pl/sql code is
declare
cursor c1 is select * from employee,employees where employee.salary=employees.emp_salary;
c2 c1%rowtype;
begin
open c1;
loop
fetch c1 into c2;
dbms_output.put_line(c2.firstname||' '||c2.emp_name||' '||c2.salary);
exit when c1%notfound;
end loop;
end;
/
I am getting the output as
ANITHA charan 30000
ANITHA charan 30000
Instead of this,
declare
cursor c1 is select * from employee,employees where employee.salary=employees.emp_salary;
c2 c1%rowtype;
begin
open c1;
loop
fetch c1 into c2;
dbms_output.put_line(c2.firstname||' '||c2.emp_name||' '||c2.salary);
exit when c1%notfound;
end loop;
end;
/
Use this,
declare
cursor c1 is select * from employee,employees where employee.salary=employees.emp_salary;
c2 c1%rowtype;
begin
open c1;
loop
fetch c1 into c2;
exit when c1%notfound;
dbms_output.put_line(c2.firstname||' '||c2.emp_name||' '||c2.salary);
end loop;
end;
/
Move the exit when clause after fetch. In your case, the dbms_output.put_line is getting executed twice. For the second iteration, c2 would still hold previous values even when not found condition is true.
You should always use exit when after fetch unless you have a requirement not to do so.
declare
cursor c1 is select * from employee,employees where employee.salary=employees.emp_salary;
c2 c1%rowtype;
begin
open c1;
loop
fetch c1 into c2;
exit when c1%notfound;
dbms_output.put_line(c2.firstname||' '||c2.emp_name||' '||c2.salary);
end loop;
end;
/
Related
I'm a novice at PL/SQL. I have attempted various approaches to use a Cursor to insert into a temp table depending on whether or not the value already exists in the temp table. I either get too many rows or nothing is inserted.
This is my last pseudocode approach and is the bare essence of what I'm attempt to accomplish:
DB: Oracle 12
Using SQL Developer
Goal: Take duplicate accountno info from table1 and merge / combine into single row in temptable
1. Add initial accountno info if it doesn’t already exists in temptable
2. If accountno exists in temptable add the additional info to accountno row
Suggestions are greatly appreciated.
Pseudocode
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN EXIT; END IF;
select accountno INTO v_cnt from table1 where Exists(select 1 from temptable where accountno <> c1d.accountno);
IF v_cnt is NULL THEN
INSERT INTO temptable (accountno)
values(c1d.accountno);
END IF;
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
If you strictly want to correct your pseudo code, You may try -
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN
EXIT;
END IF;
BEGIN
SELECT accountno
INTO V_cnt
FROM temptable
WHERE accountno = c1d.accountno
AND ROWNUM = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_cnt := NULL;
END;
IF V_cnt is NULL THEN
INSERT INTO temptable (accountno)
values(c1d.accountno);
END IF;
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
I would strongly recommend to use below pseudo code -
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN
EXIT;
END IF;
MERGE INTO temptable
USING table1
ON (accountno = c1d.accountno)
WHEN NOT MATCHED THEN
INSERT (accountno)
values(c1d.accountno);
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
create or replace procedure p2
as
CURSOR c1 IS
SELECT salary
FROM employee1
FOR UPDATE;
BEGIN
FOR employee_rec IN c1 LOOP
exit when c1%notfound;
if salary>20000 then
update employee1 set grade='A' WHERE CURRENT OF c1;
if salary>15000 then
update employee1 set grade='B' WHERE CURRENT OF c1;
if salary>10000 then
update employee1 set grade='C' WHERE CURRENT OF c1;
if salary<10000 then
update employee1 set grade='D' WHERE CURRENT OF c1;
end if;
end if;
end if;
end if;
END LOOP;
END p2;
An error shows up as salary is undeclared How can I make this code better to get the desired output???
Why not simply like this:
update employee1 set grade=
case
when salary>20000 then 'A'
when salary>15000 then 'B'
when salary>10000 then 'C'
when salary<10000 then 'D'
ELSE grade -- keep existing grade value
end;
Your error is because there is no variable salary. You have a cursor called employee_rec which contains a column called salary but you would need to reference that using employee_rec.salary.
If we fix that (and indent your code) then it compiles:
CREATE PROCEDURE p2 AS
CURSOR c1 IS
SELECT salary
FROM employee1
FOR UPDATE;
BEGIN
FOR employee_rec IN c1 LOOP
EXIT WHEN c1%NOTFOUND;
IF employee_rec.salary>20000 THEN
update employee1 set grade='A' WHERE CURRENT OF c1;
IF employee_rec.salary>15000 THEN
update employee1 set grade='B' WHERE CURRENT OF c1;
IF employee_rec.salary>10000 THEN
update employee1 set grade='C' WHERE CURRENT OF c1;
IF employee_rec.salary<10000 THEN
update employee1 set grade='D' WHERE CURRENT OF c1;
END IF;
END IF;
END IF;
END IF;
END LOOP;
END p2;
/
However, it isn't going to give you the output you want due to the nested IF statements. If you replace the nested IF statements with ELSIF:
CREATE OR REPLACE PROCEDURE p2 AS
CURSOR c1 IS
SELECT salary
FROM employee1
FOR UPDATE;
BEGIN
FOR employee_rec IN c1 LOOP
EXIT WHEN c1%NOTFOUND;
IF employee_rec.salary>20000 THEN
update employee1 set grade='A' WHERE CURRENT OF c1;
ELSIF employee_rec.salary>15000 THEN
update employee1 set grade='B' WHERE CURRENT OF c1;
ELSIF employee_rec.salary>10000 THEN
update employee1 set grade='C' WHERE CURRENT OF c1;
ELSIF employee_rec.salary<10000 THEN
update employee1 set grade='D' WHERE CURRENT OF c1;
END IF;
END LOOP;
END p2;
/
Then your procedure works (except when the salary is 10,000).
However, you could still make it more efficient by getting rid of the cursor:
CREATE OR REPLACE PROCEDURE p2 AS
BEGIN
UPDATE employee1
SET grade = CASE
WHEN salary > 20000 THEN 'A'
WHEN salary > 15000 THEN 'B'
WHEN salary > 10000 THEN 'C'
ELSE 'D'
END;
END p2;
/
db<>fiddle
It is not
if salary>20000 then
but
if employee_rec.salary>20000 then
Code you wrote can be simplified - up to a single UPDATE statement. However, if you're using PL/SQL for learning, CASE is probably a better option than nested IFs. Something like this example based on subset of Scott's EMP table.
Test data:
SQL> create table test as
2 select empno, ename, sal, 'x' grade
3 from emp
4 where deptno = 10;
Table created.
SQL> select * from test order by sal desc;
EMPNO ENAME SAL G
---------- ---------- ---------- -
7839 KING 5000 x
7782 CLARK 2450 x
7934 MILLER 1300 x
Procedure, testing & result:
SQL> create or replace procedure p2 as
2 cursor c1 is select sal from test for update;
3 begin
4 for employee_rec in c1 loop
5 update test set
6 grade = case when employee_rec.sal > 3000 then 'A'
7 when employee_rec.sal > 2000 then 'B'
8 when employee_rec.sal > 1000 then 'C'
9 when employee_rec.sal <= 1000 then 'D'
10 end
11 where current of c1;
12 end loop;
13 end;
14 /
Procedure created.
SQL> exec p2;
PL/SQL procedure successfully completed.
SQL> select * From test order by sal desc;
EMPNO ENAME SAL G
---------- ---------- ---------- -
7839 KING 5000 A
7782 CLARK 2450 B
7934 MILLER 1300 C
SQL>
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;
create or replace PROCEDURE template2(
template_id_in IN RTEMPLATE_CONFIGURE.TEMPLATE_ID%TYPE)
AS
source_table rtemplate_configure.sobject_name%type;
source_column rtemplate_configure.scolumn_name%type;
target_table rtemplate_configure.tobject_name%type;
target_column rtemplate_configure.tcolumn_name%type;
tmp VARCHAR2(2000);
tmp2 VARCHAR2(2000);
CURSOR c_template_configure is
SELECT * FROM rtemplate_configure WHERE template_id = template_id_in order by source_table, target_table;
BEGIN
FOR record_line in c_template_configure LOOP
FOR record_line2 in c_template_configure LOOP
IF record_line.sobject_name = record_line2.sobject_name
and record_line.tobject_name = record_line2.tobject_name
and record_line.tcolumn_name <> record_line2.tcolumn_name
and record_line.scolumn_name <> record_line2.scolumn_name
THEN
tmp2 := 'INSERT INTO '||record_line.tobject_name||'('||record_line.tcolumn_name||','||record_line2.tcolumn_name||')'||'
SELECT '||record_line.scolumn_name||','||record_line.scolumn_name||'
FROM '||record_line.sobject_name||'';
DBMS_OUTPUT.put_line
(tmp2);
END IF;
END LOOP;
END LOOP;
--COMMIT;
END template2;
I am getting error: PL/SQL: cursor already open, and I closed it properly, I guess? I am not sure if I used for loops properly as well, I just need that one cursor to go through the nested loops to check the data as seen in if statement.
You have opened cursor c_template_configure twice. You can't do that, you'll need to create a copy e.g. c_template_configure2.
Here is a very simple example of what you have done:
SQL> declare
2 cursor c is select * from emp;
3 begin
4 for r1 in c loop -- Open cursor c once
5 for r2 in c loop -- Open cursor c again, already open
6 null;
7 end loop;
8 end loop;
9 end;
10 /
declare
*
ERROR at line 1:
ORA-06511: PL/SQL: cursor already open
ORA-06512: at line 2
ORA-06512: at line 5
Now here is the corrected code:
1 declare
2 cursor c1 is select * from emp;
3 cursor c2 is select * from emp;
4 begin
5 for r1 in c1 loop
6 for r2 in c2 loop
7 null;
8 end loop;
9 end loop;
10* end;
SQL> /
Aside: If processing a lot of data, this is a very inefficient approach. Consider joining the data in the query for example:
select e1.empno as empno1, e2.empno as empno2
from emp e1
cross join emp e2
where e1.empno != e2.empno;
Now you only have 1 cursor to open and it returns all the pairs of employees.
declare
cursor c1 is
select *from emp;
r1 c1%rowtype;
begin
open c1;
fetch c1 into r1;
close c1;
null;
commit;
end;
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;