I keep getting the error below and I can't figure out why. I've included the tables in the code and then also the code portion that is presenting the error.
Tables in the code:
CREATE TABLE Employee (
emp_id number(8) primary key,
f_name VarChar2(20),
l_name VarChar2(20),
address VarChar2(40),
city VarChar2(20),
state Char(2),
zip Number(5),
phone_Number Number(10),
email_address VarChar2(30),
dept_id number(8) references department(Dept_ID),
office_location VarChar2(30)
);
CREATE TABLE audit_detail (
audit_detail_id number(8) primary key,
field varchar2(30),
old_value varchar2(30),
new_value varchar2(30)
);
CREATE TABLE audit_trail (
audit_trail_id number(8) primary key,
user_id number(8)references user_id_table(user_id),
table_name VarChar2(25),
process VarChar2(25),
emp_id number(8)references employee(emp_id),
timestamp Date
);
Create table Department (
dept_id number(8) primary key,
dept_name VarChar2(20) not null,
dept_start_date date
);
create table user_id_table(
user_id number(8) primary key,
emp_id number(8) references Employee(emp_id)
);
CREATE SEQUENCE audit_seq;
This is the portion that is presenting the error, Specifically the elsif Deleting portion
CREATE OR REPLACE TRIGGER audit_employee
AFTER INSERT OR UPDATE OR DELETE ON Employee
FOR EACH ROW
DECLARE
timestamp DATE;
session_user number(8);
BEGIN
timestamp := SYSDATE;
session_user := USERENV('SESSION_USER');
IF INSERTING THEN
INSERT INTO audit_trail VALUES
(Audit_seq.NEXTVAL, session_user, 'Employee', 'INSERT', :new.emp_id, timestamp);
ElSIF DELETING THEN
INSERT INTO audit_trail Values
(Audit_seq.NEXTVAL, session_user, 'Employee', 'DELETE', :old.emp_id, timestamp);
Else
INSERT INTO audit_trail
Values(Audit_seq.NEXTVAL, session_user, 'Employee', 'UPDATE', :old.emp_id);
IF UPDATING ('f_name') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'f_name', :old.f_name, :new.f_name);
ELSIF UPDATING('l_name') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'l_name', :old.l_name, :new.l_name);
ELSIF UPDATING('address') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'address', :old.address, :new.address);
ELSIF UPDATING('city') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'city', :old.city, :new.city);
ELSIF UPDATING('state') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'state', :old.state, :new.state);
ELSIF UPDATING('zip') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'zip', :old.zip, :new.zip);
ELSIF UPDATING('phone_Number') THEN
INSERT INTO Audit_detail VALUES
(Audit_seq.CURRVAL, 'phone_Number', :old.phone_number, :new.phone_number);
ELSIF UPDATING('email_address') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'email_address', :old.email_address, :new.email_address);
ELSIF UPDATING('dept_id') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'dept_id', :old.dept_id, :new.dept_id);
ELSIF UPDATING('office_location') THEN
INSERT INTO audit_detail VALUES
(Audit_seq.CURRVAL, 'office_location', :old.office_location, :new.office_location);
End IF;
End IF;
End;
/
Your else statement after the deleting has 5 columns whereas audit_trail has 6.
ElSIF DELETING THEN
INSERT INTO audit_trail
Values ( Audit_seq.NEXTVAL, session_user, 'Employee'
, 'DELETE', :old.emp_id, timestamp);
Else
INSERT INTO audit_trail
Values (Audit_seq.NEXTVAL, session_user, 'Employee'
, 'UPDATE', :old.emp_id );
You're also referencing columns in lower case not upper, UPDATING ('L_NAME'). I'm not sure whether that would cause a problem.
You're doing a lot of implicit number to character conversion which will eventually catch you out and table_name in audit_trail should by 30 characters not 25. Why risk it?
Related
--dept table
create table department(
dept_id number(5) ,
dept_name varchar2(100),
dept_city varchar2(100) ,
dept_country varchar2(100),
CONSTRAINT dept_pk PRIMARY KEY(dept_id)
);
insert into department( dept_id, dept_name, dept_city, dept_country )values(1,'hr','hyderabad','india');
insert into department( dept_id, dept_name, dept_city, dept_country )values(2,'marketing','banglore','india');
insert into department(dept_id, dept_name, dept_city, dept_country)values(3,'sales','dhaka','bangladesh');
create sequence s1
start with 1
increment by 1;
create table employee(
employee_id number(10) ,
employee_name varchar2(100) NOT NULL,
employee_age number(3) ,
employee_sal number(9,2),
dept_id number(5),
CONSTRAINT employee_pk PRIMARY KEY(employee_id),
constraint dept_fk foreign key(dept_id) references department(dept_id)
);
CREATE OR REPLACE TRIGGER trg_before_emp_insr
BEFORE INSERT
on employee_details
FOR EACH ROW
DECLARE
emp_age number;
BEGIN
IF (employee_age < 18) THEN
RAISE_APPLICATION_ERROR(-20000,'Employee age must be greater than or equal to 18.');
END IF;
END;
/
insert into employee(employee_id, employee_name, employee_age, employee_sal,dept_id )values(s1.nextval,'ravi',45,7333,1);
insert into employee(employee_id, employee_name, employee_age, employee_sal,dept_id )values(s1.nextval,'sai',74,4451,2);
insert into employee(employee_id, employee_name, employee_age, employee_sal,dept_id )values(s1.nextval,'chandu',35,9428,3);
insert into employee( employee_id,employee_name, employee_age, employee_sal,dept_id )values(s1.nextval,'raju',7,25422,2);
insert into employee( employee_id,employee_name, employee_age, employee_sal,dept_id )values(s1.nextval,'teja',36,7955,1);
select * from employee
You want to use the :NEW record to get the value from the row being inserted (and to use the EMPLOYEE table rather than EMPLOYEE_DETAILS):
CREATE OR REPLACE TRIGGER trg_before_emp_insr
BEFORE INSERT
on employee
FOR EACH ROW
BEGIN
IF (:NEW.employee_age < 18) THEN
RAISE_APPLICATION_ERROR(-20000,'Employee age must be greater than or equal to 18.');
END IF;
END;
/
db<>fiddle here
However, you should consider storing date of birth rather than age as tomorrow (or definitely next year) the age value will be outdated but storing the date of birth and calculating the age would not.
create table employee(
employee_id number(10) ,
employee_name varchar2(100) NOT NULL,
employee_dob DATE,
employee_sal number(9,2),
dept_id number(5),
CONSTRAINT employee_pk PRIMARY KEY(employee_id),
constraint dept_fk foreign key(dept_id) references department(dept_id)
);
CREATE OR REPLACE TRIGGER trg_before_emp_insr
BEFORE INSERT
on employee
FOR EACH ROW
BEGIN
IF :NEW.employee_dob > TRUNC(ADD_MONTHS(SYSDATE, -18*12)) THEN
RAISE_APPLICATION_ERROR(-20000,'Employee age must be greater than or equal to 18.');
END IF;
END;
/
db<>fiddle here
CREATE TABLE STAGING
(
E_ID NUMBER(10),
E_NAME VARCHAR2(30),
E_LOC VARCHAR2(30),
VALIDATION_STATUS varchar2(30),
validation_msg varchar2(30),
req_id number(10)
);
INSERT INTO staging VALUES(1, 'A', 'AA', NULL, NULL, 1);
INSERT INTO staging VALUES(2, 'B', 'BB', NULL, NULL, 1);
INSERT INTO staging VALUES(3, 'C', 'CC', NULL, NULL, 1);
INSERT INTO staging VALUES(NULL, 'D', 'DD', NULL, NULL, 2);
INSERT INTO staging VALUES(NULL, 'E', 'EE', NULL, NULL, 2);
INSERT INTO staging VALUES(NULL, 'F', 'GG', NULL, NULL, 2);
CREATE TABLE tab_ref
(
ref_id number(10),
ref_name varchar2(30)
);
INSERT INTO tab_ref VALUES(1, 'aa');
INSERT INTO tab_ref VALUES(2, 'bb');
INSERT INTO tab_ref VALUES(3, 'cc');
INSERT INTO tab_ref VALUES(4, 'dd');
CREATE TABLE tab_ref_2
(
ref_id number(10),
ref_name varchar2(30)
);
INSERT INTO tab_ref_2 VALUES(1, 'ee');
INSERT INTO tab_ref_2 VALUES(2, 'ff');
INSERT INTO tab_ref_2 VALUES(3, 'gg');
INSERT INTO tab_ref_2 VALUES(4, 'hh');
CREATE TABLE SUMMARY_TAB
(
TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
process_status varchar2(30)
);
CREATE TABLE TARGET_TAB
(
E_ID NUMBER(10,0),
E_NAME VARCHAR2(30),
E_LOC VARCHAR2(30)
);
Stored procedure:
create or replace procedure sp_stage_target(iv_req_id IN sys.OdciNumberList,ov_err_msg OUT varchar2) is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count_ref number(10);
lv_count_ref2 number(10);
lv_threshold_cnt number(10);
lv_RejectedCount number(10);
lv_status varchar2(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
lv_threshold_cnt := 5;
/*First checking whether data is present in reference table or not.
If data is not present then process should stop*/
select count(1) into lv_count_ref from tab_ref;
select count(1) into lv_count_ref2 from tab_ref_2;
if lv_count_ref = 0 then
ov_err_msg := 'Records are not present in the reference table !!Cannot proceed';
elsif lv_count_ref2 = 0 then
ov_err_msg := 'Records are not present in the reference table !!Cannot proceed';
else
dbms_output.put_line('Data are present into reference tables');
merge into staging d
using (
select 'Fail' as validation_status, t.column_value as req_id
from table(iv_req_id) t
) s
on (d.req_id = s.req_id)
when matched then
update set
d.validation_status = s.validation_status
, d.validation_msg = case
when e_id is null then 'Id is not present'
else 'Id is longer than expected'
end
where e_id is null OR LENGTH(e_id) > 4;
lv_RejectedCount := SQL%ROWCOUNT;
end if;
--If rejected count is less than lv_threshold_cnt i.e 5
--then success records will go in target_tab and failed records will go in reject_tab
if lv_RejectedCount <= lv_threshold_cnt then
lv_status := 'Success';
dbms_output.put_line('Success');
merge into target_tab t
using (
select e_id, e_name, e_loc
from staging
where validation_status is null and req_id in (select column_value from table(iv_req_id))
) s
on (t.e_id = s.e_id)
when matched then
update set
t.e_name = s.e_name,
t.e_loc = s.e_loc
when not matched then
insert (t.e_id,t.e_name,t.e_loc)
values (s.e_id,s.e_name,s.e_loc);
lv_succ_rec := SQL%ROWCOUNT;
end if;
insert into reject_tab
select e_id, e_name, e_loc, validation_status,validation_msg
from staging
where validation_status = 'Fail' and req_id in (select column_value from table(iv_req_id));
lv_fail_rec := SQL%ROWCOUNT;
--In Summary table keeping track of all the records i.e success record, failed records
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(tot_records, success_records, failed_records, process_status)
values (lv_succ_rec + lv_fail_rec, lv_succ_rec, lv_fail_rec, lv_status);
ov_err_msg := 'Procedure completed succesfully';
end;
Calling Procedure:
set serveroutput on;
declare
err_msg;
begin
sp_main_target(sys.OdciNumberList(1,2),err_msg);
dbms_output.put_line(err_msg);
end;
Getting compilation error and also I am not not how to process for individually for each request_id and process so have highlighted the requirement in comment block.
Error :
I wanted to create a stored procedure that will handle all the below points and I have tried but not been able to get the results.
The stored procedure should accept multiple input parameters while calling a procedure and should process for every request-id given in the parameter with comma-separated.
Then stored procedure will check whether data is present or not in the ref table (tab_ref & tab_ref_2). If data is present then only the process should start otherwise it will not proceed further.
Then it will check the data in the staging table and will do validation for e_id is null or not or its length should not exceed the given limit. For every failed validation it will update the validation status and validation msg column and have to keep count of rejected columns.
After validation, if the threshold count is less then the lv_threshold_count then insertion will happen in both the tables with status as 'Success 'i.e target table and rejected table with validation_status as null and Fail respectively.
If threshold count is more than the lv_threshold_count then it will insert into the rejected table with status as 'Fail'.
Then at last it will show all the records count into the summary table.
You start an IF on line 20 of your procedure, but you don't have a corresponding END IF.
if lv_count_ref = 0 then
I have table p_it_people with structure as below and trigger created on it.
CREATE TABLE "P_IT_PEOPLE"
( "PERSON_ID" NUMBER NOT NULL ENABLE,
"PERSON_NAME" VARCHAR2(255) NOT NULL ENABLE,
"PERSON_EMAIL" VARCHAR2(255) NOT NULL ENABLE,
"PERSON_ROLE" VARCHAR2(30) NOT NULL ENABLE,
"USERNAME" VARCHAR2(255) NOT NULL ENABLE,
"ASSIGNED_DEPT" NUMBER,
"CREATED_ON" DATE NOT NULL ENABLE,
"CREATED_BY" VARCHAR2(255) NOT NULL ENABLE,
"MODIFIED_ON" DATE,
"MODIFIED_BY" VARCHAR2(255),
"PERSON_PASSWORD" VARCHAR2(100),
"APPROVER" VARCHAR2(50),
CONSTRAINT "P_IT_PEOPLE_PK" PRIMARY KEY ("PERSON_ID")
USING INDEX ENABLE,
CONSTRAINT "P_IT_PEOPLE_NAME_UK" UNIQUE ("PERSON_NAME")
USING INDEX ENABLE,
CONSTRAINT "P_IT_PEOPLE_USERNAME_UK" UNIQUE ("USERNAME")
Existing trigger on the table:
CREATE OR REPLACE EDITIONABLE TRIGGER "P_IT_PEOPLE_BIU"
before insert or update on p_it_people
for each row
begin
if inserting then
if :NEW.PERSON_ID is null then
:NEW.PERSON_ID := it_api.gen_pk;
end if;
:NEW.CREATED_ON := sysdate;
:NEW.CREATED_BY := nvl(v('APP_USER'),USER);
end if;
if updating then
:NEW.MODIFIED_ON := sysdate;
:NEW.MODIFIED_BY := nvl(v('APP_USER'),USER);
end if;
end;
Apart from this i want to create another trigger which would send email whenever new entry is made .
I deployed this trigger:
CREATE OR REPLACE EDITIONABLE TRIGGER "P_IT_ISSUES_AIU_NEW_PASSWORD"
AFTER
insert on P_IT_PEOPLE
for each row
DECLARE
v_person_id number;
v_username varchar2(50);
v_Email varchar2(255);
Begin
select person_id,username,person_email into v_person_id,v_username,v_email from p_it_people where person_id=v_person_id;
APEX_MAIL.SEND(
p_to => v_email,
p_from => v_email,
p_body => 'Your account has been created ' ||chr(10)||
' Username'|| v_username||chr(10)||
' Password'||v_username ,
p_subj => 'New User');
end;
Now when i try inserting row, it is throwing error-p_it_people is mutating. How can i counter this?
You are selecting from the same table as the row-level trigger is firing on - exactly what causes "table is mutating". But in this case you don't even need to. Just use the :NEW pseudo-record like this:
CREATE OR REPLACE EDITIONABLE TRIGGER "P_IT_ISSUES_AIU_NEW_PASSWORD"
AFTER
insert on P_IT_PEOPLE
for each row
Begin
APEX_MAIL.SEND(
p_to => :NEW.person_email,
p_from => :NEW.person_email ,
p_body => 'Your account has been created ' ||chr(10)||
' Username'|| :NEW.username||chr(10)||
' Password'||:NEW.username ,
p_subj => 'New User');
end;
Here are the columns in my table, TB_KELUHAN
"IDKELUHAN" NUMBER(20,0) NOT NULL ENABLE,
"NAMA" VARCHAR2(10 BYTE),
"IDUNIT" NUMBER(10,0),
"TGL_KELUHAN" DATE DEFAULT sysdate,
"KELUHAN" VARCHAR2(200 BYTE),
"STATUS" VARCHAR2(10 BYTE),
"IDPEGAWAI" NUMBER(10,0),
"TGL_SELESAI" DATE DEFAULT sysdate,
"ID_JENISKELUHAN" NUMBER(5,0),
CONSTRAINT "TB_KELUHAN_PK" PRIMARY KEY ("IDKELUHAN")
I want a trigger that will update a row's TGL_SELESAI column to SYSDATE, when a row's STATUS becomes 'SELESAI'. Here is the text of the trigger I've tried:
TRIGGER SELESAI
AFTER UPDATE OF STATUS ON TB_KELUHAN
FOR EACH ROW
DECLARE
TGL_SELESAI DATE;
BEGIN
IF :new.STATUS = 'SELESAI'
THEN
TGL_SELESAI:=SYSDATE;
END IF;
END;
When I change the value of STATUS to "SELESAI", the corresponding TGL_SELESAI did not change. Why?
Your original code was setting a local PL/SQL variable, not the row's column and you were creating an AFTER UPDATE trigger, not a BEFORE UPDATE trigger. Try this:
CREATE OR REPLACE TRIGGER SELESAI
BEFORE UPDATE OF STATUS ON TB_KELUHAN
FOR EACH ROW
BEGIN
IF :new.STATUS = 'SELESAI'
THEN
:new.TGL_SELESAI := SYSDATE;
END IF;
END;
I'm writing a function for batch-importing data into our org-chart. It seems to work fine for retrieving entries that already exists, but when an entry does not already exist, and it's supposed to insert, commit, and return the result of a re-attempt (so as to get the auto-generated ID), it always returns NULL.
I'm sure I'm doing something wrong here, but what? Help appreciated.
Note: there's a before-insert trigger that fills in DEPT_ID if it's not specified. Works fine if the insert statement is executed by hand.
CREATE TABLE DEPTS
(
"DEPT_ID" VARCHAR2(10 BYTE),
"HEADER_ID" VARCHAR2(10 BYTE),
"COMMENTS" VARCHAR2(100 BYTE),
"CATEGORY" VARCHAR2(2 BYTE)
);
CREATE OR REPLACE FUNCTION get_or_make_unit(
, in_cat VARCHAR2
, in_cmt VARCHAR2
, in_hdr VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
IF unit_id IS NULL THEN
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
RETURN unit_id;
ELSE
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
END IF;
END get_or_make_unit;
And the trigger:
CREATE OR REPLACE TRIGGER HRD.DEPTS_BIR
BEFORE INSERT
ON HRD.DEPTS
FOR EACH ROW
DECLARE
JML NUMBER;
BEGIN
SELECT SEQ_DEPT_ID.NEXTVAL INTO JML FROM DUAL;
:NEW.DEPT_ID:='D'||to_char(JML);
END DEPTS_BIR;
Examples
This works:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
'D532'
, 'ACCOUNTING'
, '2');
COMMIT;
SELECT get_or_make_unit('2', 'ACCOUNTING', 'D532') FROM DUAL;
=> 'D533'
This does not:
SELECT get_or_make_unit('2', 'NEW DEPT', 'D532') FROM DUAL;
=> NULL
Instead of:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
use RETURNING INTO:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat) RETURNING dept_id INTO unit_id;
COMMIT;
I think recursive call in not the best approach, but if you are strict of using it, then please post definition of mentioned before-insert trigger.
UPDATE: You cannot call functions containing DML operations from SQL statement. Please see this answer for details. Example of correct call:
DECLARE
unit_id varchar2(32);
BEGIN
unit_id := get_or_make_unit('2', 'NEW DEPT', 'D532');
dbms_output.put_line(unit_id);
END;
UPDATE2: Also you need to catch NO_DATA_FOUND exception that is raised when you call your function with not existent combination. Below is example:
CREATE OR REPLACE FUNCTION get_or_make_unit(in_cat VARCHAR2,
in_cmt VARCHAR2,
in_hdr VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts
(header_id, comments, category)
VALUES
(in_hdr, in_cmt, in_cat)
returning dept_id into unit_id;
COMMIT;
RETURN unit_id;
END get_or_make_unit;
Your call to get_or_make_unit appears to be missing the first parameter.
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
This code is not inserting anything into dept_id of depts table. Thats the reason you are getting null