program to write values and the raise - plsql

I have to write program that generates file, which lines are of the next content:
EMPLOYEE_ID, OLD_SALARY, NEW_SALARY
For that I wrote a procedure that raises salaries for a given department, and program that generates that file. Now, my procedure is working just fine and when I compile a program it doesn't have any errors. But the problem is that it works wrong. When I execute it, I get this
108 12008 12008
109 9000 9000
110 8200 8200
111 7700 7700
112 7800 7800
113 6900 6900
OLD_SALARY column and NEW_SALARY column are same, and it didn't raise them at all. After second executing, columns are still same but the salaries are like 4 time after first executing.
I think the problem is in my cursor but I don't know how to solve it. Here is the entire code:
CREATE OR REPLACE DIRECTORY dir_vezba AS '/home/oracle/vezba';
CREATE OR REPLACE PROCEDURE raise_salary
IS
v_deptno NUMBER:=100;
v_per NUMBER:=0.2;
BEGIN
UPDATE employees
SET salary=salary*(1+v_per)
WHERE department_id=v_deptno;
UPDATE employees
SET salary=salary*1.05
WHERE department_id=v_deptno AND employee_id IN (SELECT e.employee_id
FROM employees e JOIN employees m
ON (e.manager_id=m.employee_id));
END raise_salary;
/
SET VERIFY OFF
DECLARE
v_deptno NUMBER;
v_oldsalary employees.salary%TYPE;
v_newsalary employees.salary%TYPE;
v_filehandle UTL_FILE.FILE_TYPE;
v_body VARCHAR2(200);
v_line VARCHAR2(200);
v_head VARCHAR2(200);
v_report VARCHAR2(150);
CURSOR emp_cursor IS
SELECT employee_id, salary
FROM employees
WHERE department_id=v_deptno;
BEGIN
v_deptno:=extract_dep('POVECANJE_DEP.txt');
v_report:='IZVESTAJ'||TO_CHAR(SYSDATE, 'YY')||TO_CHAR(v_deptno)||'.log';
v_filehandle:=UTL_FILE.FOPEN('DIR_VEZBA', v_report, 'w');
UTL_FILE.PUTF(v_filehandle, 'Report generated on: '||SYSDATE||' for department: '||v_deptno);
UTL_FILE.NEW_LINE(v_filehandle);
UTL_FILE.NEW_LINE(v_filehandle);
v_head:='EMPNO OLD_SALARY NEW_SALARY';
UTL_FILE.PUTF(v_filehandle, v_head);
UTL_FILE.NEW_LINE(v_filehandle);
v_line:='=================================';
UTL_FILE.PUTF(v_filehandle, v_line);
UTL_FILE.NEW_LINE(v_filehandle);
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary;
v_newsalary:=emp_rec.salary;
v_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(v_filehandle, v_body);
UTL_FILE.NEW_LINE(v_filehandle);
END LOOP;
UTL_FILE.FCLOSE(v_filehandle);
END;
/

The values in emp_rec are fetched before you call raise_salary, and won't be affected by the UPDATEs in raise_salary. To get the new value for the salary you'll need to fetch it from the database again. Try changing the main loop as follows:
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary;
SELECT SALARY
INTO v_newsalary
FROM EMPLOYEES
WHERE EMPLOYEE_ID = emprec.EMPLOYEE_ID;
v_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(v_filehandle, v_body);
UTL_FILE.NEW_LINE(v_filehandle);
END LOOP;
Share and enjoy.

Related

PLSQL_trigger cannot be complied

I need to create a trigger that when the last employee of the dept is deleted from emp table, the dept is deleted from the dept table.
emp(empno, ename, deptno)
dept(deptno, dname)
First, I created a procedure that deletes the dept from dept table given a deptno.
CREATE OR REPLACE PROCEDURE del_dept
(v_dno in number)
is
begin
delete from DEPT where deptno = v_dno;
end;
Then I created a trigger that deletes the dept when the last emp in that dept is deleted. I tried to test the trigger by deleting one of the three emps in deptno10, but I got error mesg from command that trigger is invalid and failed re-validation.
create or replace trigger del_dept
after delete on EMP
for each row
DECLARE
emp_count Number;
g_dno Number;
begin
SELECT COUNT(:old.ename) INTO emp_count FROM emp group by deptno;
FOR i IN 1.. emp_count LOOP
IF i = emp_count THEN
del_dept(g_dno);
end if;
END LOOP;
End;
Error trigger is invalid and failed re-validation specifies that yor trigger was created with compilation error hence you were not able to test it. This is not a good idea to write a trigger in your case. I can see few issues in you code. So lets first resolve these issues.
When you write the code below, your trigger would be compiled and would be a valid one. So you will not get the error trigger is invalid and failed re-validation
CREATE OR REPLACE TRIGGER del_dept
AFTER DELETE
ON EMP
FOR EACH ROW
DECLARE
EMP_CNT NUMBER;
BEGIN
SELECT COUNT (1)
INTO emp_cnt
FROM emp
WHERE deptno = :old.deptno;
IF emp_cnt = 0
THEN
---del_dept is your procedure to delete records from dept table
del_dept (:old.deptno);
END IF;
END;
But when you would try to execute it you will further get the error:
ORA-04091: table EMP is mutating, trigger/function may not see it
ORA-06512: at "DEL_DEPT", line 4 ORA-04088: error during execution of
trigger 'DEL_DEPT'
The reason for getting this error is you are trying to select from the table from which you are deleting records and thats not allowed in oracle.
The best way for your case is to write a procedure which would delete the records once the last employee data is deleted from your dept table . See below:
CREATE OR REPLACE PROCEDURE del_dept (dept_no NUMBER)
IS
EMP_CNT NUMBER;
BEGIN
SELECT COUNT (1)
INTO emp_cnt
FROM emp
WHERE deptno = dept_no;
IF emp_cnt = 0
THEN
del_dept (dept_no);
END IF;
END;

PL/SQL Cursor just check if value is greater than a value

I want
to make a procedure check if exist empno from table employees
with emno greater than 100. If exist at least one, i want
to exit from the loop.
How can I modify the following code ?
Is it problem I don;t use %NOTFOUND , %ROWCOUNT ?
CREATE OR REPLACE procedure check_value
IS
cursor c1 is
select *
from employess;
c1_values c1%ROWTYPE;
BEGIN
open c1;
fetch c1 into c1_values;
loop
if c1_values.EMPNO > 100 then
DBMS_OUTPUT.put_line ('Found row with empno > 100');
end if;
end loop;
close c1;
END;
If you just need to check if you have a record which has empno over 100 you can use EXISTS statement e.g.
DECLARE
CURSOR c1 IS
SELECT
CASE
WHEN EXISTS (
SELECT
1
FROM
employees
WHERE
empno > 100
) THEN 1
ELSE 0
END AS empno_exists
FROM
dual;
ln_empno_exists PLS_INTEGER;
BEGIN
OPEN c1;
FETCH c1 INTO ln_empno_exists;
CLOSE c1;
DBMS_OUTPUT.PUT_LINE('Empno over 100 exists: '||CASE WHEN ln_empno_exists = 1 THEN 'TRUE' ELSE 'FALSE' END);
END;
/
EDIT: If you want to fetch the rows with some conditions, you simply need to adjust your WHERE clause in your SELECT statement. Here you have an example with some ways to iterate through the records fetched:
DECLARE
CURSOR c1 IS
SELECT
emp.*
FROM
employees emp
WHERE
empno > 100
;
lr_c1_rec c1%ROWTYPE;
BEGIN
-- Using FOR loop
DBMS_OUTPUT.PUT_LINE('START: Printing employees records where empno > 100');
FOR rec IN c1
LOOP
DBMS_OUTPUT.PUT_LINE('empno = '||rec.empno);
END LOOP;
DBMS_OUTPUT.PUT_LINE('END: Printing employees records where empno > 100');
-- Using a LOOP with EXIT clause
DBMS_OUTPUT.PUT_LINE('START: Printing employees records where empno > 100');
OPEN c1;
LOOP
FETCH c1 INTO lr_c1_rec;
-- exit the loop when your cursor doesn't have any more records to be returned
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('empno = '||lr_c1_rec.empno);
END LOOP;
DBMS_OUTPUT.PUT_LINE('END: Printing employees records where empno > 100');
-- Using WHILE loop
DBMS_OUTPUT.PUT_LINE('START: Printing employees records where empno > 100');
OPEN c1;
FETCH c1 INTO lr_c1_rec;
-- As long as cursor returns any values, iterate through the records returned
WHILE c1%FOUND
LOOP
DBMS_OUTPUT.PUT_LINE('empno = '||lr_c1_rec.empno);
END LOOP;
DBMS_OUTPUT.PUT_LINE('END: Printing employees records where empno > 100');
END;
/
Just add an EXIT statement after you've found your value:
CREATE OR REPLACE procedure check_value IS
cursor c1 is
select *
from employess;
c1_values c1%ROWTYPE;
BEGIN
open c1;
loop
fetch c1 into c1_values;
IF c1%NOTFOUND THEN EXIT;
if c1_values.EMPNO > 100 then
DBMS_OUTPUT.put_line ('Found row with empno > 100');
EXIT;
end if;
end loop;
-- The EXIT statement will drop you out of the loop and leave you here
close c1;
END;
Note that I moved the fetch inside the loop and added a %NOTFOUND check. Without the %NOTFOUND the code would never know the cursor was out of data and would probably go into an infinite loop.
Why to define cursor and then open and fetch when it can be done by just
SQL But if it is required so then use FOR loop and EXIT condition
as mentioned
below. Hope it helps
CREATE OR REPLACE PROCEDURE check_value
IS
BEGIN
FOR I IN
(SELECT * FROM employess
)
LOOP
IF I.EMPNO > 5 THEN
DBMS_OUTPUT.put_line ('Found row with empno > 100');
EXIT;
END IF;
END LOOP;
END;

PL/SQL error - invalid file ID

Need help with code and error. After executing this code:
CREATE OR REPLACE DIRECTORY dir_vezba AS '/home/oracle/vezba';
CREATE OR REPLACE PROCEDURE raise_salary
(p_deptno IN NUMBER, p_per IN NUMBER)
...
END raise_salary;
/
SET VERIFY OFF
DECLARE
v_deptno NUMBER;
v_per NUMBER;
v_oldsalary employees.salary%TYPE;
v_newsalary employees.salary%TYPE;
file_handle UTL_FILE.FILE_TYPE;
f_handle UTL_FILE.FILE_TYPE;
file_line VARCHAR2(200);
f_body VARCHAR2(200);
f_line VARCHAR2(200);
f_head VARCHAR2(200);
file_report VARCHAR2(150);
CURSOR emp_cursor IS
SELECT employee_id, salary
FROM employees
WHERE department_id=v_deptno;
BEGIN
f_handle:=UTL_FILE.FOPEN('DIR_VEZBA','POVECANJE_DEP.txt','r');
LOOP
BEGIN
UTL_FILE.GET_LINE(f_handle, file_line);
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
v_deptno:=TO_NUMBER(SUBSTR(file_line,1,3));
v_per:=TO_NUMBER(SUBSTR(file_line,4,3))/1000;
file_report:='IZVESTAJ'||TO_CHAR(SYSDATE, 'YY')||TO_CHAR(v_deptno)||'.log';
file_handle:=UTL_FILE.FOPEN('DIR_VEZBA', file_report, 'w');
UTL_FILE.PUTF(file_handle, 'Report generated on: '||SYSDATE||' for department: '||v_deptno);
UTL_FILE.NEW_LINE(file_handle);
UTL_FILE.NEW_LINE(file_handle);
f_head:='EMPNO OLD_SALARY NEW_SALARY';
UTL_FILE.PUTF(file_handle, f_head);
UTL_FILE.NEW_LINE(file_handle);
f_line:='=================================';
UTL_FILE.PUTF(file_handle, f_line);
UTL_FILE.NEW_LINE(file_handle);
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary(v_deptno, v_per);
SELECT salary
INTO v_newsalary
FROM employees
WHERE employee_id=emp_rec.employee_id;
f_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(file_handle, f_body);
UTL_FILE.FCLOSE(file_handle);
END LOOP;
END LOOP;
UTL_FILE.FCLOSE(f_handle);
EXCEPTION
WHEN UTL_FILE.INVALID_OPERATION THEN
file_report:='IZVESTAJ'||TO_CHAR(SYSDATE, 'YY')||'.bad';
file_handle:=UTL_FILE.FOPEN('DIR_VEZBA', file_report, 'w');
UTL_FILE.PUTF(file_handle, 'NO FILE FOUND');
UTL_FILE.FCLOSE(file_handle);
END;
/
I get this error:
ORA-29282: invalid file ID
ORA-06512: at "SYS.UTL_FILE", line 1071
ORA-06512: at line 48
Don't know what's the problem with this and what does it means. Besides that, in output I get only one line txt file and nothing else. It should get me 3 text fles with different number of lines in them, depending on the data in tables.
This is your problem:
FOR emp_rec IN emp_cursor
LOOP
v_oldsalary:=emp_rec.salary;
raise_salary(v_deptno, v_per);
SELECT salary
INTO v_newsalary
FROM employees
WHERE employee_id=emp_rec.employee_id;
f_body:=RPAD(emp_rec.employee_id,9,' ')||RPAD(v_oldsalary,14,' ')||RPAD(v_newsalary,10,' ');
UTL_FILE.PUTF(file_handle, f_body);
----------> UTL_FILE.FCLOSE(file_handle); <-----------
END LOOP;
You close the file while in the loop, so at the next iteration the PUTF will fail.
It has been my experience that the directory name must be in ALL CAPS, but the filename must be all lower case. I know it sounds stupid, and it probably is, but if no other answer has solved your problem, what have you got to lose by trying this?

Get two records from PLSQL cursor

My question is short. I create a cursor to get some values from my table. I want to get the current record of cursor (the fetched record) and the next record from the cursor without fetching it, because I want to make a calculation over the current record and the next record. In traditional programming it's a simple operation; you can do it with a for index by increasing it by 1.
Do you have any suggestions?
I want to make a calculation over the
current record and the next record.
Presuming you are using Oracle, this can be done quite simply in SQL using analtyical functions, specifically the lead() function. This retrieves a column's value from the next nth record (default n=1).
SQL> select empno
2 , sal as this_sal
3 , lead(sal) over (order by empno) as next_sal
4 from emp
5 order by empno
6 /
EMPNO THIS_SAL NEXT_SAL
---------- ---------- ----------
7369 800 1600
7499 1600 1250
7521 1250 2975
7566 2975 1250
7654 1250 2850
7698 2850 2450
....
This query can be using in a cursor or any other mechanism for retrieving records.
I'm not sure you can do that without moving the cursor, but you should be able to accomplish the same goal like this (psuedocode):
open cursor;
fetch cursor to rec1;
if cursor%found then
loop
fetch cursor to rec2;
exit when cursor%notfound;
perform calculations with rec1 and rec2;
rec1 := rec2;
end loop;
end if;
To answer you specific question, you could use BULK COLLECT and its limit clause into a collection (table) variable.
DECLARE
CURSOR my_cursor IS
[YOUR SQL HERE];
TYPE t_my_type IS TABLE OF my_cursor%ROWTYPE INDEX BY BINARY_INTEGER;
v_my_var t_my_type;
BEGIN
FETCH my_cursor BULK COLLECT INTO v_my_var LIMIT 2;
dbms_output.put_line('My first value: ' || v_my_var(1).some_column);
dbms_output.put_line('My second value: ' || v_my_var(2).some_column);
END;
The limit clause will cause only the first two records to be fetched and stored. The first record will have an index of 1 and the second will be 2.
You can compare two variables by saving one into local and then compare it with cursor value for example
declare
l_local1 table_name%type := null;
begin
for c1 in (select
*
from
table_name
where some clause)
loop
-- do comparation
if l_local1 is not null and c1.field_value is not null then
-- do something to compare
.
.
.
-- empty local1 variable
l_local1 := null;
end if;
-- get the value of loop in local variable
l_local1 := c1.field_value;
end loop;
end;

plsql cursor iterating problem

i use oracle demo schema scott to do some plsql test ( the data in that schema are never changed ). i wrote the following program to get the employee number of each department. the problem is, there is just 4 departments but my program output 5 row. i can't find out the reason, anyone can help? great thanks.
declare
cursor employees(department_id number) is
select count(*) howmany
from scott.emp
where deptno=department_id;
employees_per_dept employees%rowtype;
cursor departments is
select *
from scott.dept;
a_department departments%rowtype;
begin
dbms_output.put_line('-----------------------------------');
open departments;
loop
exit when departments%notfound;
fetch departments into a_department;
open employees(a_department.deptno);
fetch employees into employees_per_dept;
dbms_output.put_line(employees_per_dept.howmany);
close employees;
end loop;
close departments;
dbms_output.put_line('-----------------------------------');
end;
If you output the deptno in the dbms_output you'll see the reason.
You need to switch these two lines:
fetch departments into a_department;
exit when departments%notfound;
%NOTFOUND is meaningless before the initial FETCH; your code was counting the emps in the last dept twice.
declare
cursor cl(ccode varchar2) is
Select * from employees where department_id=ccode;
z cl%rowtype;
cnt number:=0;
begin
Open cl('90');
fetch cl into Z;
while (cl%found) loop
dbms_output.put_line ( 'nsme is ' || z.last_name);
fetch cl into Z;
cnt := cnt +1;
end loop;
dbms_output.put_line (cnt);
close cl;
end;

Resources