My script needs to do the following
if the dept is 10 and the salary is greater than 2000 give a 6% raise otherwise give a 7% raise. IF dept is 20 and the salary is greater than 2500 give a 5% raise otherwise give a 5.5% raise.
but the program is not running through all my if statements it is stopping at the first if statement and I am unsure of any other logic I can use to make it run through all the if statements.
SET SERVEROUTPUT ON
DECLARE
v_newsal emp.sal%TYPE;
v_sal emp.sal%TYPE;
v_deptno emp.deptno%TYPE;
CURSOR raise_cursor IS
SELECT sal, deptno
FROM emp;
BEGIN
OPEN raise_cursor;
fetch raise_cursor INTO v_sal, v_deptno;
LOOP
IF v_deptno = 10 AND v_sal > 2000 THEN
v_newsal := v_sal * 1.060;
ELSE
v_newsal := v_sal * 1.070;
IF v_deptno = 20 AND v_sal > 2500 THEN
v_newsal := v_sal * 1.050;
ELSE
v_newsal := v_sal * 1.055;
END IF;
END IF;
UPDATE emp SET
sal = v_newsal
WHERE deptno = v_deptno;
EXIT;
END LOOP;
CLOSE raise_cursor;
END;
/
SET SERVEROUTPUT OFF
I have tried elsif statements and nested if statements and just regular if else statements but none seem to work.
The issue is that you are opening the cursor, fetching a row, and then starting a loop, which you exit at the end of the first loop through.
The usual way of handling this type of cursor is: open the cursor, start the loop, fetch a row, exit the loop if a row wasn't found, or continue with the rest of your logic if a row was found, then go back to the start of the loop and repeat.
I'm assuming that your procedure is part of a learning exercise for dealing with cursors, because in real life, you'd be best off handling the logic in a single update statement like #Littlefoot suggested in their answer.
On that basis, your code should look like:
<snip>
OPEN raise_cursor;
LOOP
fetch raise_cursor INTO v_sal, v_deptno;
EXIT WHEN raise_cursor%NOTFOUND;
<your logic>
END LOOP;
CLOSE raise_cursor;
<snip>
N.B. a cursor for loop (for <record> in <cursor> loop <logic> end loop;) is simpler and clearer to write, and also has inbuilt optimisations to convert from row-by-row processing to bulk row processing. It handles the opening, fetching and exiting the cursor for you, so you don't need to worry about it. However, as I've said, I'm assuming you're learning how to explicitly handle opening, looping, etc around a cursor.
Also, looking at the logic of your if statements, it doesn't make sense:
if the dept is 10 and the salary is greater than 2000 give a 6% raise otherwise give a 7% raise. IF dept is 20 and the salary is greater than 2500 give a 5% raise otherwise give a 5.5% raise.
Is that first "else" clause relating to just dept 10? And the same with the dept 20? Your current logic means that if you're in dept 20 with a lower than 2500 salary, or in any other dept, you'd get the 6% raise from the first query and then the 5.5% raise on top of that (sweet deal for the employees, not so much for the employer!).
If my understanding of the logic is correct, it should be something like:
IF v_deptno = 10 AND v_sal > 2000 THEN
v_newsal := v_sal * 1.060;
ELSIF v_deptno = 10 THEN
v_newsal := v_sal * 1.070;
ELSIF v_deptno = 20 AND v_sal > 2500 THEN
v_newsal := v_sal * 1.050;
ELSIF v_deptno = 20 THEN -- maybe this clause should remain as the `ELSE` clause, if it applies to all other departments, etc
v_newsal := v_sal * 1.055;
END IF
;
You can use a single UPDATE statement and use a WHERE clause to only update employees in specific departments and a CASE expression to map the different department/salary ranges to the raise percentage:
UPDATE emp
SET sal = sal * CASE
WHEN deptno = 10 AND sal > 2000 THEN 1.06
WHEN deptno = 10 THEN 1.07
WHEN deptno = 20 AND sal > 2500 THEN 1.05
WHEN deptno = 20 THEN 1.055
END
WHERE deptno IN (10,20)
maybe something like this:
IF v_deptno = 10 THEN
IF v_sal > 2500 THEN
v_newsal := v_sal * 1.060;
ELSE
v_newsal := v_sal * 1.070;
END IF;
END IF;
IF v_deptno = 20 THEN
IF v_sal > 2500 THEN
v_newsal := v_sal * 1.050;
ELSE
v_newsal := v_sal * 1.055;
END IF;
END IF;
Actually, you don't need inefficient row-by-row processing in a loop; use a single update statement with case expression:
update emp set
sal = case when deptno = 10 then
case when sal > 2000 then sal * 1.060
else sal * 1.070
end
when deptno = 20 then
case when sal > 2500 then sal * 1.050
else sal * 1.055
end
else sal
end;
Related
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've two cursors both have almost similar code just a little difference in the group by condition, I want if the id is 1 or 2 e.t.c then it should open cur1 else cur2, basically one cursor at a time. Is there any possibility of achieving this?
if id in (1,2,3,4) then
cursor_value:= 'cur1';
else
cursor_value := 'cur2';
end if;
for i in cursor_value loop
end loop;
You can use an OPEN-FOR statement. Example:
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
/
One option would be to include both cursor's SELECT statements into cursor FOR loop, along with a condition that chooses which one of them will be used. For example:
SQL> declare
2 id number := &par_id;
3 begin
4 for cur_r in (select * from emp
5 where deptno = 10
6 and id in (1,2,3,4)
7 union all
8 select * from emp
9 where deptno = 20
10 and id not in (1,2,3,4)
11 )
12 loop
13 dbms_output.put_Line(cur_r.deptno ||' '||cur_r.ename);
14 end loop;
15 end;
16 /
Enter value for par_id: 1 --> ID = 1, so use SELECT for DEPTNO = 10
old 2: id number := &par_id;
new 2: id number := 1;
10 CLARK
10 KING
10 MILLER
PL/SQL procedure successfully completed.
SQL> /
Enter value for par_id: 823 --> ID <> 1, so use SELECT for DEPTNO = 20
old 2: id number := &par_id;
new 2: id number := 823;
20 SMITH
20 JONES
20 SCOTT
20 ADAMS
20 FORD
PL/SQL procedure successfully completed.
SQL>
The simple way could be to use if else condition.
DECLARE
type cur REF CURSOR;
c cur;
BEGIN
IF id in (1,2,3,4) THEN
OPEN c FOR 'cursor query 1';
ELSE
OPEN c FOR 'cursor query 2';
END IF ;
END;
Cheers!!
I am updating emp3 (same as emp table) using a Cursor For loop,
DECLARE
CURSOR incr_cur IS SELECT * FROM emp3 FOR UPDATE OF sal;
v_job emp3.job%TYPE := '&ENTER_Job';
v_cnt INTEGER := 0;
BEGIN
FOR r_l IN incr_cur LOOP
IF v_job IN (r_l.job) THEN
UPDATE emp3 SET sal = sal + 100 WHERE CURRENT OF incr_cur;
END IF;
END LOOP;
FOR r_l IN incr_cur LOOP
IF v_job IN (r_l.job) THEN
v_cnt := v_cnt + 1;
DBMS_OUTPUT.PUT_LINE('The Salary of ' || r_l.ename || ' is Incremented by 100 and the Updated Salary is: $' || r_l.sal);
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('The Salary of '|| v_cnt ||' Employees are Updated');
END;
When executing the PL/SQL block it will ask for the job,
I give MANAGER, then the salary of the employees who are MANAGER is incremented by 100.
The emp3 table has 5 JOB categories CLERK, MANAGER, ANALYST, SALESMAN and PRESIDENT.
Then how to display the Message The Job is not listed so update is not possible., if a user inputs a JOB which is not in the table such as DEVELOPER.
I had tried with exception handling but could not get it to work.
There is no need for a separate step. Just attempt the update and if no rows were updated, say so. If you want it to be an exception, then raise one with raise_application_error.
Assuming this is a learning exercise and this is why you don't want to just do an ordinary update, you might do something like this:
declare
k_job constant emp3.job%type := '&JOB';
cursor employees_cur is
select * from emp3
where job = k_job
for update of sal;
v_update_count integer := 0;
v_payroll_increase integer := 0;
begin
for r in employees_cur loop
update emp3 set sal = sal + 100 where current of employees_cur;
dbms_output.put_line('Salary for ' || r.ename || ' is incremented by $100 from $' || r.sal || ' to $' || (r.sal +100));
v_update_count := v_update_count + 1;
v_payroll_increase := v_payroll_increase + 100;
end loop;
if v_update_count = 0 then
dbms_output.put_line('No staff are currently employed as ' || k_job ||'. Payroll is unchanged.');
else
dbms_output.put_line('Updated salary of '|| v_update_count ||' employee' || case when v_update_count <> 1 then 's' end||'.');
dbms_output.put_line('Payroll increased by $'||v_payroll_increase||'.');
end if;
end;
/
Enter value for job: SALESMAN
Salary for ALLEN is incremented by $100 from $1600 to $1700
Salary for WARD is incremented by $100 from $1250 to $1350
Salary for MARTIN is incremented by $100 from $1250 to $1350
Salary for TURNER is incremented by $100 from $1500 to $1600
Updated salary of 4 employees.
Payroll increased by $400.
PL/SQL procedure successfully completed.
For a nonexistent job, you get this:
Enter value for job: ASTRONAUT
No staff are currently employed as ASTRONAUT. Payroll is unchanged.
(In this example, v_payroll_increase is always 100 times v_update_count, but if you wanted to give a 10% raise or differing increases by department etc it might be more useful.)
Here's one option: check whether such a job exists; if not, query will return NO_DATA_FOUND which you can handle and raise an exception with appropriate message. Otherwise, proceed with the UPDATE.
SQL> declare
2 l_job emp.job%type;
3 begin
4 begin
5 select job
6 into l_job
7 from emp
8 where job = '&ENTER_Job'
9 and rownum = 1;
10 exception
11 when no_data_found then
12 raise_application_error(-20000, 'That job does not exist');
13 end;
14
15 -- Job exists, so - go on with the update
16 end;
17 /
Enter value for enter_job: MANAGER
PL/SQL procedure successfully completed.
SQL> /
Enter value for enter_job: DEVELOPER
declare
*
ERROR at line 1:
ORA-20000: That job does not exist
ORA-06512: at line 12
SQL>
P.S. Forgot to mention: I prefer doing such a job through a stored procedure (which accepts job name as a parameter) instead of an anonymous PL/SQL block.
The question is to write a block of codes that for those employees with a salary less than 1100, raise those people's salary by 50.
My code is as below but has error:
ERROR at line 2: Encountered the symbol "RAISE_SALARY" when expecting one of the following: := . ( # % ; immediate The symbol "; was inserted before "RAISE_SALARY" to continue.
CREATE OR REPLACE procedure raise_salary
AS
CURSOR c1 IS
select ename, sal from emp
where sal <1100
order by sal ASC;
BEGIN
FOR emp_rec IN c1
LOOP
UPDATE emp
SET sal = sal + 50;
END LOOP;
END;
/
begin
execute raise_salary;
end;
/
Search a little further, I think you'll find that the procedure didn't update the salary by 150, but actually updated by 50 the number of times equal to the number of rows returned in the cursor. In this case 3 rows (so 3 * 50 = 150) for anyone you validate. Further, it updated all salaries not only the ones in the cursor.
The update statement does not contain a WHERE clause, thus every row in the table is updated.
Further a simple update statement will accomplish what you need:
Update emp
set sal = sal + 50
where sal < 1100;
Get into the habit of thinking in terms of sets instead individual rows.
I tried to fix this for two days and I couldnt.
I know why the code happens but Im not sure where.
or why its happening...
This has to be done in PLSQL
its a homework assignment.
SET SERVEROUTPUT ON
DECLARE
V_IDNO PAYDATA1.IDNO%TYPE;
V_NAME PAYDATA1.NAME%TYPE;
V_SAL PAYDATA1.SALARY%TYPE;
V_JOB PAYDATA1.JOBCODE%TYPE;
V_PAY PAYDATA1.PAYHR%TYPE;
V_IDNO1 PAYTRAN1.IDNO%TYPE;
V_HOURSWK PAYTRAN1.HOURSWK%TYPE;
V_HOURS HOURSWKD.HOURSWK%TYPE;
V_CHECK NUMBER(10);
CURSOR paydata_cursor IS
SELECT IDNO, NAME, JOBCODE, SALARY, PAYHR FROM PAYDATA1
ORDER BY IDNO;
CURSOR paytran_cursor IS
SELECT IDNO, HOURSWK FROM PAYTRAN1
WHERE V_IDNO = IDNO
order by IDNO;
BEGIN
OPEN paydata_cursor;
LOOP
FETCH paydata_cursor INTO V_IDNO, V_NAME, V_CHECK, V_JOB, V_PAY;
EXIT WHEN paydata_cursor%NOTFOUND;
IF V_SAL > 0 THEN
V_CHECK := V_SAL / 52;
DBMS_OUTPUT.PUT_LINE(V_IDNO|| ' HAS A CHECK FOR: '||V_CHECK);
END IF;
IF V_SAL = 0 AND V_HOURSWK < 41 THEN
V_CHECK := V_PAY * V_HOURS;
DBMS_OUTPUT.PUT_LINE(V_IDNO|| ' HAS A CHECK FOR: '||V_CHECK);
ELSIF V_SAL = 0 AND V_HOURSWK > 40 THEN
V_CHECK := V_PAY * V_HOURS;
V_CHECK := V_SAL + ((V_HOURSWK * 1.5) * (V_HOURSWK - 40));
DBMS_OUTPUT.PUT_LINE(V_IDNO|| ' HAS A CHECK FOR: '||V_CHECK);
END IF;
END LOOP;
CLOSE paydata_cursor;
END;
/
SET SERVEROUTPUT OFF
IM GETTING THIS ERROR, TRIED TO FIX IT ALL DAY AND COULDN’T. ANY ADVICE?
SQL> # CURSOR5
DECLARE
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 22
I can see a few issues with your code but without knowing much more info it's difficult to diagnose...but I'll try:
It looks like you're selecting into the wrong variables when opening your cursor.
You also haven't opened your paytran_cursor anywhere. You can add that code yourself but until you do, your check of V_HOURSWK won't be very useful etc.
Try this instead:
SET SERVEROUTPUT ON
DECLARE
V_IDNO PAYDATA1.IDNO%TYPE;
V_NAME PAYDATA1.NAME%TYPE;
V_SAL PAYDATA1.SALARY%TYPE;
V_JOB PAYDATA1.JOBCODE%TYPE;
V_PAY PAYDATA1.PAYHR%TYPE;
V_IDNO1 PAYTRAN1.IDNO%TYPE;
V_HOURSWK PAYTRAN1.HOURSWK%TYPE;
V_HOURS HOURSWKD.HOURSWK%TYPE;
V_CHECK NUMBER(10);
CURSOR paydata_cursor IS
SELECT IDNO, NAME, JOBCODE, SALARY, PAYHR
FROM PAYDATA1
ORDER BY IDNO;
CURSOR paytran_cursor IS
SELECT IDNO, HOURSWK
FROM PAYTRAN1
WHERE V_IDNO = IDNO
order by IDNO;
BEGIN
OPEN paydata_cursor;
LOOP
-- Changed the variables you were selecting into
FETCH paydata_cursor INTO V_IDNO, V_NAME, V_JOB, V_SAL, V_PAY;
EXIT WHEN paydata_cursor%NOTFOUND;
IF V_SAL > 0
THEN
V_CHECK := V_SAL / 52;
DBMS_OUTPUT.PUT_LINE(V_IDNO|| ' HAS A CHECK FOR: '||V_CHECK);
END IF;
IF V_SAL = 0 AND V_HOURSWK < 41
THEN
V_CHECK := V_PAY * V_HOURS;
DBMS_OUTPUT.PUT_LINE(V_IDNO|| ' HAS A CHECK FOR: '||V_CHECK);
ELSIF V_SAL = 0 AND V_HOURSWK > 40
THEN
V_CHECK := V_PAY * V_HOURS;
V_CHECK := V_SAL + ((V_HOURSWK * 1.5) * (V_HOURSWK - 40));
DBMS_OUTPUT.PUT_LINE(V_IDNO|| ' HAS A CHECK FOR: '||V_CHECK);
END IF;
END LOOP;
CLOSE paydata_cursor;
END;
/
SET SERVEROUTPUT OFF
Hope it helps.
EDIT:
I have tried to guess what you are trying to do with your code and think that this below might go some way to fixing your issues in a slightly more efficient manner:
SET SERVEROUTPUT ON
DECLARE
--
c_max_hours CONSTANT NUMBER := 40;
--
-- N.B.: I have assumed that there may be more than one entry per IDNO for
-- hours worked, if this is not the case then you can remove the SUM() and
-- the GROUP BY clause
--
CURSOR pay_cursor
IS
SELECT IDNO,
NAME,
JOBCODE,
SALARY,
PAYHR,
SUM(HOURSWK) AS HOURS_WORKED
FROM PAYDATA1
JOIN PAYTRAN1 USING (IDNO)
GROUP BY IDNO,
NAME,
JOBCODE,
SALARY,
PAYHR;
--
V_CHECK NUMBER;
pay_record pay_cursor%ROWTYPE;
--
BEGIN
-- Depending on the rows in your cursor you might want to increase the output buffer for DBMS_OUTPUT
DBMS_OUTPUT.ENABLE(1000000);
--
OPEN pay_cursor;
LOOP
-- Fetch the data into your cursor rowtype variable
FETCH paydata_cursor INTO pay_record;
EXIT WHEN pay_cursor%NOTFOUND;
--
-- ASSUMPTION: salary is not NULL (i.e. 0 or more).
--
IF pay_record.salary > 0
THEN
V_CHECK := pay_record.salary / 52;
ELSE
--
-- Salary must be zero
--
IF pay_record.hours_worked <= c_max_hours
THEN
V_CHECK := pay_record.payhr * pay_record.hours_worked;
ELSE
-- Must be > c_max_hours
V_CHECK := pay_record.payhr * pay_record.hours_worked;
V_CHECK := pay_record.salary + ((pay_record.hours_worked * 1.5) * (pay_record.hours_worked - 40));
END IF;
END IF;
--
-- Output your result
--
DBMS_OUTPUT.PUT_LINE(pay_record.idno|| ' HAS A CHECK FOR: '||V_CHECK);
END LOOP;
CLOSE paydata_cursor;
EXCEPTION
WHEN others
THEN
-- Close the cursor if it is still open
IF pay_cursor%ISOPEN
THEN
CLOSE pay_cursor;
END IF;
-- Re-raise the error
RAISE;
END;
/
SET SERVEROUTPUT OFF
I hope it's useful.
P.S. I couldn't check this in a real environment as I'm not at my usual PC so apologies for any syntax errors.
Problem is that your column paydata1.JOBCODE is not a number
and in this code FETCH paydata_cursor INTO V_IDNO, V_NAME, V_CHECK, V_JOB, V_PAY; you are assigning it to V_CHECK which is number
Change the order like this: FETCH paydata_cursor INTO V_IDNO, V_NAME, V_JOB, V_CHECK, V_PAY; and it should work but ensure that paydata1.salary is a number
Also i am not sure when you have declared V_SAL then why are you using V_CHECK in this cursor fetch.
at line 13 when you are defining your cursor paydata_cursor as columns like
SELECT IDNO, NAME, JOBCODE, SALARY, PAYHR FROM PAYDATA1
ORDER BY IDNO;
column sequence IDNO, NAME, JOBCODE, SALARY, PAYHR
but in line 23 i.e.
FETCH paydata_cursor INTO V_IDNO, V_NAME, V_CHECK, V_JOB, V_PAY;
you are actually fetching the columns like this
IDNO, NAME, JOBCODE, SALARY, PAYHR into
V_IDNO, V_NAME, V_CHECK, V_JOB, V_PAY
that means JOBCODE is going into V_CHECK
and SALARY is going into V_JOB
since salary must be number and v_check must be varchar
i think this is why u r getting this error
Thanks
Sid