Counter to remove a record pl/sql - plsql

I have a problem with the procedure that should remove one product at a time based on the order number and the product number from the tables below. I tried the procedure initially and it worked only when removing the first pno from odetails, and inserting into the required tables. However, my problem is when an order has more than one product number and wanted to remove One By One. I usually get an error with the last item when I try to remove it and also the order won't be deleted because there is no delete on this table. In summary How do to remove the last ono from orders once all the removing processes finish? Any help would be much appreciated!
create or replace PROCEDURE ONE_BY_ONE( ORD_NUM IN NUMBER(5), PRO_NUM IN NUMBER(5))
AS
CURSOR CUR1
IS
SELECT * FROM ORDERS
WHERE ONO = ORD_NUM;
CURSOR CUR2
IS
SELECT * FROM ODETAILS
WHERE PNO = PRO_NUM
AND ONO = P_ONO;
VAL1 CUR1%ROWTYPE;
VAL2 CUR2%ROWTYPE;
BEGIN
OPEN CUR1;
OPEN CUR2;
LOOP
FETCH CUR1 INTO VAL1;
FETCH CUR2 INTO VAL2;
EXIT WHEN CUR1%NOTFOUND;
EXIT WHEN CUR1%NOTFOUND;
IF VAL1.SHIPPED IS NOT NULL THEN
IF PRO_NUM = VAL2.PNO AND ORD_NUM = VAL2.ONO THEN
INSERT INTO (ID_NUMBER, ONO,CNO,DATE_SEND,CONDITION)
VALUES(Seq.NEXTVAL, VAL1.ONO,VAL1.CNO,SYSDATE,'SEND BACK');
INSERT INTO REMOVED_ODETAILS(ONO,PNO,QTY) VALUES (VAL2.ONO, VAL2.PNO, VAL2.QTY);
END IF;
DELETE FROM ODETAILS WHERE ONO = ORD_NUM AND PNO = PRO_NUM;
DBMS_OUTPUT.PUT_LINE('Item will be send back ');
-- DELETE FROM ORDERS WHERE ONO = ORD_NUM;
ELSIF VAL1.SHIPPED IS NULL THEN
DBMS_OUTPUT.PUT_LINE('DO NOTHING ');
END IF;
END LOOP;
CLOSE CUR1;
CLOSE CUR2;
END ONE_BY_ONE;
ORDERS
ONO CNO ENO RECEIVED SHIPPED
---------- ---------- ---------- --------- ---------
1112 1010 9999 18-JAN-16 15-JAN-16
ODETAILS
ONO PNO QTY
---------- ---------- ----------
1112 12345 1
1112 67891 4

Just answering your question, you can add to your delete
DELETE FROM ORDERS
WHERE ONO = ORD_NUM and
NOT EXISTS (select 1 from ODETAILS where ONO = ORD_NUM );
After that delete will be executed only if it's the last product in the order.

Related

Error in executing plsql procedure (value larger than specified precision allowed for this column)

Here is my procedure where i want to insert values with the conditions like emp id start with sequence 131, phone number must be 13 digits, hire date not greater than sys date and salary not greater than 50,000. while executing im facing error . how to solve this.
create or replace procedure empinsert AS
cursor employee is select length(PHONE_NUMBER),HIRE_DATE,SAL from emp;
e_mobile_no emp.phone_number%type;
e_hire_date emp.hire_date%type;
e_salary emp.sal%type;
Begin
open employee ;
loop
fetch employee into e_mobile_no,e_hire_date,e_salary;
if ( e_mobile_no = 13 and e_hire_date < sysdate and e_salary <=50000 ) then
INSERT INTO EMP (EMP_ID,
EMP_NAME,
EMAIL,
PHONE_NUMBER,
HIRE_DATE,
JOB_ID,
SAL)
values(empinc.nextval, 'ramji','ramji#gmail.com','8975432109875','10/07/2021','JUN_TECH',40000);
else
exit;
end if;
end loop;
end;
/

PL/SQL: I get expression 'I' cannot be used as an assignment target

My code:
create table info(str varchar2(30));
declare
cursor c(job emp_ast.job_id%type, dep emp_ast.department_id%type) is select employee_id
from emp_ast
where job_id=job and department_id=dep;
type t_job is table of emp_ast.job_id%type;
t t_job:=t_job();
emp emp_ast.employee_id%type;
i number(3);
begin
select job_id
bulk collect into t
from emp_ast;
for i in 10..270 loop
for j in 1..t.count loop
open c(i, t(j));
loop
fetch c into emp;
insert into info
values (i||' '||t(j)||' '||emp);
exit when c%notfound;
end loop;
i:=i+10;
end loop;
end loop;
end;
/
I get "expression 'I' cannot be used as an assignment target", reffering to the line where I increment i by 10. I am trying to save the department_id, employee_id and job_id as a string in a table for each department and each job.
At the point where you get that message, i refers to the loop control variable i defined in the line for i in 10..270 loop, not the int(3) variable defined earlier. In PL/SQL a loop definition defines a variable which is only accessible inside the loop, and which you cannot alter. I suggest you change the name of one or the other to make them unique.
EDIT
PL/SQL doesn't provide a way to step by more than 1 in a computed FOR loop. Instead, you will need to compute the desired department number value within the loop:
DECLARE
CURSOR c(job EMP_AST.JOB_ID%TYPE,
dep EMP_AST.DEPARTMENT_ID%TYPE)
IS SELECT EMPLOYEE_ID
FROM EMP_AST
WHERE JOB_ID = job AND
DEPARTMENT_ID = dep;
TYPE t_job IS TABLE OF EMP_AST.JOB_ID%TYPE;
t t_job := t_job();
emp EMP_AST.EMPLOYEE_ID%TYPE;
nDepartment NUMBER;
BEGIN
SELECT job_id
BULK COLLECT INTO t
FROM EMP_AST;
FOR i IN 1..27 LOOP
nDepartment := i * 10;
FOR j IN 1..t.COUNT LOOP
OPEN c(t(j), nDepartment);
LOOP
FETCH c INTO emp;
INSERT INTO info
VALUES (nDepartment || ' ' || t(j) || ' ' || emp);
EXIT WHEN c%notfound;
END LOOP; -- cursor c
CLOSE c;
END LOOP; -- j
END LOOP; -- i
END;
/
Note that in the code above the nDepartment value is computed within the i loop, which now increments from 1 to 27 instead of going from 10 to 270.

unable to fetch results using returning into clause for cursors of record type

I have a procedure which should return 3 dept names and update the start time for these three dept names. This script will be run by parallel threads.
I tried to achieve it using cursors but not returning the result.
Table with inserts:
create table dept (sno number(4), deptname varchar2(40), start_time date)
insert into dept values(1,'DEPT1',NULL);
insert into dept values(1,'DEPT2',NULL);
insert into dept values(1,'DEPT3',NULL);
insert into dept values(2,'DEPT4',NULL);
insert into dept values(2,'DEPT5',NULL);
insert into dept values(2,'DEPT6',NULL);
Approach 1:
TYPE deptname IS RECORD(op_deptname dept.deptname%TYPE);
TYPE cursor_deptname IS REF CURSOR RETURN deptname;
CREATE OR REPLACE PROCEDURE get_deptname(ip_sno IN dept.sno%TYPE, op_cursor OUT cursor_deptname);
IS
vv_dept_name dept.deptname%type;
BEGIN
LOCK TABLE dept IN EXCLUSIVE MODE;
OPEN op_cursor FOR
SELECT deptname
FROM dept
WHERE sno = ip_sno
AND start_time IS NULL
AND ROWNUM <= 3;
LOOP
FETCH op_cursor INTO vv_dept_name;
EXIT WHEN op_cursor%NOTFOUND;
UPDATE dept
SET start_time = sysdate
WHERE deptname = vv_dept_name;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
IF op_cursor%ISOPEN THEN CLOSE op_cursor; END IF;
END;
Proc execution from application.
DECLARE
i deptname;
c_cursor cursor_deptname;
BEGIN
get_deptname(2,c_cursor);
LOOP
FETCH c_cursor INTO i;
DBMS_OUTPUT.PUT_LINE('name:'||i.op_deptname);
EXIT WHEN c_cursor %NOTFOUND;
END LOOP;
END;
With this approach I'm able to update the table with date but dept names are not retrieved in the cursor.
Here's how I understood the question.
Table contents:
SQL> select * from tdept;
SNO DEPTNAME START_TIME
---------- ---------------------------------------- ----------
1 DEPT1
1 DEPT2
1 DEPT3
2 DEPT4
2 DEPT5
2 DEPT6
6 rows selected.
The procedure, which
updates the table
returns refcursor
SQL> create or replace procedure get_deptname
2 (ip_sno in tdept.sno%type,
3 op_cursor out sys_refcursor
4 )
5 is
6 begin
7 update tdept t set
8 t.start_time = sysdate
9 where t.sno = ip_sno;
10
11 open op_cursor for
12 select deptname
13 from tdept
14 where sno = ip_sno;
15 end;
16 /
Procedure created.
Execution & the result:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> var lout refcursor
SQL>
SQL> exec get_deptname(2, :lout);
PL/SQL procedure successfully completed.
SQL> select * from tdept;
SNO DEPTNAME START_TIME
---------- ---------------------------------------- -------------------
1 DEPT1
1 DEPT2
1 DEPT3
2 DEPT4 10.04.2018 20:32:23
2 DEPT5 10.04.2018 20:32:23
2 DEPT6 10.04.2018 20:32:23
6 rows selected.
SQL> print lout
DEPTNAME
----------------------------------------
DEPT4
DEPT5
DEPT6
SQL>

PL/SQL trigger does not change value of table attribute

this is a follow up question from my previous post:
PL/SQL Triggers with aggregate function
I have a trigger named updategpa that updates the gpa inside the student table whenever grades get changed in the course table.
This trigger gets called as a result of 'updateGrades' trigger that updates the course table when assignments table receives new data.
Problem: gpa is 0 before and after the trigger is called?
Could anyone say what is wrong with my trigger?
drop table courses;
drop table student;
drop table assignments;
create table student (sid integer, sname char(10), saddress char(10), gpa integer);
create table courses (sid integer, cid integer, cgrade integer);
create table assignments ( sid integer, cid integer, aid integer, agrade integer);
insert into student (sid, sname, saddress, gpa) values (1, 'Mike', 'Brighton', 0);
insert into courses (sid, cid, cgrade) values(1, 2000, 0);
insert into assignments values(1, 2000, 1, 70);
insert into assignments values(1, 2000, 2, 80);
create or replace trigger updateGrades before insert or update or delete of agrade on assignments for each row
begin
if inserting then
update courses set cgrade = :new.agrade where cid = :new.cid and sid = :new.sid;
elsif updating then
update courses set cgrade = :old.agrade + (:new.agrade - :old.agrade)
where cid = :new.cid and sid = :new.sid;
elsif deleting then
delete from courses where cid = :new.cid and sid = :new.sid;
end if;
end;
/
show errors;
create or replace trigger updategpa after insert or update or delete of cgrade on courses for each row
begin
if inserting then
update student set gpa =
(select avg(cgrade) from courses inner join student on courses.sid = student.sid
)
where sid = 1;
elsif inserting then
update student set gpa =
(select avg(cgrade) from courses inner join student on courses.sid = student.sid
)
where sid = 1;
elsif deleting then
delete from student where sid = :new.sid;
end if;
end;
/
show errors;
select * from courses;
select * from student;
select * from assignments;
update assignments set agrade = agrade + 5;
select * from courses;
select * from student;
select * from assignments;
console output:
SQL> select * from student;
SID SNAME SADDRESS GPA
---------- ---------- ---------- ----------
1 Mike Brighton 0
SQL> select * from assignments;
SID CID AID AGRADE
---------- ---------- ---------- ----------
1 2000 1 70
1 2000 2 80
SQL>
SQL> update assignments set agrade = agrade + 5;
2 rows updated.
SQL>
SQL> select * from courses;
SID CID CGRADE
---------- ---------- ----------
1 2000 85
SQL> select * from student;
SID SNAME SADDRESS GPA
---------- ---------- ---------- ----------
1 Mike Brighton 0
SQL> select * from assignments;
SID CID AID AGRADE
---------- ---------- ---------- ----------
1 2000 1 75
1 2000 2 85
The root of the problem is your updategpa trigger needs to track every change to COURSES at the row level, then aggregate those changes to maintain a running total for each student. This is a good use case for a compound trigger. Find out more.
create or replace trigger updategpa
for update or update or insert on courses compound trigger
type cid_nt is table of courses%rowtype;
upd_recs cid_nt;
before statement is
begin
upd_recs := cid_nt();
end before statement;
before each row is
begin
upd_recs.extend();
if inserting or updating then
upd_recs(upd_recs.count()).sid := :new.sid;
else
upd_recs(upd_recs.count()).sid := :old.sid;
end if;
end before each row;
after each row is
begin
null;
end after each row;
after statement is
begin
for idx in 1 .. upd_recs.count() loop
update student
set gpa = (select avg(cgrade)
from courses
where courses.sid = student.sid )
where sid = upd_recs(idx).sid;
end loop;
end after statement;
end;
Here is a LiveSQL demo (free OTN account required).

PL/SQL: How to delete records in a specific manner, for example if records of specific type X exist, delete all but one record

I'm trying to create a PL/SQL procedure where by I delete records that are grouped and selected by cursor but I only want one record remaining. I want to delete first by Xcomment, if there are multiple entries with id_number, activity_code, start_dt, activity_participation_code exist, then delete all but ONE entry with blank/null xcomment. If there are multiple entries with blank xcomment, then delete all but one with blank table_nmb. If multiple entries with blank table_nmb then delete highest sequence until only one is left. Essentially, I only want one record per all these fields. I'm having trouble thinking of how to do this so any help would be appreciated.
Here is my code so far:
Create Or Replace Function Y_Cleanup_Cursor
Return Sys_Refcursor
As
My_Cursor Sys_Refcursor;
Begin
Open My_Cursor For
Select Q.Id_Number, Q.Activity_Code, Q.Start_Dt, Q.Activity_Participation_Code, Q.Rec_Count, A.Xcomment, A.Table_Nmb, A.Xsequence
From (Select Id_Number, Activity_Code, Start_Dt, Activity_Participation_Code, Count(0) As Rec_Count
From Activity A
Group By Id_Number, Activity_Code, Start_Dt, Activity_Participation_Code
Having Count(0) > 1) Q,
Activity A
Where
Q.Id_Number = A.Id_Number And
Q.Activity_Code = A.Activity_Code And
Q.Start_Dt = A.Start_Dt And
Q.Activity_Participation_Code = A.Activity_Participation_Code;
Return My_Cursor;
End Y_Cleanup_Cursor;
Create Or Replace Procedure Help_Me_Please(Code In Varchar2)
Is
-- Declare Variables
-- I Stands For Internal Variable
L_Cursor Sys_Refcursor;
I_Id_Number Varchar2(10 Byte);
I_Xsequence Number (6);
I_Activity_Code Varchar2(05 Byte);
I_Start_Dt Varchar2(08 Byte);
I_Activity_Participation_Code Varchar2(02 Byte);
I_Table_Nmb Varchar2(15 Byte);
I_Xcomment Varchar2(255 Byte);
I_Rec_Count Number (6);
L_Counter Integer;
Begin
L_Cursor := Y_Cleanup_Cursor;
Loop
Fetch L_Cursor Into
I_Id_Number, I_Activity_Code, I_Start_Dt, I_Activity_Participation_Code, I_Rec_Count, I_Xcomment, I_Table_Nmb, I_Xsequence;
Select Count (Id_Number)
Into L_Counter
From Activity Where
Id_Number = I_Id_Number
And Activity_Code = I_Activity_Code
And Start_Dt = I_Start_Dt
And Activity_Participation_Code = I_Activity_Participation_Code
And Trim(Xcomment) Is Null;
If L_Counter <> I_Rec_Count Then
Begin
Delete From Activity
Where
Id_Number = I_Id_Number
And Activity_Code = I_Activity_Code
And Start_Dt = I_Start_Dt
And Activity_Participation_Code = I_Activity_Participation_Code
And Trim(Xcomment) Is Null;
end;
End If;
Exit When L_Cursor%Notfound;
End Loop;
Close L_Cursor;
End Help_Me_Please;
From what I gather you want to delete all rows except 1 where there are repeating columns
first make sure to backup your table:
create table [backup_table] as select * from [table];
Try This:
DELETE FROM backup_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM backup_table
GROUP BY [col1], [col2]);
Col1 and col2, etc are the columns that should be identical

Resources