Creating trigger in PL/SQL oracle 9 - plsql

I'M trying to create a trigger based on the question on the image on the link below . but i'm struggling to handle duplicates on update and to handle maximum character length on insert and update. here is my solution.
CREATE OR REPLACE TRIGGER check_author
BEFORE UPDATE OR INSERT ON bk_author
FOR EACH ROW
DECLARE
v_authorid bk_author.authorid%type;
BEGIN
IF UPDATING THEN
IF LENGTH(:NEW.fname) > 10 THEN
RAISE_APPLICATION_ERROR(-20004,'Author name cannot exceed 10 characters');
END IF;
ELSIF INSERTING THEN
SELECT authorid
INTO v_authorid
FROM bk_author
WHERE authorid = :NEW.authorid;
RAISE_APPLICATION_ERROR(-20003,'Author id already existing cannot be added');
IF LENGTH(:NEW.fname) > 10 THEN
RAISE_APPLICATION_ERROR(-20004,'Author name cannot exceed 10 characters');
END IF;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Author '||:NEW.authorid||' added');
END;
/

You don't check condition to raise error about duplicate, it's executing no matter is there ID in database or not.
you should add IF before it
something like
ELSIF INSERTING THEN
SELECT authorid
INTO v_authorid
FROM bk_author
WHERE authorid = :NEW.authorid;
IF v_authorid is not null then
RAISE_APPLICATION_ERROR(-20003,'Author id already existing cannot be
added');
end if;

Try below code, it should work for you.
CREATE OR REPLACE TRIGGER check_author
BEFORE UPDATE OR INSERT ON bk_author
FOR EACH ROW
DECLARE
v_authorid number;
BEGIN
IF UPDATING THEN
IF LENGTH(:NEW.fname) > 10 THEN
RAISE_APPLICATION_ERROR(-20004,'Author name cannot exceed 10 characters');
END IF;
ELSIF INSERTING THEN
SELECT count(authorid) INTO v_authorid
FROM bk_author
WHERE authorid = :NEW.authorid;
DBMS_OUTPUT.PUT_LINE('Author id already existing cannot be added');
RAISE;
IF LENGTH(:NEW.fname) > 10 THEN
DBMS_OUTPUT.PUT_LINE('Author name cannot exceed 10 characters');
RAISE;
END IF;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Author '||:NEW.authorid||' added');
END;
/

Related

Modifying condition for PL/SQL procedure

I have a PL/SQL procedure for deleting records corresponding to a field having NULL value. Am able to achieve this by the below query.
set serveroutput on;
begin
dbms_output.put_line('Execution started');
for rec in (
select name from employee where emp_id is null
and location = 'SITE_A'
)
loop
delete from employeedetails#sitea where name = rec.name;
dbms_output.put_line('name '|| rec.name ||' deleted');
end loop;
dbms_output.put_line('Execution completed');
end;
/
set serveroutput off;
I am running the for loop query from one database and deleting the records in another database ( sitea ) using a database link.
I need to add a condition like, if the name=rec.name is not returning any records to be deleted,then dbms_output.put_line('No records to be deleted');
Is there a comfortable way to achieve it ?
You can use the sql%rowcount implicit cursor attribute to see how many rows were affected by a DML statement; that works across a database link as well as locally:
if sql%rowcount = 0 then
dbms_output.put_line('No records to be deleted');
else
dbms_output.put_line('name '|| rec.name ||' deleted');
end if;
You can include it in the messages too:
if sql%rowcount = 0 then
dbms_output.put_line('name '|| rec.name ||': no records to be deleted');
else
dbms_output.put_line('name '|| rec.name ||': '|| sql%rowcount ||' record(s) deleted');
end if;
Then you'll see something like:
Execution started
name B: 1 record(s) deleted
name C: no records to be deleted
Execution completed
PL/SQL procedure successfully completed.
As name probably isn't unique you could encounter the same value twice as you go through your loop; in which case the first delete will find multiple rows and the second delete will find none. You could avoid the second one by adding distinct to your cursor query.
And if you didn't want to see which names did and did not have remote data to delete then you could use a much simpler single delete, with no loop or PL/SQL, but it seems like this is an exercise anyway...

How to write a pl/sql program to not allow transaction on Sundays using trigger?

I have a table named emp1
Create table emp1
(
Emp_no number(3),
Name varchar(10),
Salary number(9,2),
Dept_no number(3)
);
My trigger is
create or replace trigger tri1
before insert on emp1
for each row
begin
if to_char(sysdate,'day')='sunday' then
dbms_output.put_line('You cannot access');
End if;
End;
After executing this trigger, I insert below statement
insert into emp1(Name, Salary, Dept_no) values ('abc',12000,101);
Every time I insert its always getting inserted.
I have also tried using exception
create or replace trigger tri1
before insert on emp1
for each row
declare
weekday_error exception;
begin
if to_char(sysdate,'day')='sunday' then
raise weekday_error;
end if;
Exception
when weekday_error then
dbms_output.put_line('You cannot access');
End;
With this method also records are always getting inserted.
create or replace trigger tri1
before insert on emp1
for each row
begin
if to_char(sysdate,'Day')='Sunday' then
raise_application_error(-20000,'Cannot do transaction on Sunday');
End if;
End;
There is just one tiny problem with your trigger. TO_CHAR(sysdate,'day') actually returns 9 characters.
like MONDAY that is 6 characters and then 3 Space characters. This is because of WEDNESDAY which has 9 characters.
Your trigger just needs the trim function
create trigger trig1 before insert on emp1
for each row
begin
if trim(to_char(sysdate, 'day')) = 'sunday' then
raise_application_error(-20000,'ikidyounot, today was - ' || to_char(sysdate, 'day'));
end if;
end;
/

PL/SQL Stored procedure

I want to delete record from table based on id using stored procedure. Id value has to be passed as parameter. But while trying this code, data in the table is not deleted. Can anyone help me to get through this
create or replace procedure PROC_INV_DELETE(num in number)
is
begin
delete from table_name
where id = '&num';
commit;
end;
/
This would do your job :
create or replace procedure PROC_INV_DELETE(num in number)
is
begin
delete from table_name
where id = num; ---No need to use & and '' here
commit;
end;
/
Calling:
declare
a number:= '&num' ;
Begin
PROC_INV_DELETE(a);
end;
/
Enter value for num: 4
old 3: a number:= '&num' ;
new 3: a number:= '4' ;
PL/SQL procedure successfully completed.

Raise Exception when record not found

I have a table that includes customer ID and order ID and some other data.
I want to create a procedure that takes customer ID as input and look inside the table.
if that customer exists then print the order details for that customer and
if customer does not exist then raise an exception "Customer not found."
I have this code, but it's not working properly, or maybe I have the wrong approach to this question.
CREATE OR REPLACE PROCEDURE order_details(customer NUMBER)
IS
CURSOR order_cursor IS
SELECT ORDER_ID, ORDER_DATE, TOTAL, CUSTOMER_ID
FROM PRODUCT_ORDER
WHERE CUSTOMER_ID = customer ;
order_row order_cursor%ROWTYPE ;
customer_error EXCEPTION ;
BEGIN
FOR order_row IN order_cursor
LOOP
IF order_cursor%FOUND THEN
dbms_output.put_line ('order id = ' || order_row.ORDER_ID) ;
ELSE
RAISE customer_error ;
END IF;
END LOOP;
EXCEPTION
WHEN customer_error THEN
dbms_output.put_line ('no customer' ) ;
END;
So if I run the procedure with this line
BEGIN
order_details(103);
END;
I get two results because order exists for this customer.
and if I run the procedure with this line
BEGIN
order_details(101);
END;
I don't get anything (not even the error ) because there is no order for that customer.
Table Data
You must use an "Explicit Cursor" instead of "Cursor FOR LOOP". Because the latter just enter the code between LOOP and END LOOP when the query returns more than one record.
CREATE OR REPLACE PROCEDURE order_details(customer NUMBER)
IS
CURSOR order_cursor IS
SELECT ORDER_ID, ORDER_DATE, TOTAL, CUSTOMER_ID
FROM PRODUCT_ORDER
WHERE CUSTOMER_ID = customer ;
order_row order_cursor%ROWTYPE ;
customer_error EXCEPTION ;
BEGIN
OPEN order_cursor;
LOOP
FETCH order_cursor INTO order_row;
EXIT WHEN order_cursor%NOTFOUND;
dbms_output.put_line ('order id = ' || order_row.ORDER_ID);
END LOOP;
IF order_cursor%rowcount = 0 THEN
RAISE customer_error;
END IF;
CLOSE order_cursor;
EXCEPTION
WHEN customer_error THEN
dbms_output.put_line ('no customer' ) ;
END;
Regards

Insertion using triggers by passing values

Lets say I have a table as follows--
create table employees
(
eno number(4) not null primary key,
ename varchar2(30),
zip number(5) references zipcodes,
hdate date
);
And I'm trying to create a trigger with--
CREATE OR REPLACE TRIGGER TWELVE_ONE
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
V_DATE VARCHAR2 (10);
BEGIN
SELECT TO_CHAR (SYSDATE, 'hh24:mi:ss') INTO V_DATE FROM DUAL;
IF (V_DATE >= '12:00:01' AND V_DATE < '13:00:00')
THEN
INSERT INTO TABLE ?????
ELSE
ROLLBACK? TERMINATE TRANSACTION?
END IF;
END;
Purpose of the trigger is to allow an insertion/update during 12:00-13:00 and prevent the insertion at any other time. The trigger construction (thanks to #Melkikun) is seems ok. However now I'm facing the following issues--
How is it possible to pass the values here? I mean lets say my create statement is:
Insert into employees Values (1, 'someone', 11111, '17-12-2015')
And lets say the time is 12:30:01 now. How would the trigger perform the insertion without knowing the values?
And lets say the time is now 13:00:1 now. How would the trigger stop/prevent the insertion?
I'm using Oracle SQL Developer 4.02.15
Many Thanks
You just have to do it the other way.
If the time is not correct,then you raise an exception, so the insert won't be done.
CREATE OR REPLACE TRIGGER TWELVE_ONE
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
V_DATE VARCHAR2 (10);
MyException exception;
BEGIN
SELECT TO_CHAR (SYSDATE, 'hh24:mi:ss') INTO V_DATE FROM DUAL;
IF (V_DATE < '12:00:01' OR V_DATE > '13:00:00')
THEN
raise MyException;
END IF;
EXCEPTION
When MyException then
ROLLBACK;
//output message ...
END;
How would the trigger perform the insertion without knowing the values?
The trigger knows the value thanks to :NEW and :OLD.
You normally use the terms in a trigger using :old to reference the old value and :new to reference the new value.So you will have :NEW.eno ,:NEW.ename ...
Here is an example from the Oracle documentation :
CREATE OR REPLACE TRIGGER Print_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON Emp_tab
FOR EACH ROW
WHEN (new.Empno > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :new.sal - :old.sal;
dbms_output.put('Old salary: ' || :old.sal);
dbms_output.put(' New salary: ' || :new.sal);
dbms_output.put_line(' Difference ' || sal_diff);
END;

Resources