Trigger DML in oracle database - oracle

I have problem with my dml trigger on oracle database.I want to launch trigger when i update first_name or last name on employees table in hr schema. During execution trigger i Have error ORA-00060: Please help. Idon't have any ideas how can i fix it.enter image description here
CREATE OR replace TRIGGER up_sal
BEFORE UPDATE OF first_name, last_name ON employees
FOR EACH ROW
DECLARE
PRAGMA autonomous_transaction;
var_sal employees.salary%TYPE;
var_avg NUMBER;
var_emp_id NUMBER;
BEGIN
SELECT salary
INTO var_sal
FROM employees
WHERE first_name = :NEW.first_name
OR last_name = :NEW.last_name;
SELECT Avg(salary)
INTO var_avg
FROM employees
WHERE department_id IN( :OLD.department_id );
IF var_sal < var_avg THEN
var_emp_id := :OLD.employee_id;
UPDATE employees
SET salary = var_avg * 1.1
WHERE employee_id = var_emp_id;
COMMIT;
END IF;
END;

You can use:
CREATE TRIGGER up_sal
BEFORE UPDATE OF first_name, last_name ON employees
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
var_avg employees.salary%TYPE;
BEGIN
SELECT Avg(salary)
INTO var_avg
FROM employees
WHERE department_id IN( :OLD.department_id );
IF :NEW.salary < var_avg THEN
:NEW.salary := var_avg * 1.1;
END IF;
COMMIT;
END;
/
Which, for the sample data:
CREATE TABLE employees (
department_id NUMBER,
first_name VARCHAR2(20),
last_name VARCHAR2(20),
salary NUMBER
);
INSERT INTO employees (department_id, first_name, last_name, salary)
SELECT 1, 'Alice', 'Abbot', 90 FROM DUAL UNION ALL
SELECT 1, 'Betty', 'Baron', 95 FROM DUAL UNION ALL
SELECT 1, 'Carol', 'Count', 105 FROM DUAL UNION ALL
SELECT 1, 'Debra', 'Duke', 110 FROM DUAL;
Then if you do:
UPDATE employees
SET first_name = TRIM(first_name),
last_name = TRIM(last_name);
Then:
SELECT * FROM employees;
Outputs:
DEPARTMENT_ID
FIRST_NAME
LAST_NAME
SALARY
1
Alice
Abbot
110
1
Betty
Baron
110
1
Carol
Count
105
1
Debra
Duke
110
db<>fiddle here

Related

Trigger that changes value in another table /plsql

I've created two tables: Employees and Departments
CREATE TABLE EMP
( emp_id number(3) PRIMARY KEY,
dept_id Number(3) NOT NULL,
emp_name Varchar2(50) NOT NULL,
address Varchar2(100),
phone Varchar2(20) NOT NULL,
salary Number(8,2) NOT NULL,
CONSTRAINT fk_DEPT FOREIGN KEY (dept_id) REFERENCES DEPT(DEPT_ID));
CREATE TABLE DEPT
( dept_id number(3) PRIMARY KEY,
dept_name varchar2(50) NOT NULL,
emp_cnt Number(3) NOT NULL)
I need to create trigger that changes value in DEPT.emp_cnt after inserting or deleting data in EMP table.
Here is my attempt
create or replace trigger add_emp_to_the_dep
after insert or delete on EMP
for each row
begin
update DEPT
set emp_cnt = :new.emp_id
where DEPT.dept_id = :new.dept_id;
if INSERTING then
emp_cnt += 1;
else DELETING then
emp_cnt -= 1;
end if;
end;
Wrong syntax; there's no such thing as emp_cnt += 1; in Oracle's PL/SQL.
Try something like this instead:
create or replace trigger add_emp_to_the_dep
after insert or delete on emp
for each row
begin
if inserting then
update dept set
emp_cnt = emp_cnt + 1
where dept_id = :new.dept_id;
elsif deleting then
update dept set
emp_cnt = emp_cnt - 1
where dept_id = :old.dept_id;
end if;
end;
/
You can use a compound trigger to collate the changes and make the minimum number of updates:
CREATE TRIGGER add_emp_to_the_dep
FOR INSERT OR UPDATE OR DELETE ON emp
COMPOUND TRIGGER
TYPE ids_type IS TABLE OF EMP.DEPT_ID%TYPE;
TYPE cnt_type IS TABLE OF PLS_INTEGER;
TYPE idx_type IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;
ids ids_type := ids_type();
cnts cnt_type := cnt_type();
idxs idx_type := idx_type();
PROCEDURE modify_dept_cnt (
id EMP.DEPT_ID%TYPE,
cnt PLS_INTEGER
)
IS
BEGIN
IF id IS NULL THEN
RETURN;
END IF;
IF NOT idxs.EXISTS(id) THEN
ids.EXTEND;
cnts.EXTEND;
ids(ids.COUNT) := id;
cnts(cnts.COUNT) := cnt;
idxs(id) := ids.COUNT;
ELSE
cnts(idxs(id)) := cnts(idxs(id)) + cnt;
END IF;
END modify_dept_cnt;
AFTER EACH ROW
IS
BEGIN
modify_dept_cnt(:NEW.DEPT_ID, 1);
modify_dept_cnt(:OLD.DEPT_ID, -1);
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
FORALL i IN 1 .. ids.count
UPDATE dept
SET emp_cnt = emp_cnt + cnts(i)
WHERE dept_id = ids(i);
END AFTER STATEMENT;
END;
/
Then, if you do:
INSERT INTO emp (emp_id, dept_id, emp_name, phone, salary)
SELECT 1, 1, 'Alice', '0', 100 FROM DUAL UNION ALL
SELECT 2, 1, 'Betty', '1', 100 FROM DUAL UNION ALL
SELECT 3, 2, 'Carol', '2', 100 FROM DUAL UNION ALL
SELECT 4, 1, 'Debra', '3', 100 FROM DUAL UNION ALL
SELECT 5, 3, 'Emily', '4', 100 FROM DUAL UNION ALL
SELECT 6, 3, 'Fiona', '5', 100 FROM DUAL;
It will collate all the changes and UPDATE the DEPT table only 3 times, as employees for 3 unique DEPT_ID are added, rather than performing 6 updates, one for each inserted row.
db<>fiddle here
create or replace trigger add_emp_to_the_dep
after insert or delete on emp
for each row
begin
if inserting then
update dept set
emp_cnt = emp_cnt + 1
where dept_id = :new.dept_id;
elsif deleting then
update dept set
emp_cnt = emp_cnt - 1
where dept_id = :old.dept_id;
end if;
end;

oracle package and trigger

Create a database that contains four tables department, employee,
address and contact info, then Store 5 Records in each table.
Write the PL/SQL Statement for the following:
Store a record into employee, address and contact using trigger.
Retrieve departments and their employees using package
..........................
this is my code , what am i doing wrong ?
-- Question 2 :
-- Create Tables :
CREATE TABLE address (
code int primary key,
city varchar2(30),
street varchar2(30)
);
create table Department (
DepId int primary key ,
Dep_Name varchar2(30) ,
Dep_adress varchar(30));
create table Employee (
Emp_Id int primary key ,
firstName varchar2(30),
lastName varchar2(30),
salary int,
Dep_Id int references Department (DepId),
AdCode int references address (code));
CREATE TABLE contact_info (
email varchar2(30) primary key ,
phone int ,
EmpId int references Employee (Emp_Id));
-- insert :
insert into Department values (1,'IT','Amman');
insert into Department values (2,'CS','Jerash');
insert into Department values (3,'accounting','Amman');
insert into Department values (4,'managment','Amman');
insert into Department values (5,'employment','Amman');
insert into address values (50,'Amman','AAA');
insert into address values (60,'Amman','AAB');
insert into address values (70,'Amman','AAC');
insert into address values (80,'Jerash','AAD');
insert into address values (90,'Irbid','AAE');
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (1,'john' , 'samo' , 1000 , (select DepId from Department where Dep_Name = 'IT'),(select code from address where street = 'AAA'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (2,'mark' , 'wol' , 2000 , (select DepId from Department where Dep_Name = 'IT'),(select code from address where street = 'AAB'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (3,'ahmad' , 'moh' , 1100 , (select DepId from Department where Dep_Name = 'IT'),(select code from address where street = 'AAC'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (4,'maher' , 'imk' , 1700 , (select DepId from Department where Dep_Name = 'CS'),(select code from address where street = 'AAD'));
insert into Employee (Emp_Id,firstName,lastName,salary,Dep_Id,AdCode) values (5,'ali' , 'geh' , 1200 , (select DepId from Department where Dep_Name = 'CS'),(select code from address where street = 'AAE'));
insert into contact_info values ('john#gmail.com',0785602200, (select Emp_Id from Employee where salary = 1000));
insert into contact_info values ('mark#gmail.com',0785602201, (select Emp_Id from Employee where salary = 2000));
insert into contact_info values ('ahmad#gmail.com',0785602202, (select Emp_Id from Employee where salary = 1100));
insert into contact_info values ('maher#gmail.com',0785602203, (select Emp_Id from Employee where salary = 1700));
insert into contact_info values ('ali#gmail.com',0785602204, (select Emp_Id from Employee where salary = 1200));
-- trigger :
CREATE OR REPLACE TRIGGER add_rec
AFTER INSERT
ON Department
FOR EACH ROW
DECLARE
Empl_id NUMBER;
BEGIN
INSERT INTO address(code,city,street)
VALUES(:NEW.code, :NEW.city, :NEW.street)
INSERT INTO contact_info(email, phone, EmpId)
VALUES(:NEW.email, :NEW.phone, :NEW.EmpId)
INSERT INTO Employee(Emp_Id,firstName,lastName,salary,Dep_Id,AdCode)
VALUES(:NEW.Emp_Id, :NEW.firstName, :NEW.lastName, :NEW.salary, :NEW.lastName, :NEW.Dep_Id, :NEW.AdCode)
END;
-- Package :
set serveroutput on;
CREATE OR REPLACE PACKAGE emp_dept AS
TYPE EmpDept IS RECORD (employee_id number , last_name varchar2(25) , department_id number);
CURSOR dept_employees RETURN EmpDept;
PROCEDURE department_employee;
END emp_dept;
/
CREATE OR REPLACE PACKAGE BODY emp_dept AS
CURSOR dept_employees RETURN EmpDept is (select employee_id , last_name , d.department_id from Employee e join Department d on e.department_id = d.department_id);
rec EmpDept;
PROCEDURE department_employee
is
begin
open dept_employees;
loop
fetch dept_employees into rec;
exit when dept_employees%notfound;
dbms_output.put_line(rpad(rec.employee_id,5,' ') ||rpad(rec.last_name,12,' ') || rec.department_id);
end loop;
end;
END emp_dept;
/
execute EMP_DEPT.DEPARTMENT_EMPLOYEE;
As of package, when you fix errors (wrong column names), it compiles and returns result:
SQL> CREATE OR REPLACE PACKAGE emp_dept AS
2 TYPE empdept IS RECORD (
3 employee_id NUMBER,
4 last_name VARCHAR2(25),
5 department_id NUMBER
6 );
7 CURSOR dept_employees RETURN empdept;
8 PROCEDURE department_employee;
9
10 END emp_dept;
11 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY emp_dept AS
2
3 CURSOR dept_employees RETURN empdept IS
4 ( SELECT
5 emp_id,
6 lastname,
7 d.depid
8 FROM
9 employee e
10 JOIN department d ON e.dep_id = d.depid
11 );
12
13 rec empdept;
14
15 PROCEDURE department_employee IS
16 BEGIN
17 OPEN dept_employees;
18 LOOP
19 FETCH dept_employees INTO rec;
20 EXIT WHEN dept_employees%notfound;
21 dbms_output.put_line(rpad(rec.employee_id, 5, ' ')
22 || rpad(rec.last_name, 12, ' ')
23 || rec.department_id);
24
25 END LOOP;
26
27 END;
28
29 END emp_dept;
30 /
Package body created.
Testing:
SQL> EXECUTE emp_dept.department_employee;
1 samo 1
2 wol 1
3 moh 1
4 imk 2
5 geh 2
PL/SQL procedure successfully completed.
SQL>
As of trigger: that question/requirement doesn't make any sense. How would you enter rows into all those tables, based on 3 columns you enter into the department table? You just don't have any values to insert ... From my point of view, either you misinterpreted the problem, or it can't be done as you described it.

Procedure to delete employee records from employee table

Create a procedure that deletes employee records from the Employee table. Get the department name as an input parameter. Delete the employee records who belongs to that department.
Display the count of employee records that were deleted. If the respective department was not found, then raise "DeptNotFoundException" and print the message 'No Records found.'
Assume the Employee table has been already created and a few records have been inserted.
EMPLOYEE:
Column name Data type Constraints
EMP_ID NUMBER(5) PK
EMP_NAME VARCHAR2(25) NOT NULL
SALARY NUMBER(10,2)
DEPT VARCHAR2(25)
EMP_ID EMP_NAME SALARY DEPT
------ -------- ------- -----
101 Tom 54000 MECH
102 William 43000 CSE
103 John 34560 MECH
104 Smith 56000 CSE
105 Steve 23450 IT
Functional Requirements:
PROCEDURE DELETE_EMPLOYEE( v_dept IN EMPLOYEE.dept%TYPE)
Sample Output:
2 Employee record(s) got deleted.
(Hint: Data is case sensitive. Use '/' to terminate the PLSQL block)
I have tried to solve this using my own logic, but it is showing error(out of 2 test only one is passed) can anyone point out the mistake?
set serveroutput on;
create or replace PROCEDURE DELETE_EMPLOYEE(v_dept IN EMPLOYEE.dept%TYPE)
is
temp number;
DEPTNOTFOUNDEXCEPTION Exception;
begin
select count(dept) into temp from EMPLOYEE where dept=v_dept;
delete from EMPLOYEE where dept=v_dept;
if temp>=1 then
dbms_output.put_line(temp||' Employee record(s) got deleted.');
else
raise DEPTNOTFOUNDEXCEPTION;
end if;
exception
when DEPTNOTFOUNDEXCEPTION then
dbms_output.put_line('No Records Found.');
end;
/
As others have pointed out, you can use SQL%ROWCOUNT and generally tidy up a bit but it doesn't look "wrong".
CREATE TABLE employee (
emp_id NUMBER(5) PRIMARY KEY
, emp_name VARCHAR2(25) NOT NULL
, salary NUMBER(10,2)
, dept VARCHAR2(25)
);
INSERT INTO employee (emp_id, emp_name, salary, dept) VALUES (101, 'Tom', 54000, 'MECH');
INSERT INTO employee (emp_id, emp_name, salary, dept) VALUES (102, 'William', 43000, 'CSE');
INSERT INTO employee (emp_id, emp_name, salary, dept) VALUES (103, 'John', 34560, 'MECH');
INSERT INTO employee (emp_id, emp_name, salary, dept) VALUES (104, 'Smith', 56000, 'CSE');
INSERT INTO employee (emp_id, emp_name, salary, dept) VALUES (105, 'Steve', 23450, 'IT');
DECLARE
dept_not_found EXCEPTION;
PROCEDURE delete_employee (p_dept IN employee.dept%TYPE) IS
BEGIN
DELETE employee
WHERE dept = p_dept;
IF SQL%ROWCOUNT = 0 THEN
RAISE dept_not_found;
END IF;
dbms_output.put_line(SQL%ROWCOUNT||' Employee record(s) got deleted.');
END delete_employee;
BEGIN
delete_employee('CSE');
delete_employee('ZZZ');
EXCEPTION
WHEN dept_not_found THEN
dbms_output.put_line('No Records Found.');
END;
/
2 Employee record(s) got deleted.
No Records Found.
PL/SQL procedure successfully completed.
Only an advice: Stop using raise when you want to output something. raise have always much more workload than:
if (sql%rowcount = 0) then
DBMS_Output.Put_Line('No Record Found');
return;
end if;

View based on function, correct query treated as bad

This is code:
CREATE TABLE emp_where (where_clause VARCHAR2(4000));
INSERT INTO emp_where (where_clause)
VALUES ('first_name=''KING'' or department_id = 20');
commit;
CREATE OR REPLACE TYPE t_emp_rec AS OBJECT (
EMPLOYEE_ID NUMBER(4),
FIRST_NAME VARCHAR2(10),
JOB_ID VARCHAR2(9),
MANAGER_ID NUMBER(4),
HIRE_DATE DATE,
SALARY NUMBER(7,2),
DEPARTMENT_ID NUMBER(2)
);
/
CREATE OR REPLACE TYPE t_emp_tab AS TABLE OF t_emp_rec;
/
CREATE OR REPLACE FUNCTION emp_fn RETURN t_emp_tab
PIPELINED IS
l_sql VARCHAR2(32767);
l_where VARCHAR2(4000);
TYPE l_cur_type IS REF CURSOR;
l_cur l_cur_type;
l_rec employees%ROWTYPE;
BEGIN
SELECT where_clause INTO l_where FROM emp_where;
l_sql := 'SELECT * FROM employees WHERE ' || l_where;
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur
INTO l_rec;
EXIT WHEN l_cur%NOTFOUND;
PIPE ROW(t_emp_rec(EMPLOYEE_ID => l_rec.EMPLOYEE_ID
,FIRST_NAME => l_rec.FIRST_NAME
,JOB_ID => l_rec.JOB_ID
,MANAGER_ID => l_rec.MANAGER_ID
,hire_date => l_rec.hire_date
,SALARY => l_rec.SALARY
,DEPARTMENT_ID => l_rec.DEPARTMENT_ID));
END LOOP;
RETURN;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20000, SQLERRM || chr(10) || l_sql);
END;
/
CREATE OR REPLACE VIEW emp_vw AS
UPDATE emp_where SET where_clause = 'EMPLOYEE_ID BETWEEN 100 and 200';
COMMIT;
SELECT * FROM emp_vw;
When I execute SELECT * FROM emp_vw; with this clause: EMPLOYEE_ID BETWEEN 100 and 200
Oracle gives me an error
ORA-20000: ORA-06502: PL/SQL: numeric or value error: number precision too large
SELECT * FROM employees WHERE EMPLOYEE_ID BETWEEN 100 and 200
But when I execute query itself (SELECT * FROM employees WHERE EMPLOYEE_ID BETWEEN 100 and 200) there is no error. Another scenario -
when clause is 'deparment_id = 20', executing a view is correct. But when I change '=' to '>' (department_id > 20) - numeric or value error: number precision too large.
Can someone explain me how this is happening?
If I run:
DESCRIBE employees
Then I get the output:
Name Null Type
-------------- -------- ------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
If you compare it to your t_emp_rec object then you will see that most of the object attributes are a smaller size than the table columns.
Change the object to have the same sizes and it should work:
CREATE OR REPLACE TYPE t_emp_rec AS OBJECT (
EMPLOYEE_ID NUMBER(6),
FIRST_NAME VARCHAR2(20),
JOB_ID VARCHAR2(10),
MANAGER_ID NUMBER(6),
HIRE_DATE DATE,
SALARY NUMBER(8,2),
DEPARTMENT_ID NUMBER(4)
);
/

Assigning data in a new table to an existing foreign key in a for loop

I was wondering if there was a way to assign new data in a table to an existing foreign key.
For example if I use the following loop to assign randomly generated numbers to columns in the customer table, how would I be able to link cust_id, which I have assigned as a foreign key in the Sales table, with new data created each time a new sale is made?
CUSTOMER:
DECLARE
v_cust_id NUMBER(4) NOT NULL := 0000;
v_cust_name VARCHAR2(30);
v_cust_add VARCHAR2(30);
v_phone VARCHAR2(10);
BEGIN
FOR v IN 1 .. 2000 --Loop 2000 times to create data for the 2000 customers in the database.
LOOP
v_cust_id := v_cust_id + 1;
v_cust_name := dbms_random.string('U',5);
v_cust_add := dbms_random.string('A',15);
v_phone := dbms_random.value(1000000,9999999);
INSERT INTO customer (cust_id, cust_name, cust_add, phone)
VALUES (v_cust_id, v_cust_name, v_cust_add, v_phone);
END LOOP;
END;
/
SALES:
DECLARE
v_sale_id NUMBER(4) NOT NULL := ;
v_sale_price NUMBER(8,2);
v_sale_date DATE;
v_no_of_prods NUMBER(4);
v_prod_id NUMBER(4);
v_desp_id NUMBER(4);
v_cust_id NUMBER(4);
BEGIN
FOR v IN 1 .. 10
LOOP
v_sale_id :=
v_sale_price
v_sale_date :=
v_no_of_products :=
v_prod_id :=
v_desp_id :=
v_cust_id :=
INSERT INTO sales (sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
VALUES (v_sale_id, v_sale_price, v_sale_date, v_no_of_prods, v_prod_id, v_desp_id, v_cust_id);
END LOOP;
END;
\
You are generating test data to do some kind of performance test?
Let's first generate 2000 customers.
(untested)
insert into customer
(cust_id, cust_name, cust_add, phone)
select
level l,
dbms_random.string('U',5),
dbms_random.string('A',15),
dbms_random.value(1000000,9999999)
from dual
connect by level <= 2000;
Now you can genereate sales data by selecting from the customer table:
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_random. ...., cust_id
from customer;
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_random. ...., cust_id
from customer
where mod(cust_id,2) =0;
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_radom. ...., cust_id
from customer
where mod(cust_id,7) =0;
insert into sales
(sale_id, sale_price, sale_date, no_of_prods, prod_id, desp_id, cust_id)
select sale_id_sequence.nextval , dbms_random. ...., cust_id
from customer
where mod(cust_id,13) =0;
commit;
I assume there is a sequence to create a sale id.
edit1 improvement:
create table customer
( cust_id number(10)
, cust_name varchar2(50)
, cust_add varchar2(30)
, cust_phone varchar2(10)
);
create sequence cust_id_seq;
create table sales
( sale_id number(10)
, prod_no number(10)
, cust_id number(10)
);
create sequence sale_id_seq;
begin
insert into customer
select cust_id_seq.nextval
, dbms_random.string('U',5)
, dbms_random.string('A',15)
, trunc(dbms_random.value(1000000,9999999))
from dual
connect by level < 2000;
for i in 1..10 loop
insert into sales
select sale_id_seq.nextval
, trunc(dbms_random.value(1,100))
, cust_id
from customer;
insert into sales
select sale_id_seq.nextval
, trunc(dbms_random.value(1,100))
, cust_id
from customer
where mod(cust_id+i,2)=0;
insert into sales
select sale_id_seq.nextval
, trunc(dbms_random.value(1,100))
, cust_id
from customer
where mod(cust_id+i,7)=0;
end loop;
end;
/
commit;
select count(*) from customer;
select count(*) from sales;

Resources