PL/SQL creating a trigger problems - sql

I have to create a trigger on an Employee table. If an INSERT or UPDATE statement is issued for the Employee table the trigger launches and makes sure that the value of 'salary' field meets the criteria in the job_min_sal table. After trying over and over I got a mutating table error and now am very frustrated and don't know what to do.
JOB_MIN_SALARY TABLE:
JOB VARCHAR2(50) PRIMARY KEY
MIN_SAL NUMBER(7,2) NOT NULL
The JOB_MIN_SAL table is populated with a variety of job titles and salaries. I am confused working with my trigger and wondering if I could get some assistance where to go from here
CREATE OR REPLACE TRIGGER employee_job_salary
BEFORE INSERT OR UPDATE OF SALARY on employee
FOR EACH ROW
DECLARE
v_salary NUMBER;
BEGIN
SELECT minimum_salary
INTO v_salary
FROM job_min_salary
WHERE UPPER(job) = UPPER(:NEW.job);
I know I am really far off I am just looking for help as for what this requires and what steps I need to take to get this. Thanks!
The EMPLOYEE table:
(
EMPLOYEE_ID NUMBER(4)
EMPLOYEE_NAME VARCHAR2(20)
JOB VARCHAR2(50)
MANAGER_ID NUMBER(4)
HIRE_DATE DATE
SALARY NUMBER(9)
COMMISION NUMBER(9)
DEPARTMENT_ID NUMBER(4)
);

i am supposing you are doing something like
comparing new salary with min salary criterion and update only if :new.SALARY >= v_salary
what are you doing if this is not met, are u trapping an exception or just ignoring the error or returning an error code to debug.
post more info

CREATE TABLE job_min_salary
(
job VARCHAR2(50) PRIMARY KEY,
min_sal NUMBER(7,2) NOT NULL
);
INSERT INTO job_min_salary VALUES('CEO','100');
-- 1 rows inserted.
CREATE TABLE employee
(
employee_id NUMBER(4),
employee_name VARCHAR2(20),
job VARCHAR2(50),
manager_id NUMBER(4),
hire_date DATE,
salary NUMBER(9),
commision NUMBER(9),
department_id NUMBER(4)
);
INSERT INTO employee VALUES(1, 'Name', 'CEO', 1, TO_DATE('2000-01-01', 'YYYY-MM-DD'), 80, 80, 1);
-- 1 rows inserted.
CREATE OR REPLACE TRIGGER employee_job_salary
BEFORE INSERT OR UPDATE OF salary ON employee
FOR EACH ROW
DECLARE
v_salary NUMBER(1);
BEGIN
SELECT 1
INTO v_salary
FROM job_min_salary
WHERE UPPER(job) = UPPER(:NEW.job)
AND :NEW.salary >= min_sal;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20999, 'Salary value is too low for given job');
END;
-- TRIGGER EMPLOYEE_JOB_SALARY compiled
SELECT * FROM employee;
-- 1 Name CEO 1 2000-01-01 00:00:00 80 80 1
UPDATE employee
SET salary = 10
WHERE job = 'CEO';
-- ORA-20999: Salary value is too low for given job
UPDATE employee
SET salary = 100
WHERE job = 'CEO';
-- 1 rows updated.
SELECT * FROM employee;
-- 1 Name CEO 1 2000-01-01 00:00:00 100 80 1

Related

Why am I getting error in trigger when I update salary of employee?

create or replace TRIGGER CHECK_GRADE
BEFORE INSERT OR UPDATE ON EMPLOYEE
FOR EACH ROW
DECLARE
LoSal NUMBER(8, 2);
HiSal NUMBER(8, 2);
Letter VARCHAR(1);
Sal NUMBER(8, 2);
BEGIN
IF(:NEW.GLetter IS NOT NULL)
THEN
Sal := NVL(:NEW.Salary, -1);
SELECT MinSalary, MaxSalary
INTO LoSal, HiSal
FROM GRADE
WHERE GLetter = :NEW.GLetter;
IF(NOT(Sal BETWEEN LoSal AND HiSal))
THEN
RAISE_APPLICATION_ERROR(-20001, 'Salary does not correspond to grade error! ' );
END IF;
ELSIF(:NEW.Salary IS NOT NULL)
THEN
SELECT GLetter
INTO Letter
FROM GRADE
WHERE :NEW.Salary BETWEEN MinSalary AND MaxSalary;
:NEW.GLetter := Letter;
END IF;
END;
This is the sql statement I run
basically what I want to do is that when a user tries to update the salary of an employee the trigger will automatically fill the grade of the employee which corresponds to his/her salary
update employee set salary = 25500 where employeeid = 1006;
Table for employee
has attributes like name department name and others and salary and grade
Table for Grade has
minsalary
max salary
gradeletter
Error starting at line : 2 in command -
update employee set salary = 25500 where employeeid = 1006
Error report -
ORA-20001: Salary does not correspond to grade error!
ORA-06512: at "gh12345.CHECK_GRADE", line 18
ORA-04088: error during execution of trigger 'gh12345.CHECK_GRADE'
The error is being raised because of this part here:
SELECT MinSalary, MaxSalary
INTO LoSal, HiSal
FROM GRADE
WHERE GLetter = :NEW.GLetter;
IF(NOT(Sal BETWEEN LoSal AND HiSal))
THEN
RAISE_APPLICATION_ERROR(-20001, 'Salary does not correspond to grade error! ' );
END IF;
you are setting the salary to the value 25500 which is out of the range defined in this table GRADE.
So you should do a query on this table and define a value for Salary that is within this range, or update the GRADE table for your new disired range.

Table not updating when trigger is fired

I have 2 tables:
Employees(Employee_ID, First_name, Last_name, Email, Hire_date, Department_ID)
Departments(Department_ID, Department_name, Total_employees).
I have created trigger that fires after inserting an employee to employees table. It increases the total_employees column by 1 for :new inserted Department_ID.
Trigger:
create or replace TRIGGER emp_count_department
AFTER INSERT OR DELETE ON EMPLOYEES
for each row
DECLARE
counter NUMBER;
BEGIN
if inserting then
select total_employees into counter
from departments
where department_id = :new.department_id;
update departments
set total_employees = counter+1
where department_id = :new.department_id;
elsif deleting then
select total_employees into counter
from departments
where department_id = :old.department_id;
update departments
set total_employees = counter-1
where department_id = :old.department_id;
end if;
END;
When I try to insert an employee, it is inserted but it doesn't increase total_employees by 1 in departments table. When I try to insert an employee with a department_ID that doesn't exist in departments table, it gives me error. So the trigger is working but not increasing the total numbers by 1.
Try this:
create or replace TRIGGER emp_count_department
AFTER INSERT OR DELETE ON EMPLOYEES
for each row
BEGIN
if inserting then
update departments
set total_employees = total_employees+1
where department_id = :new.department_id;
elsif deleting then
update departments
set total_employees = total_employees-1
where department_id = :old.department_id;
end if;
END;
I did simple testing of your code in Oracle 11g and it seems to work.
Please check if you missed in adding information.
create table emp1(emp_id number ,dept_id number);
create table dept1 (dept_id number ,total_emp number);
CREATE OR replace TRIGGER emp_count_dept
AFTER INSERT OR DELETE ON emp1
FOR EACH ROW
DECLARE
counter NUMBER;
BEGIN
IF inserting THEN
UPDATE dept1
SET total_emp = total_emp + 1
WHERE dept_id = :new.dept_id;
ELSIF deleting THEN
UPDATE dept1
SET total_emp = total_emp - 1
WHERE dept_id = :old.dept_id;
END IF;
END;
insert into dept1 values(10,10);
insert into emp1 values(10,10);
select * from dept1;
DEPT_ID TOTAL_EMP
10 11
I think you are missing an important use case, what if the employees table is updated and the dept_id changes. To expand upon the answer #pmdba gave, I think the following code would work best.
CREATE OR REPLACE TRIGGER emp_count_dept
AFTER INSERT OR UPDATE OF DEPT_ID OR DELETE ON employees
FOR EACH ROW
BEGIN
IF :new.dept_id IS NOT NULL THEN
UPDATE departments d
SET d.total_employees = NVL(d.total_employees, 0)+1
WHERE d.department_id = :new.dept_id;
END IF;
IF :old.dept_id IS NOT NULL THEN
UPDATE departments d
SET d.total_employees = d.total_employees - 1
WHERE d.department_id = :old.dept_id;
END IF;
END;
/
This takes care of UPDATE statements now. And also takes care of the first INSERT for a given department (If you didn't initialize all departments.total_employee to 0 when you created them).
I also created this DBFiddle demonstrating the functionality (And why you need the NVL function or to default your TOTAL_EMPLOYEES column to 0) (Link)

ORACLE PL/SQL Function is created successfully but the execution body is not working

These are the list of tables I have created and inserted values for the created tables:
CREATE TABLE DEPARTMENT
(DEPARTMENT_ID NUMBER PRIMARY KEY,
DEPARTMENT_NAME VARCHAR(30) NOT NULL
);
CREATE TABLE JOBS
(JOB_ID NUMBER PRIMARY KEY,
JOB_TITLE VARCHAR(35) NOT NULL,
MIN_SALARY DECIMAL NOT NULL,
MAX_SALARY DECIMAL NOT NULL
);
CREATE TABLE EMPLOYEES
(EMPLOYEE_ID NUMBER PRIMARY KEY,
FIRST_NAME VARCHAR(20) NOT NULL,
LAST_NAME VARCHAR(25) NOT NULL,
EMAIL VARCHAR(25) NOT NULL,
PHONE_NUMBER VARCHAR(20) NOT NULL,
HIRE_DATE DATE NOT NULL,
JOB_ID NUMBER NOT NULL,
SALARY DECIMAL NOT NULL,
DEPARTMENT_ID NUMBER NOT NULL,
CONSTRAINT emp_job_fk FOREIGN KEY(JOB_ID) REFERENCES JOBS(JOB_ID),
CONSTRAINT emp_department_fk FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(DEPARTMENT_ID)
);
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(1,'IT');
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(2,'Sales');
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (1,'IT Administrator',250000.00,50000.00);
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (2,'Salesman',200000.00,40000.00);
INSERT INTO EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,DEPARTMENT_ID)
VALUES (1,'Tony','Starc','starc#gmail.com','0123456789',TO_DATE('15/1/2008','DD/MM/YYYY'),1,45000.00,1);
INSERT INTO EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,DEPARTMENT_ID)
VALUES (2,'Bruce','Wayne','bruce#gmail.com','0123456788',TO_DATE('15/1/2009','DD/MM/YYYY'),1,40000.00,1);
INSERT INTO EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,DEPARTMENT_ID)
VALUES (3,'Larry','Ellison','larry#gmail.com','0123456787',TO_DATE('15/1/2010','DD/MM/YYYY'),1,30000.00,1);
INSERT INTO EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,DEPARTMENT_ID)
VALUES (4,'Steve','Jobs','steve#gmail.com','0123456786',TO_DATE('15/1/2011','DD/MM/YYYY'),2,35000.00,2);
INSERT INTO EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,DEPARTMENT_ID)
VALUES (5,'Remy','Lebeau','remy#gmail.com','0123456785',TO_DATE('15/1/2012','DD/MM/YYYY'),2,30000.00,2);
INSERT INTO EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,SALARY,DEPARTMENT_ID)
VALUES (6,'Clark','Kent','clark#gmail.com','0123456784',TO_DATE('15/1/2013','DD/MM/YYYY'),2,35000.00,2);
Now in my assignment question, I have been asked to solve the following question:
Write a function called fn_emps_per_dept_jc450912 to retrieve EMPLOYEE_ID,FIRST_NAME,LAST_NAME AND JOB_TITLE with a given DEPARTMENT_ID. This function should have DEPARTMENT_ID as input parameter and this function should have EMPLOYEE_ID,FIRST_NAME,LAST_NAME AND JOB_TITLE as output parameters. It should return TRUE if found and FALSE if not found.
In order to display the information of employees from a particular department, I have written the following function:
CREATE OR REPLACE FUNCTION fn_emps_per_dept_jc450912 (f_dept_id IN NUMBER,f_emp_id OUT NUMBER,f_first_name OUT VARCHAR,f_last_name OUT VARCHAR,f_job_title OUT VARCHAR)
RETURN BOOLEAN
AS
BEGIN
SELECT EMPLOYEE_ID,FIRST_NAME,LAST_NAME,JOB_TITLE
INTO f_emp_id,f_first_name,f_last_name,f_job_title
FROM EMPLOYEES,JOBS,DEPARTMENT
WHERE DEPARTMENT.DEPARTMENT_ID = EMPLOYEES.DEPARTMENT_ID
AND JOBS.JOB_ID = EMPLOYEES.JOB_ID
AND EMPLOYEES.DEPARTMENT_ID = f_dept_id;
RETURN TRUE;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.PUT_LINE('Employee not found');
RETURN FALSE;
END fn_emps_per_dept_jc450912;
Function FN_EMPS_PER_DEPT_JC450912 compiled
As you can see from the above that the function has been successfully compiled.
Then I tried executing the function:
DECLARE
f_dept_id NUMBER;
f_emp_id NUMBER;
f_first_name VARCHAR(30) ;
f_last_name VARCHAR(30) ;
f_job_title VARCHAR(30);
f_return BOOLEAN;
BEGIN
f_dept_id := 1;
f_return := fn_emps_per_dept_jc450912(f_dept_id,f_emp_id,f_first_name,f_last_name,f_job_title);
DBMS_OUTPUT.PUT_LINE('Employee_ID: ' || f_emp_id);
DBMS_OUTPUT.PUT_LINE('First Name: ' || f_first_name);
DBMS_OUTPUT.PUT_LINE('Last Name: ' || f_last_name);
DBMS_OUTPUT.PUT_LINE('Job: ' || f_job_title);
END;
I'm getting the following error:
Error report -
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "SYSTEM.FN_EMPS_PER_DEPT_JC450912", line 5
ORA-06512: at line 10
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
I need the solution code for the execution body to fix this error.
Firstly please use modern join syntax (ANSI standard since 92!).
So your SELECT should be:
SELECT EMPLOYEE_ID,FIRST_NAME,LAST_NAME,JOB_TITLE
INTO f_emp_id,f_first_name,f_last_name,f_job_title
FROM EMPLOYEES INNER JOIN JOBS ON JOBS.JOB_ID = EMPLOYEES.JOB_ID
INNER JOIN DEPARTMENT ON DEPARTMENT.DEPARTMENT_ID = EMPLOYEES.DEPARTMENT_ID
WHERE EMPLOYEES.DEPARTMENT_ID = f_dept_id;
Next when you get a problem like this, you should just run the query in a query window, like this:
SELECT EMPLOYEE_ID,FIRST_NAME,LAST_NAME,JOB_TITLE
FROM EMPLOYEES INNER JOIN JOBS ON JOBS.JOB_ID = EMPLOYEES.JOB_ID
INNER JOIN DEPARTMENT ON DEPARTMENT.DEPARTMENT_ID = EMPLOYEES.DEPARTMENT_ID
WHERE EMPLOYEES.DEPARTMENT_ID = 1;
This will reveal the cause of your problem as the output looks like this
EMPLOYEE_ID FIRST_NAME LAST_NAME JOB_TITLE
----------- -------------------- ------------------------- -----------------------------------
1 Tony Starc IT Administrator
2 Bruce Wayne IT Administrator
3 Larry Ellison IT Administrator
I.e the field employees.department_id is not unique. Therefore on execution the stored procedure is trying to put multiple values in a single variable. This it cannot do, hence the error. The procedure compiles ok, because at compile time, the procedure can have no idea that it will be passed a parameter which results in more than one record in the result set.

Delete child table records based on column size in parent table in oracle

I have a scenario like this :
I have three tables:
CREATE TABLE DEPT(DEPT_PK NUMBER NOT NULL,DEPT_NAME VARCHAR(10),DESCRIPTION VARCHAR(50));
CREATE TABLE EMPLOYEE(EMPLOYEE_PK NUMBER(4) NOT NULL PRIMARY KEY,DEPT_PK NUMBER(4),EMP_NAME VARCHAR2(50),ADDRESS VARCHAR2(200));
CREATE TABLE SALARY(SALARY_PK NUMBER NOT NULL,EMP_PK NUMBER(4),net_salary number(5,2));
Now i want to delete the record from dept table and whose dept_pk size is 4 and modify the table's dept_pk size to 3.
Note that emp table has dept_pk as foreign key and salary table has emp_pk as foreign key.
I need to write a pl/sql procedure or sql query for this.
Thanks in advance.
So you have 1st to delete from SALARY:
delete from SALARY where EMP_PK in
(
select EMPLOYEE_PK
from EMPLOYEE
where DEPT_PK in
(
select DEPT_PK from DEPT where length(DEPT_PK) = 4
);
);
Then from EMPLOYEE:
delete from EMPLOYEE
where DEPT_PK in
(
select DEPT_PK from DEPT where length(DEPT_PK) = 4
);
And now from DEPT:
delete from DEPT where length(DEPT_PK) = 4;
EDIT : if you really want to use PL/SQL, you just have to enclose the following code into begin and end for one shot execution:
begin
delete from SALARY where EMP_PK in
(
select EMPLOYEE_PK
from EMPLOYEE
where DEPT_PK in
(
select DEPT_PK from DEPT where length(DEPT_PK) = 4
);
);
delete from EMPLOYEE
where DEPT_PK in
(
select DEPT_PK from DEPT where length(DEPT_PK) = 4
);
delete from DEPT where length(DEPT_PK) = 4;
end;
/
or create a procedure for re-usability: add create or replace procedure proc_name is before begin.

Oracle SQL: Invalid table name when creating a trigger

I created this 4 tables:
create table terminatedEmployees (
empid number primary key,
dept number,
empname varchar2(50),
salary number
);
create table employees (
empid number primary key,
dept number,
empname varchar2(50),
salary number
);
create table payroll (
empid number primary key,
salary number,
CONSTRAINT fk_payemploy
FOREIGN KEY (empid)
REFERENCES employees(empid)
);
create table salaryAudit (
empid number primary key,
oldsal number,
newsal number,
datechanged date,
changedby varchar2(25),
CONSTRAINT fk_salaryaudit
FOREIGN KEY (empid)
REFERENCES employees(empid)
);
and now I'm trying to create a trigger in order to update two of them when employees table is updated:
CREATE TRIGGER trigger_updated_employees
AFTER UPDATE ON employees
FOR EACH ROW
when (old.salary != new.salary)
BEGIN
UPDATE INTO salaryAudit (newsal, oldsal)
VALUES(:new.salary, :old.salary);
UPDATE INTO payroll (salary)
VALUES(:new.salary);
END;
But I'm getting the error:
2/5 PL/SQL: SQL Statement ignored
2/12 PL/SQL: ORA-00903: invalid table name
4/5 PL/SQL: SQL Statement ignored
4/12 PL/SQL: ORA-00903: invalid table name
The three tables I'm calling in the trigger are ok and other triggers I created work...
Try something like this:
CREATE TRIGGER TRIGGER_UPDATED_EMPLOYEES
AFTER UPDATE ON EMPLOYEES
FOR EACH ROW
WHEN (OLD.SALARY <> NEW.SALARY)
BEGIN
MERGE INTO PAYROLL p
USING (SELECT :NEW.EMPID AS EMPID FROM DUAL) d
ON (p.EMPID = d.EMPID)
WHEN NOT MATCHED THEN
INSERT (EMPID, SALARY)
VALUES (:NEW.EMPID, :NEW.SALARY)
WHEN MATCHED THEN
UPDATE
SET SALARY = :NEW.SALARY;
MERGE INTO SALARYAUDIT a
USING (SELECT :NEW.EMPID AS EMPID FROM DUAL) d
ON (a.EMPID = d.EMPID)
WHEN NOT MATCHED THEN
INSERT (EMPID, OLDSAL, NEWSAL, DATECHANGED, CHANGEDBY)
VALUES (:NEW.EMPID, :OLD.SALARY, :NEW.SALARY, SYSDATE, 'SOME_USER')
WHEN MATCHED THEN
UPDATE
SET OLDSAL = :OLD.SALARY,
NEWSAL = :NEW.SALARY,
DATECHANGED = SYSDATE,
CHANGEDBY = 'SOME_USER';
END TRIGGER_UPDATED_EMPLOYEES;
Share and enjoy.
I got it working just correcting the UPDATE statements syntax and modifying the condition as #BobJarvis suggested, this is the final result:
CREATE TRIGGER trigger_updated_employees
AFTER UPDATE OF salary ON employees
FOR EACH ROW
when (old.salary <> new.salary)
BEGIN
UPDATE salaryAudit
SET (newsal, oldsal)
VALUES (:new.salary, :old.salary)
WHERE (salaryAudit.empid = old.empid);
UPDATE payroll
SET (salary)
VALUES (:new.salary)
WHERE (payroll.empid = old.empid);
END;
The concept is the same that #BobJarvis proposed but a lot simpler. Thanks