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
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;
Im creating a procedure to display the n number of maximum and minimum salary for an employee. If i ll give 5 as input, the query will get me 5 maximum and minimum salary for an employee.
For the above scenario, I have created an object with two columns like below
create type vrec as object(
empno number,
sal number
);
/
Then i created nested table with the help of object type, so that i can use the nested table as out parameter to return all the rows at one short.
create type vrec_type is table of vrec;
/
After the data type creation, im creating a procedure like below
create or replace procedure pro_first_last(input in number,salary out vrec_type)
is
begin
select empno,sal BULK COLLECT INTO salary from (
select empno,sal from
(select empno,sal,rank() over(order by sal asc) min_sal from emp5 where sal is not null) where min_sal <= 5
union all
select empno,sal from
(select empno,sal,rank() over(order by sal desc) max_sal from emp5 where sal is not null) where max_sal <= 5);
for i in salary.first..salary.last
loop
dbms_output.put_line(salary(i).empno);
end loop;
end;
/
When i compiling the above procedure, im getting not enough values. I have also created the object with two columns and in select statement also im returning only two column. Could someone review and help me on this or provide some alternate solution.
You are directly adding empno, sal values into salary (vrec_type object, which can take the values of only object type vrec)
You need to create the object of vrec and then add it into salary as following:
create or replace procedure pro_first_last(input in number,salary out vrec_type)
is
begin
select vrec(empno,sal) -- change in this line
BULK COLLECT INTO salary from (
select empno,sal from
(select empno,sal,rank() over(order by sal asc) min_sal from emp5 where sal is not null) where min_sal <= 5
union all
select empno,sal from
(select empno,sal,rank() over(order by sal desc) max_sal from emp5 where sal is not null) where max_sal <= 5);
for i in salary.first..salary.last
loop
dbms_output.put_line(salary(i).empno);
end loop;
end;
Cheers!!
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
CREATE TABLE emp (
empno NUMBER (10) NOT NULL,
empname VARCHAR2 (50) NOT NULL,
mgr VARCHAR2 (10),
hiredate DATE,
sal NUMBER (10),
comm VARCHAR2 (10),
CONSTRAINT emp_pk PRIMARY KEY (empno)
) ;
DESC emp ;
INSERT ALL
INTO emp
(100, 'MARK', 'CXS', hire_date('1999-09-08', 'yyyy/mm/dd'), 8000, 'axw')
(200, 'peter', 'NULL', hire_date('1996-01-08', 'yyyy/mm/dd'), 9000)
(300, 'karl', 'NULL', hire_date('1995-05-08', 'yyyy/mm/dd'), 5000, 'AZQ')
(400, 'MAx', 'NULL', hire_date('1994-04-08', 'yyyy/mm/dd'), 10000, 'DES')
(500, 'Maggie', 'SAQ', hire_date('1998-06-08', 'yyyy/mm/dd'), 20000, 'QAS')
SELECT * FROM dual ;
Your insert all syntax is malformed, you need multiple into ... values ... pairs. At the moment it's trying to interpret 100 as a column name, hence the specific error you're seeing.
You also have hire_date(...) instead of to_date(...), and you're missing one of the comm values; assuming that is supposed to be null:
INSERT ALL
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (100, 'MARK', 'CXS', TO_DATE('1999-09-08', 'yyyy-mm-dd'), 8000, 'axw')
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (200, 'peter', NULL, TO_DATE('1996-01-08', 'yyyy-mm-dd'), 9000, null)
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (300, 'karl', NULL, TO_DATE('1995-05-08', 'yyyy-mm-dd'), 5000, 'AZQ')
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (400, 'MAx', NULL, TO_DATE('1994-04-08', 'yyyy-mm-dd'), 10000, 'DES')
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (500, 'Maggie', 'SAQ', TO_DATE('1998-06-08', 'yyyy-mm-dd'), 20000, 'QAS')
SELECT * FROM dual ;
5 rows inserted.
I've also changed the stirng literals 'NULL' to plain nulls, as it seems much more liekly that is what you really intended.
And I've changed the date format masks to use - rather than /, to match the values. But if you're using fixed values like that it's simpler to use date literals instead, e.g. instead of
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (100, 'MARK', 'CXS', TO_DATE('1999-09-08', 'yyyy-mm-dd'), 8000, 'axw')
use:
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (100, 'MARK', 'CXS', DATE '1999-09-08', 8000, 'axw')
The syntax for the INSERT ALL statement in Oracle/PLSQL is:
INSERT ALL
INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n)
INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n)
INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n)
SELECT * FROM dual;
So in your case you can use
INSERT ALL
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (100, 'MARK', 'CXS', TO_DATE('1999-09-08', 'yyyy-mm-dd'), 8000, 'axw')
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (200, 'peter', NULL, TO_DATE('1996-01-08', 'yyyy-mm-dd'), 9000, null)
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (300, 'karl', NULL, TO_DATE('1995-05-08', 'yyyy-mm-dd'), 5000, 'AZQ')
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (400, 'MAx', NULL, TO_DATE('1994-04-08', 'yyyy-mm-dd'), 10000, 'DES')
INTO emp (empno, empname, mgr, hiredate, sal, comm)
VALUES (500, 'Maggie', 'SAQ', TO_DATE('1998-06-08', 'yyyy-mm-dd'), 20000, 'QAS')
SELECT * FROM dual ;
Also for any column you want blank/no value just put NULL if you include this as 'NULL' it will be considered as text NULL.
I can't create a PL/SQL stored procedure. For example returns all of the ename of employees in deptno 20.
create table emp(
empno number(4,0),
ename varchar2(10),
job varchar2(9),
mgr number(4,0),
hiredate date,
sal number(7,2),
comm number(7,2),
deptno number(2,0),
constraint pk_emp primary key (empno),
constraint fk_deptno foreign key (deptno) references dept (deptno)
);
This is what i manage to do but i can't seem to move/write it into a procedure.
declare
TYPE name_array IS TABLE OF emp.ename %type
INDEX BY BINARY_INTEGER;
enames name_array;
begin
update emp set empno = empno where deptno = 20
returning ename bulk collect into enames;
for i in 1..enames.count loop
dbms_output.put_line('ename : '||enames(i));
end loop;
end;
/
When returning a single object (which includes a table of values), I prefer functions over out parameters. Here is code that implements both.
drop table emp purge;
drop table dept purge;
CREATE TABLE dept
(
deptno INTEGER PRIMARY KEY
);
CREATE TABLE emp
(
empno NUMBER (4, 0)
, ename VARCHAR2 (10)
, job VARCHAR2 (9)
, mgr NUMBER (4, 0)
, hiredate DATE
, sal NUMBER (7, 2)
, comm NUMBER (7, 2)
, deptno NUMBER (2, 0)
, CONSTRAINT pk_emp PRIMARY KEY (empno)
, CONSTRAINT fk_deptno FOREIGN KEY (deptno) REFERENCES dept (deptno)
);
CREATE or replace PACKAGE emp_pkg
AS
TYPE name_array_t IS TABLE OF emp.ename%TYPE
INDEX BY BINARY_INTEGER;
FUNCTION upd (p_empno IN emp.empno%TYPE)
RETURN name_array_t;
PROCEDURE upd (
p_empno IN emp.empno%TYPE
, p_names OUT name_array_t
);
END emp_pkg;
CREATE or replace PACKAGE BODY emp_pkg
AS
FUNCTION upd (p_empno IN emp.empno%TYPE)
RETURN name_array_t
AS
l_names name_array_t;
BEGIN
UPDATE emp
SET empno = empno
WHERE deptno = 20
RETURNING ename
BULK COLLECT INTO l_names;
RETURN l_names;
END upd;
PROCEDURE upd (
p_empno IN emp.empno%TYPE
, p_names OUT name_array_t
)
AS
BEGIN
p_names := upd (p_empno);
END upd;
END emp_pkg;
This is a sample Stored Proc with return type as Array
create
procedure sample_proc(p_cust_id in number, p_customers out custarray)
as
my_cust custarray := custarray();