Audit Table Trigger - sql

Trying to create a trigger for populating an audit table with old data before changes are made to the base table BOOKS
CREATE OR REPLACE TRIGGER populate_audit_table
BEFORE INSERT OR UPDATE OF cost, retail OR DELETE
ON books
REFERENCING NEW as new OLD as old
FOR EACH ROW
DECLARE
dml_operation varchar2(1) :=
CASE WHEN UPDATING THEN 'U'
WHEN DELETING THEN 'D'
ELSE 'I'
END;
BEGIN
INSERT INTO cost_retail_history
(isbn
,title
,pubid
,pubdate
,cost
,retail
,discount
,category
,dml_operation)
SELECT
(old.isbn
,old.title
,old.pubid
,old.pubdate
,old.cost
,old.retail
,old.discount
,old.category
,new.dml_operation)
FROM BOOKS;
end;
I get the following errors:
Errors: TRIGGER POPULATE_AUDIT_TABLE
Line/Col: 8/7 PL/SQL: SQL Statement ignored
Line/Col: 20/13 PL/SQL: ORA-00907: missing right parenthesis
I also tried using VALUES on the INSERT INTO and not designating a source table ("FROM BOOKS")
....VALUES
(old.isbn
,old.title
,old.pubid
,old.pubdate
,old.cost
,old.retail
,old.discount
,old.category
,new.dml_operation);
end;
And I get the following errors:
Line/Col: 8/7 PL/SQL: SQL Statement ignored
Line/Col: 27/18 PL/SQL: ORA-00984: column not allowed here
I've read and re-read https://docs.oracle.com/cd/A64702_01/doc/server.805/a58225/ch4a.htm#1997457 so I feel like I'm missing something simple and just need a point in the right direction.
Thanks,
EDIT:
I dropped the dml_operation as this wasn't required and followed the advice below, but I am still getting errors:
Current version:
CREATE OR REPLACE TRIGGER cost_retail_history
BEFORE INSERT
OR UPDATE OF cost, retail
OR DELETE
ON books
REFERENCING NEW as new OLD as old
FOR EACH ROW
BEGIN
CASE
WHEN INSERTING THEN
INSERT INTO cost_retail_history
(isbn
,title
,pubid
,pubdate
,cost
,retail
,discount
,category)
VALUES
(:new.isbn
,:new.title
,:new.pubid
,:new.pubdate
,:new.cost
,:new.retail
,:new.discount
,:new.category)
WHEN UPDATING cost, retail OR DELETING THEN
INSERT INTO cost_retail_history
(isbn
,title
,pubid
,pubdate
,cost
,retail
,discount
,category)
VALUES
(:old.isbn
,:old.title
,:old.pubid
,:old.pubdate
,:old.cost
,:old.retail
,:old.discount
,:old.category);
END CASE;
END;
I'm getting the errors:
Errors: TRIGGER COST_RETAIL_HISTORY
Line/Col: 4/9 PL/SQL: SQL Statement ignored
Line/Col: 22/5 PL/SQL: ORA-00933: SQL command not properly ended
I can't tell exactly what line the error is on as this would indicate it's with:
...OR DELETE
and
... ,:new.title
which does not make sense to me.

If you were doing insert ... select ... then you should not have the parentheses around the column list; i.e.:
INSERT INTO cost_retail_history
(isbn
...
,dml_operation)
SELECT
old.isbn
...
,new.dml_operation
FROM BOOKS;
but you should not be selecting from the table the trigger is against - partly because you'll get a mutating table error, and you are attempting to insert an audit row for every row in the base table; but mostly because you already have all of the information you need.
With the values clase you need to prefix the old/new values with a colon:
....VALUES
(:old.isbn
,:old.title
,:old.pubid
,:old.pubdate
,:old.cost
,:old.retail
,:old.discount
,:old.category
,:new.dml_operation);
end;
Read more in the documentation.
Oracle has built-in audit tools but presumably this is an exercise.
I can't tell exactly what line the error is on as this would indicate...
This can be slightly confusing with triggers as they are a mix of SQL and PL/SQL. Because you are getting PL/SQL errors the line numbering starts from the beginning of the PL/SQL part, so here BEGIN is line 1, INSERT is line 4; WHEN UPDATING... is line 22.
Those new errors are just because you don't have a semicolon at the end of the first INSERT statement:
,:new.discount
,:new.category);
-----------------------^
WHEN UPDATING cost, retail OR DELETING THEN
INSERT INTO cost_retail_history
The error is reported against line 22 because that WHEN is where it's expecting to see the semicolon, and the first point it can see the previous SQL command hasn't been completed. The error against line 4 is saying where that not-ended command started; so you're getting two messages for one actual problem.

Related

Why am I receiving the PSL-00103 Error in Oracle SQL?

I am trying to make a procedure that would insert a row into a table in Oracle SQL. However, I can't figure out a solid reason on why this issue exists when I write any type of procedure.
I have tried changing the syntax around a couple of times, but I still don't know how to remedy the issue.
Errors given:
Error at line 1: PLS-00103: Encountered the symbol ")" when expecting one of the following: in out table ... columns long double ref char time timestamp interval date binary national character nchar
Error at line 5: PL/SQL: SQL Statement ignored Error at line 6: PL/SQL: ORA-00984: column not allowed here
Code:
create or replace procedure insert_category(
category_name_param in categories.category_name%type)
as
begin
insert into categories (category_id, category_name)
values (category_id, category_name_param);
end;
It seems that procedure lacks in yet another parameter; see if this helps (I presumed that such a column exists in the table; can't be sure as you didn't post table description):
create or replace procedure insert_category(
category_name_param in categories.category_name%type,
category_id_param in categories.category_id%type --> this
)
as
begin
insert into categories
(category_id,
category_name)
values
(category_id_param, --> this
category_name_param);
end;

Creating a trigger that will update an attribute

Question: write a trigger that will update the invoice subtotal each time the line table is updated (a new row inserted, updated or deleted). Include the SQL statements used to test the insert, update and delete.
I'm finding it difficult to properly understand triggers and I don't know why. I understand the basic concept of it (at least I think I do) but I can't seem to understand how to answer my question. The following code is my attempt at answering the above question:
create or replace trigger update_subtotal
after insert or update or delete
on invoice
for each row
begin
insert into line ('inv_number', 'line_number', 'p_code', 'line_units', 'line_price')
values ('1009', '3', '12345-6t', '1', '123.45');
end;
select * from line;
After running this code I ended up with these errors:
Errors: TRIGGER UPDATE_SUBTOTAL
Line/Col: 3/1 PL/SQL: SQL Statement ignored
Line/Col: 3/19 PL/SQL: ORA-00928: missing SELECT keyword
Line/Col: 17/1 PLS-00103: Encountered the symbol "SELECT"
I'm using Oracle Live.
In short: help.
You seem to have the concept backwards. The invoice table needs to be updated when line changes -- so line needs the trigger and the change to invoice is an update. That would be something like this:
create or replace trigger trg_line_update_subtotal
after insert or update or delete
on line
for each row
begin
update invoice i
set total = coalesce(i.total, 0) +
coalesce(:new.line_Units * :new.line_price, 0) -
coalesce(:old.line_Units * :old.line_price, 0)
where i.inv_number = coalesce(:new.inv_number, :old.inv_number);
end;

Oracle PL/SQL - BEFORE INSERT 'table does not exist' error?

I'm taking my first steps in Pl/SQL and am struggling with triggers. I've tried creating the trigger below but am receiving this error:
Error at line 2: PL/SQL: SQL Statement ignored
PL/SQL: ORA-00942: table or view does not exist
To clarify: I have checked the name of the table over and over, and it does exist. It is also in the same schema as the trigger I'm trying to create. The 'customer_seq.NEXTVAL' refers to a sequence created previously that runs without errors.
The code is as follows:
CREATE TRIGGER new_customer
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
INSERT INTO customer_id VALUES ('c-', customer_seq.NEXTVAL);
END;
Thanks in advance for any help.
You probably intend something like this:
CREATE TRIGGER new_customer
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
SELECT customer_seq.NEXTVAL INTO :NEW.customer_id
FROM dual;
END;
It is unclear what the purpose of 'C_' is. If it is part of the customer id, I would advise you to stick to numbers.
Also note that more recent versions of Oracle support generated always as identity -- which is much preferred over defining a sequence and trigger.

SQL Trigger Compilation Error

I am new to SQL Triggers.
The below trigger gives me a error saying that
Error: PL/SQL: ORA-00942: table or view does not exist
Error: PL/SQL: SQL Statement ignored
create or replace trigger HIST
after update of DATE ON EPISODE
for each row
begin
INSERT INTO HIST SELECT * FROM INSERTED;
end HIST;
Any help would be appreciated.
You can't use INSERTED table on Oracle DB.
You should use new values insetad as below
INSERT INTO HIST (col1,col2)
VALUES (:new.col1,:new.col2)
Here you can find more information about triggers.

Creating a INSERT TRIGGER in different table

I need help on creating a trigger on my table1
that will insert specific columns into table2 from table1.
How can i do that? I am using Oracle 11G XE .
Here is my code:
create trigger AllowanceTrigger
on ex_bulacan
after insert
as
insert into allowance VALUES (PLANT_ORIGIN,SO_NO, SO_STATUS,SO_REMARKS,DRIVER_NAME)
select plant_origin, sales_order_no, status,remarks, driver_name
from ex_bulacan;
go ;
When I run that command I get this error
ORA-04071: missing BEFORE, AFTER or INSTEAD OF keyword
The explanation is:
ORA-04071. 00000 - "missing BEFORE, AFTER or INSTEAD OF keyword"
*Cause: The trigger statement is missing the BEFORE/AFTER/INSTEAD OF clause.
*Action: Specify either BEFORE, AFTER or INSTEAD OF.
There is no "go" statement in PL/SQL, and you have not followed the correct syntax for a trigger definition (it is well documented)
Try this:
create trigger AllowanceTrigger
after insert on ex_bulacan
begin
insert into allowance (PLANT_ORIGIN,SO_NO, SO_STATUS,SO_REMARKS,DRIVER_NAME)
select plant_origin, sales_order_no, status,remarks, driver_name
from ex_bulacan;
end;
I'm not sure whether it does what you intended. It fires once per insert statement, not for each row, and it inserts all rows from ex_bulacan into allowance, not just the ones you just inserted. Maybe what you want is actually:
create trigger AllowanceTrigger
after insert on ex_bulacan
for each row
begin
insert into allowance (PLANT_ORIGIN,SO_NO, SO_STATUS,SO_REMARKS,DRIVER_NAME)
values (:new.plant_origin, :new.sales_order_no, :new.status, :new.remarks,
:new.driver_name);
end;
There is a syntax error in your code. You must specify the trigger in this order:
CREATE TRIGGER AllowanceTrigger
AFTER INSERT
ON ex_bulacan