Unable to find in oracle cursor update that how many records are updated? - plsql

Scenario:
Write a PL/SQL block that takes a department number from a user and increases the salary of all the employees belonging to the department by 10%. The block should display on the screen how many records are updated.
My Program:
DECLARE
V_TOT_ROWS NUMBER(3);
CURSOR emp_cursor IS
SELECT EMPSAL FROM emp WHERE deptno=&DEPT_NO
FOR UPDATE OF EMPSAL NOWAIT;
BEGIN
FOR emp_record IN emp_cursor
LOOP
UPDATE emp
SET EMPSAL=EMPSAL+emp_record.EMPSAL*0.1
WHERE CURRENT OF emp_cursor;
-- V_TOT_ROWS := SQL%ROWCOUNT;
-- DBMS_OUTPUT.PUT_LINE('TOTAL UPDATED RECORDS: ' || V_TOT_ROWS);
DBMS_OUTPUT.PUT_LINE('Updated ' || SQL%ROWCOUNT || ' salaries.');
END LOOP;
COMMIT;
-- DBMS_OUTPUT.PUT_LINE('Updated ' || SQL%ROWCOUNT || ' salaries.');
END;
It is giving 1 row updated every time the loop is executed but if I keep the dbms_output outside the loop, it gives 0.
Please help.
Thanks,

Please check below Script:
declare V_TOT_ROWS NUMBER(3) :=0;
CURSOR emp_cursor IS
SELECT EMPSAL FROM emp WHERE deptno=&DEPT_NO
FOR UPDATE OF EMPSAL NOWAIT;
begin FOR emp_record IN emp_cursor
LOOP
UPDATE emp
SET EMPSAL=EMPSAL+emp_record.EMPSAL*0.1
WHERE CURRENT OF emp_cursor;
V_TOT_ROWS := V_TOT_ROWS+SQL%ROWCOUNT;
-- DBMS_OUTPUT.PUT_LINE('TOTAL UPDATED RECORDS: ' || V_TOT_ROWS);
-- DBMS_OUTPUT.PUT_LINE('Updated ' || SQL%ROWCOUNT || ' salaries.');
END LOOP;
COMMIT;
DBMS_OUTPUT.PUT_LINE('Updated ' || V_TOT_ROWS || ' salaries.'); end;

Use simple update instead of a cursor + forall.
If you use FORALL ... UPDATE, then only 1 record is updated in each loop cycle, so UPDATE returns SQL%ROWCOUNT always = 1.
DECLARE
V_TOT_ROWS NUMBER(3);
BEGIN
UPDATE emp
SET EMPSAL= EMPSAL+ EMPSAL*0.1
WHERE deptno=&DEPT_NO;
V_TOT_ROWS := SQL%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE('TOTAL UPDATED RECORDS: ' || V_TOT_ROWS);
COMMIT;
END;
/

Related

PLSQL trigger compilation error

PLSQL trigger created with compilation error and I cannot update the table.
create or replace trigger dis_salary_changes
before update of Salary on system.emp
for each row
when (new.Eno in('E1','E2','E3','E4','E5'))
declare
sal_diff integer;
begin
sal_diff := :new.salary - :old.salary;
dbms_output.put_line('Old Salary= '|| :old.Salary);
dbms_output.put_line('New Salary= '|| :new.Salary);
dbms_output.put_line('Salary difference= '|| :sal_diff);
end;
/
The trigger is more or less OK, apart from a few things.
First of all, don't use SYSTEM schema for your business. Create another user and test your skills there (or, use one of pre-installed ones, such as SCOTT or HR, if they are there). You might screw the database up if you continue working in SYSTEM.
As of your code, the only syntax error is in the last DBMS_OUTPUT.PUT_LINE call - remove the colon in front of the SAL_DIFF variable:
CREATE OR REPLACE TRIGGER dis_salary_changes
BEFORE UPDATE OF Salary
ON SYSTEM.emp
FOR EACH ROW
WHEN (new.Eno IN ('E1',
'E2',
'E3',
'E4',
'E5'))
DECLARE
sal_diff INTEGER;
BEGIN
sal_diff := :new.salary - :old.salary;
DBMS_OUTPUT.put_line ('Old Salary= ' || :old.Salary);
DBMS_OUTPUT.put_line ('New Salary= ' || :new.Salary);
DBMS_OUTPUT.put_line ('Salary difference= ' || :sal_diff);
^
remove it
END;
/
Also, once you calculate the difference, you don't do anything with that information. Is it intentional?
create or replace trigger dis_SAL_changes
before update of SAL on emp
for each row
when (new.EMPNO in('E1','E2','E3','E4','E5'))
declare
SAL integer;
begin
SAL := :new.SAL - :old.SAL;
dbms_output.put_line('Old SAL= '|| :old.SAL);
dbms_output.put_line('New SAL= '|| :new.SAL);
dbms_output.put_line('SAL difference= '|| :SAL);
end;
TRIGGER DIS_SAL_CHANGES compiled

Point me in the right direction

-- File: PLh10.sql
-- Author: John Tunisi
-- ----------------------------------
SET SERVEROUTPUT ON
SET VERIFY OFF
-- ----------------------------------
ACCEPT traineeID NUMBER PROMPT 'Enter a trainee ID: '
ACCEPT increment NUMBER PROMPT 'Enter an increment for his trainers: '
DECLARE
sr sailors%ROWTYPE;
CURSOR tCursor IS
SELECT S.sid, S.sname, S.rating, S.age, S.trainee
FROM sailors S, sailors R
WHERE R.sid = '&traineeID' AND
S.trainee = R.sid;
BEGIN
OPEN tCursor;
LOOP
-- Fetch the qualifying rows one by one
FETCH tCursor INTO sr;
-- Print the sailor' old record
DBMS_OTPUT.PUT_LINE ('+++++ old row: '||sr.sid||' '
||sr.sname||sr.rating||' '||sr.age||' '||sr.trainee);
-- Increment the trainers' rating
sr.rating := sr.rating + &increment;
UPDATE sailors
SET rating = sr.rating
WHERE sailors.sid = sr.sid;
-- Print the sailor' new record
DBMS_OUTPUT.PUT_LINE ('+++++ new row: '||sr.sid||' '
||sr.sname||sr.rating||' '||sr.age||' '||sr.trainee);
END LOOP;
IF tCursor%ROWCOUNT = 0 /*test whether the trainee has no trainers*/
DBMS_OUTPUT.PUT_LINE ('+++++ '||sr.sid||' is either not a sailor,'
||' or has no trainer');
ELSE
DBMS_OUTPUT.PUT_LINE ('+++++ DB has been updated');
END IF;
CLOSE tCursor;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('+++++'||SQLCODE||'...'||SQLERRM);
END;
/
-- Let's see what happened to the database
SELECT *
FROM sailors S
WHERE S.trainee = '&traineeID';
UNDEFINE traineeID
UNDEFINE increment
Okay, so I need to increment trainers based on a trainee. I think most of it is correct, but I am getting an error on the line below " DBMS_OUTPUT.PUT_LINE ('+++++ '||sr.sid||' is either not a sailor,'". I am not sure what is supposed to go here, as this is my first time writing PL/SQL.
Try this:
SET SERVEROUTPUT ON
SET VERIFY OFF
-- ----------------------------------
ACCEPT traineeID NUMBER PROMPT 'Enter a trainee ID: '
ACCEPT increment NUMBER PROMPT 'Enter an increment for his trainers: '
DECLARE
sr sailors%ROWTYPE;
srNew sailors%ROWTYPE;
nRecords_updated NUMBER := 0;
CURSOR tCursor IS
SELECT S.sid, S.sname, S.rating, S.age, S.trainee
FROM sailors S, sailors R
WHERE R.sid = '&traineeID' AND
S.trainee = R.sid;
BEGIN
OPEN tCursor;
LOOP
-- Fetch the qualifying rows one by one
FETCH tCursor INTO sr;
EXIT WHEN tCursor%NOTFOUND; -- ADDED
-- Print the sailor' old record
DBMS_OUTPUT.PUT_LINE ('+++++ old row: ' || sr.sid || ' ' ||
sr.sname || sr.rating || ' ' || sr.age ||
' ' || sr.trainee);
-- Increment the trainers' rating
sr.rating := sr.rating + &increment;
UPDATE sailors
SET rating = sr.rating
WHERE sailors.sid = sr.sid;
nRecords_updated := nRecords_updated + SQL%ROWCOUNT; -- ADDED
-- Obtain the updated record -- ADDED
SELECT s.* -- ADDED
INTO srNew -- ADDED
FROM SAILORS s -- ADDED
WHERE s.SID = sr.SID; -- ADDED
-- Print the sailor' new record
DBMS_OUTPUT.PUT_LINE ('+++++ new row: ' || srNew.sid || ' ' || -- CHANGED
srNew.sname || srNew.rating || ' ' || -- CHANGED
srNew.age || ' ' || srNew.trainee); -- CHANGED
END LOOP;
IF nRecords_updated = 0 /*test whether the trainee has no trainers*/ -- CHANGED
DBMS_OUTPUT.PUT_LINE ('+++++ ' || sr.sid || ' is either not a sailor,' ||
' or has no trainer');
ELSE
DBMS_OUTPUT.PUT_LINE ('+++++ DB has been updated');
END IF;
CLOSE tCursor;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('+++++'||SQLCODE||'...'||SQLERRM);
END;
/
I included comments (either ADDED or CHANGED) on the lines I altered or added. It's tough to say whether this will work or not as I don't have access to your data but it might be a step in the right direction.
Share and enjoy.

TOAD displaying cursor recordset returned by stored procedure

How I could print recorset result from the returning cursor, please.
Down below executes fine but I need to see result.
This is block in TOAD, calling package sp AMD_NEEDMSG:
DECLARE
RETURN_RECORDSET CTI_MATRIX.AMD.REF_CURSOR;
BEGIN
CTI_MATRIX.AMD.AMD_NEEDMSG ( '88888888885', RETURN_RECORDSET );
END;
This package spec:
CREATE OR REPLACE PACKAGE CTI_MATRIX.AMD AS
TYPE REF_CURSOR IS REF CURSOR;
PROCEDURE AMD_NEEDMSG (v_CRN IN VARCHAR2, return_recordset OUT REF_CURSOR);
END AMD;
This is package body:
CREATE OR REPLACE PACKAGE BODY CTI_MATRIX.AMD AS
PROCEDURE AMD_NEEDMSG (v_CRN IN VARCHAR2, return_recordset OUT REF_CURSOR) IS
return_flag INTEGER;
row_cnt INTEGER;
number_of_days INTEGER;
var_DATE DATE;
CURSOR ACCNTSEARCH (P_CRN IN VARCHAR2) IS
SELECT DISTINCT COUNT(*) , AMD.MSG_DATE
FROM TBL_AMD_NEEDMSG AMD
WHERE AMD.PHONE_NUMBER = P_CRN AND ROWNUM = 1;
BEGIN
OPEN ACCNTSEARCH(v_CRN);
FETCH ACCNTSEARCH INTO row_cnt, var_DATE;
CLOSE ACCNTSEARCH;
IF (row_cnt = 0)
THEN
INSERT INTO TBL_AMD_NEEDMSG (PHONE_NUMBER, MSG_DATE) VALUES (v_CRN , SYSDATE);
return_flag := 1;
ELSE
SELECT SYSDATE-var_DATE INTO number_of_days FROM dual;
IF (number_of_days>7)
THEN
UPDATE TBL_AMD_NEEDMSG SET MSG_DATE = SYSDATE WHERE PHONE_NUMBER = v_CRN;
return_flag := 1;
ELSE
return_flag := 0;
END IF;
END IF;
COMMIT;
OPEN return_recordset FOR
SELECT return_flag AS ReturnFLag FROM DUAL;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END AMD_NEEDMSG;
END AMD;
/
Bottom line is to return to client a value of return_flag in the form of record set.
To return the return_flag, which is int=1 or 0 you need function that returns a single value, not recordset. Below are general recordset examples - hope this helps:
DECLARE
TYPE empcurtyp IS REF CURSOR;
emp_cv empcurtyp;
--
TYPE namelist IS TABLE OF scott.emp.ename%TYPE;
TYPE sallist IS TABLE OF scott.emp.sal%TYPE;
names namelist;
sals sallist;
BEGIN
OPEN emp_cv FOR
SELECT ename, sal FROM scott.emp
WHERE job = 'MANAGER' ORDER BY sal DESC;
--
FETCH emp_cv BULK COLLECT INTO names, sals;
CLOSE emp_cv;
-- loop through the names and sals collections
FOR i IN names.FIRST .. names.LAST LOOP
DBMS_OUTPUT.PUT_LINE
('Name = ' || names(i) || ', salary = ' || sals(i));
END LOOP;
END;
/
-- SYS_REFCURSOR example --
DECLARE
p_rc_type SYS_REFCURSOR;
emp_rec scott.emp%ROWTYPE;
--
PROCEDURE p_Emp_Info (p_cur_var OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_cur_var FOR
SELECT ename, job FROM scott.emp WHERE job = 'MANAGER';
LOOP
FETCH p_cur_var INTO emp_rec.ename, emp_rec.job;
EXIT WHEN p_cur_var%NOTFOUND;
dbms_output.put_line(emp_rec.ename ||' '|| emp_rec.job);
END LOOP;
CLOSE p_cur_var;
END p_Emp_Info;
--
BEGIN
p_Emp_Info(p_rc_type);
END;
/

Duplicate INSERT record procedure

I have problem with mu procedure which insert duplicate last record in table
example when I put INSERT..... 'AAA' I got to rows in table 'AAA' and 'AAA'
In place when I put DBMS()... in code I got tow records
I use trigger and sequence for column ID in HistoriaDismissDate but they are in good condition. I check if I dropped trigger and sequence and its the same situation
I also use viewDate but this view get mi ONE record not two
my code
CREATE OR REPLACE PROCEDURE ChangeDismissDate
IS
v_id VARCHAR2(11);
v_dateBhd DATE := TO_DATE('20491231','yyyymmdd');
v_dateDismiss DATE := TO_DATE('20491231','yyyymmdd');
v_login VARCHAR2(50);
last_id NUMBER :=0;
CURSOR cur IS
select EMP_NO, LOGIN, ODEJSCIE_BHD, ODEJSCIE_OLD FROM viewDate;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO v_id,v_login,v_dateBhd,v_dateDismiss;
DBMS_OUTPUT.put_line(v_id || ' ' || v_login || ' ' || v_dateBhd || ' ' || v_dateDismiss);
UPDATE employee_tab SET DISMISS_DATE = v_dateBhd WHERE EMP_NO = v_id;
COMMIT;
INSERT INTO HistoriaDismissDate(CUSTOMER_ID,LOGIN, DATE_CHANGE, DATE_BHD, DATE_DISMISS)
VALUES(v_id,v_login, sysdate, v_dateBhd, v_dateDismiss);
COMMIT;
EXIT WHEN cur%NOTFOUND;
END LOOP;
CLOSE cur;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_stack);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (DBMS_UTILITY.format_error_stack);
END;
/
2 tips on your original code:
1) cursor is a very old programming technic on PL/SQL. Prefer to use FOR ... LOOP construction. It's cleaner and less error-prone! See how it works:
CREATE OR REPLACE PROCEDURE ChangeDismissDate IS
BEGIN
for cur in (select EMP_NO, LOGIN, ODEJSCIE_BHD, ODEJSCIE_OLD FROM viewDate) loop
DBMS_OUTPUT.put_line(cur.EMP_NO || ' ' || cur.login || ' ' || cur.ODEJSCIE_BHD || ' ' || cur.ODEJSCIE_OLD);
UPDATE employee_tab
SET DISMISS_DATE = cur.ODEJSCIE_BHD
WHERE EMP_NO = cur.EMP_NO;
INSERT INTO HistoriaDismissDate
( CUSTOMER_ID,LOGIN, DATE_CHANGE, DATE_BHD, DATE_DISMISS )
VALUES
( cur.EMP_NO, cur.LOGIN, sysdate, cur.ODEJSCIE_BHD, cur.ODEJSCIE_OLD, );
end loop;
end;
/
2) Never, I mean never put a commit inside your procedure. The commit should be done on the caller block or on your client-side app. When you put a commit inside your procedure, you miss the chance to rollback after running it and other procedures could not call it if they want to control the transaction flow.

How to solve the Issue declaring a cursor in a Procedure/Package?

I'm new to Oracle and having issues declaring a cursor within my package. Can anyone tell me what i'm doing wrong?
Thanks
CREATE OR REPLACE PACKAGE CHECK_STOCK_LEVELS AS
PROCEDURE CHECK_PAYMTS_AGAINST_INVOICES;
FUNCTION CHECK_PROD_PRICE RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY CHECK_STOCK_LEVELS AS
FUNCTION CHECK_PROD_PRICE (INP_PROD_NAME VARCHAR2)
RETURN NUMBER
IS
PROD_CNT PRODUCT.PROD_NAME%TYPE;
BEGIN
SELECT PROD_PRICE INTO PROD_CNT
FROM PRODUCT WHERE PROD_PRICE = INP_PROD_NAME;
RETURN (PROD_CNT);
END CHECK_PROD_PRICE;
/
SELECT CHECK_STOCK_LEVELS.CHECK_PROD_PRICE(3.5mm Jack) FROM DUAL;
PROCEDURE CHECK_PAYMTS_AGAINST_INVOICES
DECLARE CURSOR SEL_PAYMTS_AND_INVS
IS
SELECT P.INVOICE_ID, P.PAYMENT_AMOUNT, I.INVOICE_AMOUNT
FROM PAYMENT P
INNER JOIN INVOICE I ON P.INVOICE_ID = I.INVOICE_ID;
V_TEMP_RESULTS_ROW SEL_PAYMTS_AND_INVS%ROWTYPE;
PAYMT_INVOICE_MISMATCH EXCEPTION;
BEGIN
OPEN SEL_PAYMTS_AND_INVS;
FETCH SEL_PAYMTS_AND_INVS INTO V_TEMP_RESULTS_ROW;
WHILE SEL_PAYMTS_AND_INVS%FOUND LOOP
IF V_TEMP_RESULTS_ROW.PAYMENT_AMOUNT != V_TEMP_RESULTS_ROW.INVOICE_AMOUNT THEN
DBMS_OUTPUT.PUT_LINE('EMPLOYEE ID : ' || V_EMPLOYEE_ROW.EMPID || ' HAS A SALARY OF : ' || V_EMPLOYEE_ROW.SALARY);
RAISE PAYMT_INVOICE_MISMATCH;
END IF;
FETCH SEL_PAYMTS_AND_INVS INTO V_TEMP_RESULTS_ROW;
END LOOP;
CLOSE SEL_PAYMTS_AND_INVS;
EXCEPTION
WHEN PAYMT_INVOICE_MISMATCH THEN
DBMS_OUTPUT.PUT_LINE('PAYMENT AMOUNT DOES NOT MATCH INVOICE AMOUNT');
RAISE;
END;
END;
This:
SELECT CHECK_STOCK_LEVELS.CHECK_PROD_PRICE(3.5mm Jack) FROM DUAL;
should be:
SELECT CHECK_STOCK_LEVELS.CHECK_PROD_PRICE('3.5mm Jack') FROM DUAL;
And this:
PROCEDURE CHECK_PAYMTS_AGAINST_INVOICES
DECLARE CURSOR SEL_PAYMTS_AND_INVS
IS
SELECT P.INVOICE_ID, P.PAYMENT_AMOUNT, I.INVOICE_AMOUNT
FROM PAYMENT P
INNER JOIN INVOICE I ON P.INVOICE_ID = I.INVOICE_ID;
V_TEMP_RESULTS_ROW SEL_PAYMTS_AND_INVS%ROWTYPE;
PAYMT_INVOICE_MISMATCH EXCEPTION;
BEGIN
...
should be:
PROCEDURE CHECK_PAYMTS_AGAINST_INVOICES
IS -- not DECLARE
CURSOR SEL_PAYMTS_AND_INVS
IS
SELECT P.INVOICE_ID, P.PAYMENT_AMOUNT, I.INVOICE_AMOUNT
FROM PAYMENT P
INNER JOIN INVOICE I ON P.INVOICE_ID = I.INVOICE_ID;
V_TEMP_RESULTS_ROW SEL_PAYMTS_AND_INVS%ROWTYPE;
PAYMT_INVOICE_MISMATCH EXCEPTION;
BEGIN
...

Resources