Oracle trigger conflict with null - sql

First and foremost, this is for an assignment, so most of the really cool pre-written functions you'll want to suggest, I will not be allowed to use.
I have a couple tables that all have fields
creation_date
created_by
last_update_date
last_updated_by
I need to write a trigger that fills these in for creation or updating. The problem is, these tables have null values that are problematic to me. Example:
CREATE TABLE parts
(
pno NUMBER,
pname VARCHAR2(50) NOT NULL,
qoh NUMBER NOT NULL,
price NUMBER(5,2),
reorder_level NUMBER(2),
creation_date DATE NOT NULL,
created_by VARCHAR2(10) NOT NULL,
last_update_date DATE NOT NULL,
last_updated_by VARCHAR2(10) NOT NULL,
CONSTRAINT parts_PK PRIMARY KEY (pno))
These NOT NULL's are given to me and I'm not allowed to change them. So I'm having trouble conceptualizing this.
If my trigger adds these values before the field is created, I can't do an INSERT INTO with those fields blank because they're NOT NULL.
If my trigger adds these values after the field is created, I get compilation errors ORA-00903 and 00922. Invalid table name and invalid option.
I was thinking my trigger would look like
CREATE OR REPLACE TRIGGER pcreate
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
UPDATE
SET creation_date = SYSDATE;
SET created_by = USER;
SET last_update_date = SYSDATE;
SET last_updated_by = USER;
END;
/
CREATE OR REPLACE TRIGGER pchange
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
UPDATE
SET last_update_date = SYSDATE;
SET last_updated_by = USER;
END;
/
repeat for the other tables
I might be allowed to use UPSERT but I don't really know how that works.. Any suggestions are welcome. I'm really in this to learn so any other advice is appreciated.
EDIT:
My package that will not acknowledge the trigger is as follows. Do I need to call the trigger inside the package?
CREATE OR REPLACE PACKAGE process_orders
AS
PROCEDURE add_order (p_cno IN NUMBER, p_eno IN NUMBER, p_received IN DATE);
PROCEDURE add_order_details (p_ono IN NUMBER, p_pno IN NUMBER, p_qty IN NUMBER);
PROCEDURE ship_order (p_ono IN NUMBER, p_sdate IN DATE);
PROCEDURE delete_order (p_ono IN NUMBER);
FUNCTION total_emp_sales (f_eno IN NUMBER) RETURN NUMBER;
END process_orders;
/
CREATE OR REPLACE PACKAGE BODY process_orders
AS
PROCEDURE add_order (p_cno IN NUMBER, p_eno IN NUMBER, p_received IN DATE)
IS
ao_emsg VARCHAR2(100);
p_rec_today DATE;
BEGIN
SELECT SYSDATE INTO p_rec_today FROM dual;
IF p_received is null THEN
INSERT INTO orders (ono, cno, eno, received)
VALUES(order_number_seq.NEXTVAL,p_cno,p_eno,p_rec_today);
ELSE
INSERT INTO orders (ono, cno, eno, received)
VALUES(order_number_seq.NEXTVAL,p_cno,p_eno,p_received);
END IF;
EXCEPTION
WHEN OTHERS THEN
ao_emsg := substr(SQLERRM,1,100);
INSERT INTO orders_errors (ono,transaction_date,message)
VALUES(order_number_seq.CURRVAL,SYSDATE,ao_emsg);
END;
--Etc. Procedures
END;
/

In your trigger, you simply want to modify the :new record. Your triggers would look something like
CREATE OR REPLACE TRIGGER parts_before_insert
BEFORE INSERT on parts
FOR EACH ROW
BEGIN
:new.creation_date := SYSDATE;
:new.created_by := USER;
:new.last_update_date := SYSDATE;
:new.last_updated_by := USER;
END;
and
CREATE OR REPLACE TRIGGER parts_before_update
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
:new.last_update_date := SYSDATE;
:new.last_updated_by := USER;
END;
In your INSERT statement, you would omit these four columns and let the trigger fill in the values. For example (obviously, your primary keys would be coming from something like a sequence rather than being hard-coded)
SQL> insert into parts( pno, pname, qoh, price, reorder_level )
2 values( 1, 'Widget', 10, 100, 75 );
1 row created.
SQL> select *
2 from parts;
PNO PNAME QOH
---------- -------------------------------------------------- ----------
PRICE REORDER_LEVEL CREATION_ CREATED_BY LAST_UPDA LAST_UPDAT
---------- ------------- --------- ---------- --------- ----------
1 Widget 10
100 75 26-SEP-12 SCOTT 26-SEP-12 SCOTT

There is no need of executing UPDATE statement in triggers in your case. Just assign new values to the columns using :new.
CREATE OR REPLACE TRIGGER pchange
BEFORE UPDATE on parts
FOR EACH ROW
BEGIN
:new.last_update_date = SYSDATE;
:new.last_updated_by = USER;
END;

Related

cannot change NEW values for this trigger type

I'm trying to create a trigger that would give discount to the customer with id 24535 when a new row is inserted in the table, and i get the error in the tittle.
CREATE OR REPLACE TRIGGER TOP_DISCOUNT
AFTER INSERT ON PURCHASE
FOR EACH ROW
BEGIN
IF :old.ClientNo = 24535 THEN
:new.Amount := :old.Amount * 0.85;
END IF;
END;
this is what the table looks like
CREATE TABLE PURCHASE (
PURCHASENO NUMBER(5),
RECEIPTNO NUMBER(6),
SERVICETYPE VARCHAR2(25),
PAYMENTTYPE VARCHAR2(10),
GST VARCHAR2(3),
AMOUNT NUMBER(4),
SERVEDBY NUMBER(4),
CLIENTNO NUMBER(5)
);
Thanks in advance!
After insert trigger can not change the value which is going to be inserted and also :old is not supported in insert trigger.
Your trigger should be like:
CREATE OR REPLACE TRIGGER TOP_DISCOUNT
BEFORE INSERT ON PURCHASE
FOR EACH ROW
BEGIN
IF :new.ClientNo = 24535 THEN
:new.Amount := :new.Amount * 0.85;
END IF;
END;
/
Cheers!!

trigger to override the message to error(ORA-02292)

I want override the message that generate error (ORA-02292).
It is message like that
ORA-02292: integrity constraint (IVANKA.FK_SUPPLIER) violated - child record found
I want a trigger to override the above message to his example on this (MY override :))
I tried do like that
for first create table
CREATE TABLE supplier
( supplier_id numeric(10) not null,
supplier_name varchar2(50) not null,
contact_name varchar2(50),
CONSTRAINT supplier_pk PRIMARY KEY (supplier_id)
);
CREATE TABLE products
( product_id numeric(10) not null,
supplier_id numeric(10) not null,
CONSTRAINT fk_supplier
FOREIGN KEY (supplier_id)
REFERENCES supplier (supplier_id)
);
then insert data
INSERT INTO supplier
(supplier_id, supplier_name, contact_name)
VALUES (1000, 'Microsoft', 'Bill Gates');
INSERT INTO products
(product_id, supplier_id)
VALUES (50000, 1000);
then do trigger
create or replace trigger sup_z
after delete on supplier
for each row
declare
v_error_constraint exception;
pragma exception_init(v_error_constraint, -2292);
begin
null;
exception
When v_error_constraint then
raise_application_error(-20001,
'My ovvervide:)');
End;
then do delete to generate message
DELETE from supplier
WHERE supplier_id = 1000
but I see not my message in trigger I see
ORA-02292: integrity constraint (IVANKA.FK_SUPPLIER) violated - child record found
Can you help me? What am I doing wrong?
Your trigger is fired after the DELETE has been done, so it will never fire if the DELETE gives an error.
You could use a BEFORE trigger to avoid the DELETE, if you find some child records; for example:
create or replace trigger sup_z
before delete on supplier
for each row
declare
vNumberOfChild number;
begin
select count(1)
into vNumberOfChild
from products
where supplier_id = :old.supplier_id;
--
if vNumberOfChild > 0 then
raise_application_error(-20001, 'My ovveride:)');
end if;
End;
Another way could be defining a procedure to handle the delete:
SQL> create or replace procedure deleteSupplier(p_id in numeric) is
2 vNumberOfChild number;
3 begin
4 select count(1)
5 into vNumberOfChild
6 from products
7 where supplier_id = p_id;
8 --
9 if vNumberOfChild > 0 then
10 dbms_output.put_line('My ovveride');
11 else
12 delete supplier
13 where supplier_id = p_id;
14 end if;
15 end;
16 /
Procedure created.
SQL> set serveroutput on
SQL> exec deleteSupplier(1000);
My ovveride
PL/SQL procedure successfully completed.
This avoids running the delete checking the data before, so you need no triggers.
Check this out (Display custom message when constraint is violated PL/SQL). I think you want something similar.
When all you need is just the message to be printed, why do we need a trigger in the first place? Shouldn't we be handling this just with an exception alone?
declare
v_error_constraint exception;
pragma exception_init(v_error_constraint, -2292);
begin
DELETE from supplier
WHERE supplier_id = 1000;
exception
When v_error_constraint then
raise_application_error(-20001,
'My ovvervide:)');
End;

Error: numeric or value errors

I know this error issue was been addressed before, but I can't seem to find any relevant solution so I'm posting this question.
create table subscribers(
num_s number(6,0) ,
name varchar2(30) constraint nameM not null,
surname varchar2(20),
town varchar2(30),
age number(3,0) ,
rate number(3,0) ,
reduc number(3,0) ,
CONSTRAINT subscriber_pk primary key (num_s),
constraint age_c check (age between 0 and 120)
);
create or replace type copy_bookT as object(
num number(6),
loancode varchar2 (10),
book_ref ref bookT
);
create table copy_books of copy_bookT(
constraint pk_cb primary key (num),
constraint chk_st check (loancode in('Loan', 'Not')),
loancode default 'Loan' not null
);
create table Lending(
cb_num number(6),
sb_num number(6),
date_L date,
constraint fk_cb foreign key (cb_num) references copy_books(num),
constraint fk_sb foreign key (sb_num) references Subscribers(num_s)
);
create or replace trigger chk_DateL
for insert or update on lending
COMPOUND TRIGGER
--declare
L_Date int;
avail varchar2(10);
subtype copy_booksRec is lending%ROWTYPE;
type copied_bks is table of copy_booksRec;
cbks copied_bks := copied_bks();
before each row is
begin
cbks.extend;
cbks(cbks.last).cb_num := :new.cb_num;
cbks(cbks.last).sb_num := :new.sb_num;
end before each row;
before statement is
begin
for i in cbks.first .. cbks.last loop
select loancode into avail from copy_books where num = cbks(i).cb_num;
select count(date_L) into L_Date from lending where sb_num = cbks(i).sb_num and date_L = cbks(i).date_L;
if (L_Date = 0 and avail = 'Loan') then
update copy_books set loancode = 'Not' where num = cbks(i).cb_num;
cbks.delete;
-- cbks(i).date_L := cbks(i).date_L;
else
dbms_output.put_line('You can only make ONE LOAN at a time! You have already loaned a book on ' || L_Date);
cbks.delete;
end if;
end loop;
-- FORALL i IN cbks.first .. cbks.last
-- insert into lending values cbks(i);
cbks.delete;
end before statement;
end chk_DateL;
/
show errors
It all compiles successfully, but when I try to insert a sample record like:
insert into lending values (2, 700, '10-MAR-14');
it raises a numeric error which comes from the trigger line 18. I don't know what needs fixing despite my efforts.
You should not count on Oracle's default date format to translate your string literal to a date value , you should define the format you're using explicitly:
insert into lending values (2, 700, to_date('10-MAR-14', 'DD-MON-YY'));
While the date format issue is a valid point, that isn't causing your error. It's coming from line 18, which is the for ... loop line:
before statement is
begin
for i in cbks.first .. cbks.last loop
You've got cbks being extended and populated from the before row part of the trigger. When the before statement part fires, cbks is empty, as the row-level trigger hasn't fired yet. It's the first and last references that are throwing the ORA-06502: PL/SQL: numeric or value error error.
You can demonstrate the same thing with a simple anonymous block:
declare
type my_type is table of dual%rowtype;
my_tab my_type := my_type();
begin
for i in my_tab.first .. my_tab.last loop
null;
end loop;
end;
/
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 5
SQL Fiddle; you can see you can avoid it by adding an extend, but that doesn't really help you in your version, since you seem to want the row values. (You can eliminate the error in your code with an extend, but it's unlikely to do what you want still).
I'm really not sure what you're trying to achieve here, so I don't really have any advice on what you need to do differently.
as Mureinik already told, Oracle does not know about how to transform your varchar2 into date datatype and you should use date explicitly. But instead of making to_date use date literal - in my opinion it is more clear than using of to_date function
insert into lending values (2,700,date '2014-03-10');
by the way, you can simply change your NLS settings by altering the current session and installing the date format you need

PL/SQL Assistance Needed with Trigger

I am in the process of trying to create a trigger that when a record is inserted into the employee table(a new employee), the trigger fires and inserts a record into the employee_dept_history table as 'N/A' (since this employee is new and has no previous department). Also, if a current employee switches departments the trigger should insert a record in the employee_dept_history table and the employees depatment_id changes. I am having problems creating this trigger.I was wondering if someone could lead me to the right direction with my code. I can't get the below trigger to display 'N/A'. How could I go about making this trigger work? Do I need to create local variables for the new and old department?
CREATE OR REPLACE TRIGGER employee_dept_trigger
BEFORE INSERT OR UPDATE ON employee
FOR EACH ROW
BEGIN
INSERT INTO employee_dept_history
VALUES(:NEW.employee_id,
:NEW.employee_name,
from_department_name (NEEDS TO OUTPUT 'N/A'),
to_department_name,
sysdate);
END employee_dept_trigger;
The EMPLOYEE_DEPT_HISTORY table looks like:
CREATE TABLE empployee_dept_history
(
EMPLOYEE_ID NUMBER(4)
EMPLOYEE_NAME VARCHAR2(50)
FROM_DEPARTMENT_NAME VARCHAR2(50)
TO_DEPARTMENT_NAME VARCHAR2(50)
OPERATION_TIME DATE
);
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)
);
Note: This answer is similar to the other two, but I'll post it anyway because it defines the trigger differently in a way that may be useful. Please note that I've up-voted the other two answers as they'll do the job nicely.
First, if you're only interested in changes to the DEPARTMENT_ID column, you can specify that in your trigger definition:
CREATE OR REPLACE TRIGGER employee_dept_trigger
BEFORE INSERT OR UPDATE OF department_id ON employee
FOR EACH ROW
....
A trigger can use the "automatic" logical values INSERTING and UPDATING to determine if an insert or an update called it. You can use the INSERTING value to indicate that this is a new employee:
IF INSERTING THEN
-- set old department name = 'N/A'
ELSE
-- set old department name = whatever
END IF;
Your question doesn't describe where the department name comes from, so this example descends into pseudocode when it comes to the department names:
CREATE OR REPLACE TRIGGER employee_dept_trigger
BEFORE INSERT OR UPDATE OF department_id ON employee
FOR EACH ROW
BEGIN
IF INSERTING THEN
-- New employee
INSERT INTO employee_dept_history VALUES (
:NEW.employee_id,
:NEW.employee_name,
'N/A',
(SELECT department_name from ...), <-- based on :NEW.department_id
SYSDATE);
ELSE
-- Existing employee
INSERT INTO employee_dept_history VALUES (
:NEW.employee_id,
:NEW.employee_name,
(SELECT department_name from ...), <-- based on :OLD.department_id
(SELECT department_name from ...), <-- based on :NEW.department_id
SYSDATE);
END IF;
END employee_dept_trigger;
It's either
CREATE OR REPLACE TRIGGER employee_dept_trigger
BEFORE INSERT OR UPDATE ON employee
FOR EACH ROW
DECLARE
from_department_name varchar(255);
to_department_name varchar(255);
BEGIN
select from DEPARTMENT where DEPARTMENT_ID=:NEW.DEPARTMENT_ID into to_department_name;
IF INSERTING THEN
INSERT INTO employee_dept_history
VALUES(:NEW.employee_id,
:NEW.employee_name,
'N/A',
to_department_name,
sysdate);
end if;
IF UPDATING THEN
select from DEPARTMENT where DEPARTMENT_ID=:OLD.DEPARTMENT_ID into from_department_name;
INSERT INTO employee_dept_history
VALUES(:NEW.employee_id,
:NEW.employee_name,
from_department_name ,
to_department_name,
sysdate);
end if;
END employee_dept_trig;
or I don't understand your question.
You probably want something like this:
CREATE OR REPLACE TRIGGER employee_dept_trigger
BEFORE INSERT OR UPDATE ON employee
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO employee_dept_history
VALUES(:NEW.employee_id,
:NEW.employee_name,
'N/A',
:NEW.DEPARTMENT_ID,
sysdate);
ELSE
INSERT INTO employee_dept_history
VALUES(:NEW.employee_id,
:NEW.employee_name,
:OLD.DEPARTMENT_ID,
:NEW.DEPARTMENT_ID,
sysdate);
END IF;
END employee_dept_trigger;
It won't be quite right yet though as you haven't indicated where the department name comes from. I've used department id just to give you an idea..

After insert trigger update

CREATE TABLE "EMPLOYEE_BP"
( "EMP_ID" VARCHAR2(10) NOT NULL ENABLE,
"FNAME" VARCHAR2(20),
"LNAME" VARCHAR2(20),
"JOB_ROLE" VARCHAR2(20),
"AIRPORT_CODE" VARCHAR2(10) NOT NULL ENABLE,
"SALARY" NUMBER(9,0),
"MOBILE" NUMBER(10,0)
);
CREATE or REPLACE TRIGGER emp_after_insert AFTER INSERT ON EMPLOYEE
FOR EACH ROW
DECLARE
BEGIN
INSERT INTO EMPLOYEE_BP values (:NEW.EMP_ID, :NEW.FNAME, :NEW.LNAME, :NEW.JOB_ROLE, : NEW.AIRPORT_CODE, : NEW.SALARY, : NEW.MOBILE);
DBMS_OUTPUT.PUT_LINE('Record successfully inserted into emp_backup table');
END;
--> Apparently constraints are not inserted into backup tables
​The table gets created, but it gives me an error for the trigger on line 4,where the begin statement is. Ther error is error at line 4 statement ignored. The synthax seems ok and I'm confident it's a small error but I can't figure it out. I am using Oracle.
Thanks in advance.
Running the trigger as you have it, I actually get an internal Oracle error, which is not good. But I think the problem is with the spaces you have between the : and the NEW.
This works for me:
SQL> CREATE or REPLACE TRIGGER emp_after_insert AFTER INSERT ON EMPLOYEE
2 FOR EACH ROW
3 DECLARE
4 BEGIN
5 INSERT INTO EMPLOYEE_BP values (:NEW.EMP_ID, :NEW.FNAME, :NEW.LNAME, :NEW.JOB_ROLE, :NEW.AIRPORT_CODE, :NEW.SALARY, :NEW.MOBILE);
6 DBMS_OUTPUT.PUT_LINE('Record successfully inserted into emp_backup table');
7 END;
8 /
Trigger created.
I'm not an Oracle expert but you would either need to strike your DECLARE line or actually declare something based on this example
CREATE or REPLACE TRIGGER emp_after_insert AFTER INSERT ON EMPLOYEE
FOR EACH ROW
DECLARE
unused varchar2(10);
BEGIN
INSERT INTO EMPLOYEE_BP values (:NEW.EMP_ID, :NEW.FNAME, :NEW.LNAME, :NEW.JOB_ROLE, :NEW.AIRPORT_CODE, :NEW.SALARY, :NEW.MOBILE);
DBMS_OUTPUT.PUT_LINE('Record successfully inserted into emp_backup table');
END;