I do not know how to create log trigger - oracle

I need to create log trigger for after update, after insert and before delete.
In the accounts_history table I have more rows than in the table of accounts and it confuses me
How to write that trigger?
I tried to do it but I did not succeed, This is how I created tables and sequences.
I'm a beginner in Oracle and plsql
I'm sorry if I did not explain my problem well.
create table accounts (
id number,
name varchar2(32),
amount number,
date date
);
create sequnce acc_seq_id
start wtih 1
increment by 1
nocache
nocycle;
create table accounts_history (
id number
, old_name varchar2(32)
, new_name varchar2(32)
, old_amount number
, new_amount number
, change_date date
);
My trigger for only after update
create or replace trigger after_update
after update
on accounts referencing new as new old as old
for each row
begin
iNSERT INTO account_history
(
id,
name,
old_name,
amount,
old_amount,
date
)
values
(
:old.id,
:new.name,
:old.old_name,
:new.amount,
:old.old_amount,
sysdate
);
end;
/
The error:
SQL> show error
Errors for TRIGGER AFTER_UPDATE:
LINE/COL ERROR
-------- --------------------------------------------------------------
2/1 PL/SQL: SQL Statement ignored
9/2 PL/SQL: ORA-01747: invalid user.table.column, table.column, or
column specification
13/2 PLS-00049: bad bind variable 'OLD.ID'
14/2 PLS-00049: bad bind variable 'NEW.NAME'
15/2 PLS-00049: bad bind variable 'OLD.OLD_NAME'
16/2 PLS-00049: bad bind variable 'NEW.AMOUNT'
17/2 PLS-00049: bad bind variable 'OLD.OLD_AMOUNT'
SQL>

The values you insert come from the ACCOUNTS table, the columns you target come ACCOUNT_HISTORY table. You have mixed them up, which is why you get the ORA-01747 error. Try this:
create or replace trigger after_update
after update on accounts
referencing new as new old as old
for each row
begin
INSERT INTO account_history
(
id,
new_name,
old_name,
new_amount,
old_amount,
change_date
)
values
(
:old.id,
:new.name,
:old.name,
:new.amount,
:old.amount,
sysdate
);
End;
/

Related

Getting error while creating Trigger in Oracle

I have created a trigger below but its giving me error as
3/1 PLS-00428: an INTO clause is expected in this SELECT statement 5/1 PL/SQL: SQL Statement ignored 6/28 PL/SQL: ORA-00984: column not allowed here Errors: check compiler log
Below is my code
create or replace TRIGGER "APP_WFM"."TRG_INS_NE_SF_SITE_INSTANCE"
BEFORE INSERT OR UPDATE ON NE_SITE_INSTANCE
FOR EACH ROW
BEGIN
select rf_siteid, sap_id, site_name from SF_NE_DETAILS;
INSERT INTO NE_SITE_INSTANCE (rf_siteid, sap_id, sitename)
VALUES (rf_siteid, sap_id, site_name);
END;
What can I try to resolve this?
Based on your previous question, you want to use the :NEW record to get the inserted values and then insert into the second table that you are copying into:
CREATE TRIGGER APP_WFM.TRG_INS_NE_SF_SITE_INSTANCE
BEFORE INSERT OR UPDATE ON NE_SITE_INSTANCE -- Table being copied from
FOR EACH ROW
BEGIN
INSERT INTO SF_NE_DETAILS ( -- Table being copied to
rf_siteid,
sap_id,
site_name
) VALUES (
:NEW.rf_siteid,
:NEW.sap_id,
:NEW.site_name
);
END;
/
db<>fiddle here

inserting into relational table from entity table

Hi I want to create a trigger which will insert values on relational table A_EQ, A_U and A_P after inserting the values to entity table ACCOUNTS. The values will the inserted into A_EQ if only the ACCOUNTS.TYPE='Equipments', into A_U if only ACCOUNTS.TYPE='Utility' and into A_P for the rest.
Whenever I'm trying to compile the trigger certain errors are showing:
Error(40,13): PLS-00049: bad bind variable 'NEW.A_U'
Error(41,13): PLS-00049: bad bind variable 'NEW.A_U'
Error(42,13): PLS-00049: bad bind variable 'NEW.A_U'
Error(45,9): PLS-00049: bad bind variable 'NEW.A_EQ'
Error(46,13): PLS-00049: bad bind variable 'NEW.A_EQ'
Error(47,13): PLS-00049: bad bind variable 'NEW.A_EQ'
Error(50,9): PLS-00049: bad bind variable 'NEW.A_P'
Error(51,13): PLS-00049: bad bind variable 'NEW.A_P'
Error(52,13): PLS-00049: bad bind variable 'NEW.A_P'
Error(57,4): PLS-00103: Encountered the symbol ";" when expecting one of the following: if
The Code for trigger is given below:
create or replace trigger accounts_relational_insert
after insert on accounts
REFERENCING NEW AS new OLD AS Old
for each row
declare
sys_month VARCHAR2 (10) ;
i_month VARCHAR2 (10);
old1 int;
curr_amount number (20,2);
curr_id varchar2 (20);
curr_txn varchar2 (20);
curr_type varchar2 (20);
begin
select to_char(SYSDATE,'Month') into sys_month from dual;
select ACCOUNTS_ID_SEQ.currval into old1 from dual;
select to_char(paid_on,'Month') into i_month from accounts where transaction_id ='Txn' || lpad(old1,9,'0');
select amount into curr_amount from accounts where transaction_id='Txn' || lpad(old1,9,'0');
select id into curr_id from accounts where transaction_id='Txn' || lpad(old1,9,'0');
select transaction_id into curr_txn from accounts where transaction_id='Txn' || lpad(old1,9,'0');
select type into curr_type from accounts where transaction_id='Txn' || lpad(old1,9,'0');
if i_month = sys_month then
if curr_type ='Utility' then
:new.a_u.transaction_id := curr_txn;
:new.a_u.u_id :=curr_id;
:new.a_u.amount := curr_amount;
else if curr_type='Equipments' then
:new.a_eq.transaction_id := curr_txn;
:new.a_eq.eq_id :=curr_id;
:new.a_eq.amount := curr_amount;
else
:new.a_p.transaction_id := curr_txn;
:new.a_p.barcode:=curr_id;
:new.a_p.amount := curr_amount;
end if;
end if;
end;
The Tables are given below:
It will be very helpful if someone tells me how to resolve this error and also if there is any other way to insert into relational tables after inserting on accounts table.
You can rearrange the trigger simply as simple as this one
CREATE OR REPLACE TRIGGER accounts_relational_insert
AFTER INSERT ON accounts
FOR EACH ROW
BEGIN
IF TO_CHAR(:new.paid_on, 'YYYYMM') = TO_CHAR(sysdate, 'YYYYMM') THEN
IF :new.type = 'Utility' THEN
INSERT INTO a_u VALUES(:new.id, :new.transaction_id, :new.amount);
ELSIF :new.type = 'Equipments' THEN
INSERT INTO a_eq VALUES(:new.id, :new.transaction_id, :new.amount);
ELSE
INSERT INTO a_p VALUES(:new.id, :new.transaction_id, :new.amount);
END IF;
END IF;
END;
/
where
the values, which are already been able to get from identifiers of
columns of the table accounts qualified with :new , to be
inserted shouldn't return from SELECT statements. Moreover, you would get mutating
trigger error for this case.
indeed, defining the local variables are not needed due to the fact
that expressed above, and also values for i_month and sys_month
will directly be derived from TO_CHAR(:new.paid_on, 'YYYYMM') and
TO_CHAR(sysdate, 'YYYYMM') respectively. Btw, do not prefer using string
expression within character conversion such as Month, since you might not
only face case sensitivity issues, but also might fail for international
studies related to language differences.
the trigger is created on accounts table, for the other tables you
should use INSERT statements, cannot use such variable assignments.
The expression REFERENCING NEW AS new OLD AS old is redundant, as
being default.

encountering "Warning: Trigger created with compilation errors." while creating multiple triggers

I was trying to create multiple triggers in an sql file. And the code is as follows:
CREATE OR REPLACE TRIGGER trigger_selling_price_change
AFTER INSERT OR UPDATE ON SellingPrice
FOR EACH ROW
BEGIN
dbms_output.put_line('price of phone updated');
INSERT INTO LogSellingPriceChange
VALUES(sysdate, phone_id, :old.price, :new.price);
END;
/
CREATE OR REPLACE TRIGGER trigger_purchase_cost_change
AFTER INSERT OR UPDATE ON PurchaseCost
FOR EACH ROW
BEGIN
dbms_output.put_line('purchase cost updated');
INSERT INTO LogPurchaseCostChange
VALUES(sysdate, phone_id, :old.cost, :new.cost);
END;
/
CREATE OR REPLACE TRIGGER trigger_inventory_addition
AFTER INSERT ON Inventory
FOR EACH ROW
BEGIN
dbms_output.put_line('enlisted new phones in database');
INSERT INTO LogInventoryAddition
VALUES(sysdate, phone_id, :old.quantity, :new.quantity);
END;
/
But when I execute it in pl/sql I get Warning: Trigger created with compilation errors. three times.
Upon executing show_errors I get the following output:
Errors for TRIGGER TRIGGER_INVENTORY_ADDITION:
LINE/COL ERROR
-------- -----------------------------------------------------------------
3/2 PL/SQL: SQL Statement ignored
4/18 PL/SQL: ORA-00984: column not allowed here
Declaration of the tables included in the query are as follows:
CREATE TABLE Inventory(
id number,
phone_id number,
quantity number,
PRIMARY KEY (id),
FOREIGN KEY (phone_id) REFERENCES Phone(id)
);
CREATE TABLE PurchaseCost(
id number,
phone_id number,
cost number,
PRIMARY KEY(id),
FOREIGN KEY (phone_id) REFERENCES Phone(id)
);
CREATE TABLE SellingPrice(
id number,
phone_id number,
price number,
PRIMARY KEY(id),
FOREIGN KEY (phone_id) REFERENCES Phone(id)
);
CREATE TABLE LogSellingPriceChange(
change_date date,
phone_id number,
old_price number,
new_price number,
FOREIGN KEY(phone_id) REFERENCES Phone(id)
);
CREATE TABLE LogPurchaseCostChange(
change_date date,
phone_id number,
old_price number,
new_price number,
FOREIGN KEY(phone_id) REFERENCES Phone(id)
);
CREATE TABLE LogInventoryAddition(
addition_date date,
phone_id number,
old_quantity number,
new_quantity number,
FOREIGN KEY(phone_id) REFERENCES Phone(id)
);
I executed the file for creating triggers after successfully creating the tables. However, I did not insert any values.
Column not allowed error because you are trying to access the phone_id column directly without the psudo key :new or :old,
CREATE OR REPLACE TRIGGER trigger_selling_price_change
AFTER INSERT OR UPDATE ON SellingPrice
FOR EACH ROW
BEGIN
dbms_output.put_line('price of phone updated');
INSERT INTO LogSellingPriceChange
VALUES(sysdate, :new.phone_id, :old.price, :new.price);
END;
/
You need to change in other triggers as well.

What mistake have I made while creating TRIGGER?

After creating the trigger, I got a warning:
Trigger created with compilation errors
What should I do?
SQL> create trigger customer_trig
2 BEFORE INSERT OR UPDATE OR DELETE ON customer1
3 FOR EACH ROW
4 BEGIN
5 set customer1.t_cost=customer1.pprice*customer1.tq
6 END;
7 /
Warning: Trigger created with compilation errors.
SQL> desc customer1;
Name Null? Type
----------------------------------------- -------- ----------------------------
CID VARCHAR2(10)
NAME VARCHAR2(20)
PNAME VARCHAR2(15)
PPRICE NUMBER(5)
TQ NUMBER(5)
T_COST NUMBER(7)
SQL> insert into customer1 values('ssr345','vikram','book',30,12,0);
insert into customer1 values('ssr345','vikram','book',30,12,0)
*
ERROR at line 1:
ORA-04098: trigger 'RAYUDU.CUSTOMER_TRIG' is invalid and failed re-validation
You are not using the trigger properly. You must use :NEW for referencing the new value and ON DELETE event is not needed here.
I think you need the following:
CREATE TRIGGER CUSTOMER_TRIG BEFORE
INSERT OR UPDATE
--OR DELETE
ON CUSTOMER1
FOR EACH ROW
BEGIN
:NEW.T_COST := :NEW.PPRICE * :NEW.TQ;
END;
/
As #jarlh asked Oracle does have computed columns. In Oracle speak that's a virtual column. And is the best solution here. Redefine the column T_COST as a virtual column. If your table already exists then:
alter table customer1 drop column t_cost;
alter table customer1 add ( t_cost number(7) generated always as (pprice * tq ) virtual);
Note: The above does not require "generated always" not "virtual" phrases but does enhance readability (at least IMO);

is there a better way to write avg procedure?

I wrote this procedure but it keeps showing "WARNING: Procedure creates with compilation errors" and I don't know why, here's the table I created
create table EnrolledInClasses (
St_Id char(9) primary key,
C_Id char(6),
GradeN Number(2),
constraint FK_StId Foreign key (St_Id) references Student (St_Id),
constraint FK_CoID Foreign key (C_Id) references course (C_Id)
constraint CheckGrade check (Grade>-1)
);
and here's the procedure:
CREATE OR REPLACE PROCEDURE Avg_grades
IS
avg_grades NUMBER := 0;
BEGIN
SELECT AVG (Grade)
INTO avg_grades
FROM EnrolledInClasses
dbms_output.put_line('The average of grades is :'||avg_grades);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line ('No Data Is Found..');
END;
/
Since you are using PL/SQL Developer, make sure you are writing your code in a Procedure window. Then the compilation error will be clearly highlighted. In this case it's the missing semicolon at the end of the select into statement.
Or in SQL*Plus you can use show errors:
Warning: Procedure created with compilation errors.
SQL> show errors
Errors for PROCEDURE AVG_GRADES:
LINE/COL ERROR
-------- -----------------------------------------------------------------
6/1 PL/SQL: SQL Statement ignored
9/12 PL/SQL: ORA-00933: SQL command not properly ended
or the full syntax, show errors procedure avg_grades.
Or you can directly query dba|all|cdb|user_errors:
select * from user_errors e
where e.name = 'AVG_GRADES'
and e.attribute = 'ERROR';
Also, the table definition could be written more succinctly as:
create table enrolledinclasses
( st_id primary key constraint fk_stid references student(st_id)
, c_id constraint fk_coid references course (c_id)
, grade number(2) constraint checkgrade check (grade>-1)
);
student.st_id and course.c_id should probably be numbers or integers, or at least varchar2, and not char.
It is good practice to use code indentation to align code into blocks showing their dependency structure. That would give something like this:
create or replace procedure avg_grades as
avg_grades number := 0;
begin
select avg(grade) into avg_grades
from enrolledinclasses;
dbms_output.put_line('The average grade is: ' || avg_grades);
exception
when no_data_found then
dbms_output.put_line('No Data Is Found.');
end;
You might want to round() that average before displaying it, as by default it could be displayed with a lot more decimal places than you might want.
Also, it's not possible to get a no_data_found exception from a single-group aggregate query like this. If there are no rows in the table you will get a null value as the result, not an error.

Resources