I have a trigger for my task. I updated my trigger which means I added GL_TP column in my trigger. But before delivering that part I also have to rollback that too for undo that column in my trigger.
So how can I undo GL_TP column in my trigger with sql query?
I am open to every ideas or recommendations?
CREATE OR REPLACE TRIGGER GL_DEF_TO_TRG
BEFORE INSERT OR DELETE OR UPDATE OF CDATE, CMPNY_DEF_ID, CUSER, DESCR, GL_DEF_ID,
MNY_TP_ID, ST, UDATE, UUSER, GL_TP ON FCBSADM.GL_DEF
FOR EACH ROW
DECLARE
BEGIN
IF INSERTING THEN
INSERT INTO MTTFOD.GL_DEF_TO
(SEQ_GLDEF_ID, GL_DEF_ID, DESCR, MNY_TP_ID, CMPNY_DEF_ID, ST, CDATE, CUSER, UDATE,
UUSER, CHANGE_TYPE, CHANGE_DATE, GL_TP)
VALUES
(MTTFOD.SEQ_GLDEF_ID.NEXTVAL, :NEW.GL_DEF_ID, :NEW.DESCR, :NEW.MNY_TP_ID,
:NEW.CMPNY_DEF_ID, :NEW.ST, :NEW.CDATE, :NEW.CUSER, :NEW.UDATE, :NEW.UUSER, 'I',
SYSDATE, :NEW.GL_TP);
ELSIF UPDATING THEN
INSERT INTO MTTFOD.GL_DEF_TO
(SEQ_GLDEF_ID, GL_DEF_ID, DESCR, MNY_TP_ID, CMPNY_DEF_ID, ST, CDATE, CUSER, UDATE,
UUSER, CHANGE_TYPE, CHANGE_DATE, GL_TP)
VALUES
(MTTFOD.SEQ_GLDEF_ID.NEXTVAL, :NEW.GL_DEF_ID, :NEW.DESCR, :NEW.MNY_TP_ID,
:NEW.CMPNY_DEF_ID, :NEW.ST, :NEW.CDATE, :NEW.CUSER, :NEW.UDATE, :NEW.UUSER, 'U',
SYSDATE,
NEW:GL_TP);
ELSIF DELETING THEN
INSERT INTO MTTFOD.GL_DEF_TO
(SEQ_GLDEF_ID, GL_DEF_ID, DESCR, MNY_TP_ID, CMPNY_DEF_ID, ST, CDATE, CUSER, UDATE,
UUSER, CHANGE_TYPE, CHANGE_DATE, GL_TP)
VALUES
(MTTFOD.SEQ_GLDEF_ID.NEXTVAL, :OLD.GL_DEF_ID, :OLD.DESCR, :OLD.MNY_TP_ID,
:OLD.CMPNY_DEF_ID, :OLD.ST, :OLD.CDATE, :OLD.CUSER, :OLD.UDATE, :OLD.UUSER, 'D',
SYSDATE, NEW:GL_TP);
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: CODE:' || SQLCODE || ' MSG:' ||
SQLERRM || ' TRACE:' ||
SUBSTR(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE(), 1, 3000));
END;
Your explanation is a bit unclear but I'm assuming this is what you are trying to achieve. After the trigger has been executed, set the value of column GL_TP back to the old value:
...
SYSDATE, NEW:GL_TP); --> note the typo here !
END IF;
-- to revert the change in GL_TP and set it to the old value
:NEW.GL_TP := :OLD.GL_TP;
-- or you could just set it to NULL like this
-- :NEW.GL_TP := NULL;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: CODE:' || SQLCODE || ' MSG:' ||
SQLERRM || ' TRACE:' ||
SUBSTR(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE(), 1, 3000));
END;
Note that there are several typos in your code. You have NEW:GL_TP - that is not valid oracle syntax, it should be :NEW.GP_TP instead.
Note that this is NOT rollback. ROLLBACK is a concept in oracle that indicates the undoing of a transaction. There is no such thing as "Rollback of a column".
Related
My end user is going to enter query like this:
INSERT INTO course(
id, column_1, column_2, column_3, column_4, column_5)
VALUES ('f34-gr5-t46','ABC', '2022-10-18 07:19:29', 2, 'false', now())
ON CONFLICT (id) DO UPDATE
SET (column_1, column_2, column_3, column_4, column_5)
= (EXCLUDED.column_1, EXCLUDED.column_2, EXCLUDED.column_3, EXCLUDED.column_4, EXCLUDED.column_5);
User is going to either create new record or update using this same query. Whenever there is an update I need to store only updated column's name, old value, new value and changed time in my audit table.
I have created a function and a trigger like this:
CREATE OR REPLACE FUNCTION log_changes()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF NEW.column_1 != OLD.column_1 THEN
INSERT INTO course_history(id, course_id, field_name, action_type, old_value, new_value, changed_time)
VALUES(uuid_generate_v4(), OLD.id, 'column_1', 'UPDATE', OLD.column_1, NEW.column_1, new.changed_time);
ELSEIF NEW.column_2 != OLD.column_2 THEN
INSERT INTO course_history(id, course_id, field_name, action_type, old_value, new_value, changed_time)
VALUES(uuid_generate_v4(), OLD.id, 'column_2', 'UPDATE', OLD.column_2, NEW.column_2, new.changed_time);
// and so on for all columns that could be changed in course table
END IF;
RETURN NEW;
END;
$$
CREATE TRIGGER course_changes
BEFORE UPDATE
ON course
FOR EACH ROW
EXECUTE PROCEDURE log_changes();
This function creates new rows for each updated fields in single update. I am trying to reduce repeating code of if condition for each column. How can I achieve that?
Is there a way to combine updates of multiple fields into single row? so that if user updated only two columns in single query, I will have single row showing those updates in history table.
To answer your second question : you can insert only one row with updates on several columns, by using a jsonb column in your table course_history in place of fields field_name, action_type, old_value, new_value, and put only one INSERT statement in your trigger function :
INSERT INTO course_history(id, course_id, changes_jsonb, changed_time)
SELECT uuid_generate_v4(), OLD.id, jsonb_agg(l.change_jsonb) FILTER (WHERE l.change_jsonb IS NOT NULL), new.changed_time)
FROM (SELECT CASE WHEN OLD.column_1 != NEW.column_1 THEN jsonb_build_object('field_name', 'column_1', 'action_type', 'UPDATE', 'old_value', OLD.column_1, 'new_value', NEW.column_1) ELSE NULL END AS change_jsonb
UNION ALL
SELECT CASE WHEN OLD.column_2 != NEW.column_2 THEN jsonb_build_object('field_name', 'column_2', 'action_type', 'UPDATE', 'old_value', OLD.column_2, 'new_value', NEW.column_2) ELSE NULL END
UNION ALL
SELECT CASE WHEN OLD.column_3 != NEW.column_3 THEN jsonb_build_object('field_name', 'column_3', 'action_type', 'UPDATE', 'old_value', OLD.column_3, 'new_value', NEW.column_3) ELSE NULL END
UNION ALL ... ) AS l
To answer your first question : you can try using a dynamic sql command in you trigger function :
DECLARE
col text ;
txt text ;
BEGIN
FOR EACH col IN ARRAY array['column_1', 'column_2', 'column_3', ...]
LOOP
IF txt <> ''
THEN txt = txt || ' UNION ALL ' ;
END IF ;
txt = txt || 'SELECT CASE WHEN OLD.' || col || ' != NEW.' || col || E' THEN jsonb_build_object(\'field_name\', ' || col || E', \'action_type\', \'UPDATE\', \'old_value\', OLD.' || col || E', \'new_value\', NEW.' || col || ') ELSE NULL END AS change_jsonb'
END LOOP ;
EXECUTE 'INSERT INTO course_history(id, course_id, changes_jsonb, changed_time)
SELECT uuid_generate_v4(), OLD.id, jsonb_agg(l.change_jsonb) FILTER (WHERE l.change_jsonb IS NOT NULL), new.changed_time) FROM (' || txt || ') AS l' ;
RETURN NEW ;
END ;
You can get the column names from information_schema.columns. To compare the values for a column, you can use code like:
DECLARE
is_distinct boolean;
col_name text;
BEGIN
/* get col_name FROM the metadata */
EXECUTE format('SELECT $1.%1I IS DISTINCT FROM $2.%1I', col_name)
INTO is_distinct USING OLD, NEW;
IF is_distinct THEN
/* whatever */
END IF;
END;
This is the error I am getting:
Msg 512, Level 16, State 1, Procedure tr_UpdateFolio, Line 361
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I've run up and down this code over and over for hours now. What am I not seeing? Any help would be appreciated. I've been working on this for a few days now, and this is pretty much the one thing I need working in order to get this project done.
-- 2. Write a trigger named tr_UpdateFolio that will be invoked when the Folio table 'status'
-- field ONLY (column update) is changed.
ALTER TRIGGER tr_UpdateFolio ON FOLIO--switch alter back to create
AFTER UPDATE
AS
IF UPDATE([Status])
BEGIN
DECLARE #Status char(1)
DECLARE #FolioID smallint
DECLARE #CheckinDate smalldatetime
DECLARE #Nights tinyint
DECLARE #CurrentDate smalldatetime = '7/27/2016 2:00:00 PM'
SELECT
#Status = i.Status,
#FolioID = i.FolioID,
#CheckinDate = i.CheckinDate,
#Nights = i.Nights
FROM
INSERTED i
-- If Folio status is updated to 'C' for Checkout, trigger two different Insert statements to
-- (1) INSERT in the Billing table, the amount for the total lodging cost as BillingCategoryID1
-- - (normally the FolioRate * number of nights stay, but you must also factor in any late checkout fees*).
-- *Checkout time is Noon on the checkout date. Guest is given a one hour
-- grace period to check out. After 1PM (but before 4PM), a 50% surcharge is added to the FolioRate. After 4PM, an
-- additional full night's FolioRate is applied. You can recycle code from A7 (part 5), but note it's not the exact same
-- function - we only need the late charge (if any).
IF #Status = 'C'
SET #Nights = 1
--#CurrentDate may need to switch back to getdate()
IF DATEDIFF(HOUR, #CheckinDate + #Nights, #CurrentDate) >= 16
SET #Nights = #Nights + 1
ELSE IF DATEDIFF(HOUR, #CheckinDate + #Nights, #CurrentDate) >= 13
SET #Nights = #Nights + .5
UPDATE FOLIO
SET Nights = #Nights
WHERE FolioID = #FolioID
INSERT INTO BILLING (FolioID, BillingCategoryID, BillingDescription, BillingAmount, BillingItemQty, BillingItemDate)
VALUES (25, 1, 'Room', dbo.GetRackRate(11, #CurrentDate) * #Nights, 1, #CurrentDate)
-- (2) The second INSERT statement in the same trigger will insert the Lodging Tax* - as a separate entry in the
-- Billing table for tax on lodging (BillingCategoryID2). *Use the dbo.GetRoomTaxRate function from A7 to determine
-- the Lodging Tax.
INSERT INTO BILLING (FolioID, BillingCategoryID, BillingDescription, BillingAmount, BillingItemQty, BillingItemDate)
VALUES (25, 2, 'Lodging Tax', dbo.GetRoomTaxRate(20), 1, #CurrentDate)
END
GO
-- 3. Write a trigger named tr_GenerateBill that will be invoked when an entry is INSERTED in to the Billing
-- table. If BillngCategoryID is 2 (for testing purposes only) then call the function dbo.ProduceBill (from A7).
ALTER TRIGGER tr_GenerateBill ON BILLING
AFTER INSERT
AS
BEGIN
DECLARE #FolioID smallint
DECLARE #BillingCategoryID smallint
SELECT #FolioID = i.FolioID, #BillingCategoryID = i.BillingCategoryID
FROM INSERTED i
IF #BillingCategoryID = 2
SELECT * FROM dbo.ProduceBill(#FolioID)
END
GO
dbo.producebill should be fine, but the error occurs when I try to run this block
-- 4A. Assume today is (July 27, 2016 at 2PM)*. Anita is due to check out today (from Part 1 above).
-- Write an Update Statement to change the status of her Folio to 'CheckedOut.
-- (Be careful to include a WHERE clause so ONLY here folio is updated).
-- Note: This should automatically invoke tr_UpdateFolio above (factoring in the late charge),
-- which automatically invokes tr_GenerateBill above, and calls dbo.ProduceBill , and produces a bill.
UPDATE FOLIO
SET [Status] = 'C'
WHERE ReservationID = 5020
I'm going nuts trying to figure this out. Thanks.
Let's start with the easier one (tr_GenerateBill). This one is a little odd for sure. You have a select statement in an insert trigger. This means that when you insert a row you are expecting it to return row(s). This is not the typical behavior of an insert but you can work with it.
If you insert 3 rows and only 2 of them have a BillingCategoryID of 2 what should the trigger do? What does that table valued function look like?
This is kind of a guess but you should be able to rewrite that entire trigger to something along these lines.
ALTER TRIGGER tr_GenerateBill ON BILLING
AFTER INSERT
AS
BEGIN
SELECT *
FROM inserted i
cross apply dbo.ProduceBill(i.FolioID) pb
where i.BillingCategoryID = 2
END
Your second trigger is more challenging. It is not totally clear what you are trying to do here but I think this is pretty close.
UPDATE f
set Nights = case when DATEDIFF(HOUR, #CheckinDate + i.Nights, #CurrentDate) >= 16 then 2 else 1 end --adding .5 to a tiny int is pointless. It will ALWAYS be the same value.
from inserted i
join Folio f on f.FolioID = i.FolioID
INSERT INTO BILLING (FolioID, BillingCategoryID, BillingDescription, BillingAmount, BillingItemQty, BillingItemDate)
Select i.FolioID
, 1
, 'Room'
, dbo.GetRackRate(11, #CurrentDate) * case when DATEDIFF(HOUR, #CheckinDate + i.Nights, #CurrentDate) >= 16 then 2 else 1 end, 1, #CurrentDate)
from inserted i
INSERT INTO BILLING (FolioID, BillingCategoryID, BillingDescription, BillingAmount, BillingItemQty, BillingItemDate)
select i.FolioID
, 2
, 'Lodging Tax'
, dbo.GetRoomTaxRate(20), 1, #CurrentDate)
from inserted i
I'm trying to get my head around a bit of PL/SQL logic (I don't have much experience with it!).
A former colleague created a trigger on a table. This trigger fires before an update on the table and basically checks to see if a user is in a list of authorised users in a seperate table. If they are in the list, they're allowed to make changes to a certain column.
create or replace TRIGGER "SATURN".UWB_SFAREGS_ENROLL BEFORE UPDATE ON SFBETRM
FOR EACH ROW
DECLARE
l_user CHAR(12);
l_authorised VARCHAR2(1);
l_new_saved VARCHAR2(2);
l_mailhost VARCHAR2(24) := 'smtp.xxxx';
l_from VARCHAR2(24) := 'xxxx';
l_to VARCHAR2(24) := 'xxxx';
l_mail_conn UTL_SMTP.connection;
BEGIN
IF :OLD.SFBETRM_ESTS_CODE <> :NEW.SFBETRM_ESTS_CODE THEN
l_new_saved := :NEW.SFBETRM_ESTS_CODE;
l_authorised := 'N';
SELECT SYS_CONTEXT ('USERENV', 'SESSION_USER') into l_user FROM DUAL;
SELECT
'Y' INTO l_authorised
FROM STUMAN.UWB_ALLOW_ENROLL
WHERE (
ALLOWED_USER = l_user
AND QUALIFICATION = '9000'
AND :NEW.SFBETRM_TERM_CODE like '9%'
) OR (
ALLOWED_USER = l_user
AND QUALIFICATION IS NULL
);
IF l_authorised = 'N' THEN
:NEW.SFBETRM_ESTS_CODE := :OLD.SFBETRM_ESTS_CODE;
END IF;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN :NEW.SFBETRM_ESTS_CODE := :OLD.SFBETRM_ESTS_CODE;
INSERT INTO SFBETRM_LOG
(
TERM,
PIDM,
OLD_ESTS,
NEW_ESTS,
CHANGE_DATE,
CHANGED_BY,
ALLOWED
)
VALUES
(
:old.SFBETRM_TERM_CODE,
:old.SFBETRM_PIDM,
:old.SFBETRM_ESTS_CODE,
:new.SFBETRM_ESTS_CODE,
SYSDATE,
l_user,
'N'
);
l_mail_conn := UTL_SMTP.open_connection(l_mailhost, 25);
UTL_SMTP.helo(l_mail_conn, l_mailhost);
UTL_SMTP.mail(l_mail_conn, l_from);
UTL_SMTP.rcpt(l_mail_conn, l_to);
UTL_SMTP.data(l_mail_conn, l_user || ' tried to change SFAREGS enrollment status from ' || :OLD.SFBETRM_ESTS_CODE || ' to ' || l_new_saved || ' for PIDM ' || :NEW.SFBETRM_PIDM || ' l_authorised = ' || l_authorised || Chr(13));
UTL_SMTP.quit(l_mail_conn);
END UWB_SFAREGS_ENROLL;
What I'd like to do is restrict this 'authentication' further and limit certain users to being able change the SFBETRM_ESTS_CODE field to certain 'CODES' based on a list of values.
If I select distinct on SFBETRM_ESTS_CODE I get the following values:
--------
RE
IS
RS
NS
RT
ER
SR
TR
RF
EX
EZ
EL
So the above is my list of distinct codes.
Now, my table of authrorised users literally just contains their username. I'd like to expand this further to include a column containing the codes they can change to and from and then include this condition in the trigger For example:
"USER1" "EL, EZ"
"USER2" "EL, EZ, RX"
"USER3" "EL, ER"
"USER4" "RF, RE"
Hope this makes sense (to someone at least!) Any idea how I'd go about this?
TIA
Huskie
You have to use the UPDATING('COLUMN_NAME')
Try to use something like:
CREATE TRIGGER ...
BEFORE UPDATE
...
DECLARE
l_exists number(10);
...
BEGIN
...
WHEN UPDATING ('SFBETRM_ESTS_CODE') THEN
SELECT COUNT(*)
INTO l_exists
FROM DUAL
WHERE EXISTS (SELECT null FROM SFBETRM_ESTS_CODE WHERE code = :new.SFBETRM_ESTS_CODE)
;
IF l_exists = 1
THEN
... -- perform the update operation
ELSE
... -- ignore update operation
END IF;
...
END;
l_exists will be 1 - if the :new.SFBETRM_ESTS_CODE value is stored in the SFBETRM_ESTS_CODE table and 0 - if the SFBETRM_ESTS_CODE table doesn't have the :new.SFBETRM_ESTS_CODE value
P.S: adjust it according to your needs
I need to continue my loop whenever any select statement in my cursor loop does not fetch data. I want to handle the exception for the same. This procedure inserts data whenever it finds it in continuity but as soon as o.id selected by the cursor does not hold the related data it exists the loop and only inserts data for the previously fetched records and does not continue the loop.
CREATE OR REPLACE procedure order_violation1(
p_type number,
p_code varchar2,
p_submit_from date,
p_submit_to date,
p_approved_from date,
p_approved_to date,
p_flag number,
p_status varchar2
) is
pp_type varchar2(100);
pp_company varchar2(50);
pp_code varchar2(20);
pp_ord_num varchar2(50);
pp_status varchar2(50);
SUBMIT_DATE date;
APPROVAL_DATE date;
ORDERING_RATIO_FLAG number;
pp_submit_date date;
pp_app_date date;
pp_package varchar2(3000);
pp_flag NUMBER;
cursor pp_id is
select distinct o.id
from orders o,
partnerprofile pp
where type_id=p_type
and o.ordering_ratio_flag=p_flag
and pp.id=o.to_partner_id
and decode(P_CODE,null,'1',pp.code) = decode(P_CODE,null,'1',p_code)
and decode(p_submit_from,null, to_date('01/01/01','dd/mm/yy'),
to_date(submit_date,'dd/mm/yy')) between
decode(p_submit_from ,null,
to_date('01/01/01','dd/mm/yy'),p_submit_from) and
decode(p_submit_to,null,to_date('01/01/01','dd/mm/yy'),'05-JUL-14')
and decode(p_approved_from,null,
to_date('01/01/01','dd/mm/yy'),
to_date(submit_date,'dd/mm/yy')) between
decode(p_approved_from,null,
to_date('01/01/01','dd/mm/yy'),p_approved_from) and
decode(p_approved_to,null,to_date('01/01/01','dd/mm/yy'),'05-JUL-14')
and decode(p_status,null,'1',o.status) = decode(p_status,null,'1',p_status);
begin
FOR r_partner IN pp_id
loop
select name
into pp_type
from partnertype
where id=p_type;
select code,
company_name
into pp_code,
pp_company
from partnerprofile pp,
orders o
where o.id=r_partner.id
and pp.id=o.to_partner_id;
select ORDER_NUMBER,
STATUS,
SUBMIT_DATE,
APPROVAL_DATE,
ORDERING_RATIO_FLAG
into pp_ord_num,
pp_status,
pp_submit_date,
pp_app_date,
pp_flag
from orders
where id=r_partner.id;
select distinct
rtrim (xmlagg (
xmlelement (e, pk.name||'='||
nvl(oln.total_amount,0) || '||')
).extract ('//text()'), ',')
into pp_package
from package pk,
orderlineitem oln
where oln.package_id=pk.id
and oln.order_id=r_partner.id
GROUP BY oln.order_id;
insert into order_violation_tab1
values (pp_type, pp_code, pp_company, pp_ord_num,
pp_status, pp_submit_date, pp_app_date,
pp_flag, null, null);
--pp_package);
END;
As Nicholas pointed out previous method will not work here. You will have to use exception handling inside loop like below to handle this.
LOOP
BEGIN
-- select code
exception
when no_data_found then
continue;
END;
-- insert code
END LOOP;
Also continue; is feature of 11gr1 and up, for older ones you will have to use goto.
LOOP
BEGIN
-- select code
exception
when no_data_found then
goto label1;
END;
-- insert code
<<label1>>
null;
END LOOP;
I'm trying to create a trigger on oracle database and i'm having troubles with when condition.
When i try to use it i get "invalid relational operator"
create or replace TRIGGER SQLTEST.TRIGGER_TESTE
AFTER INSERT OR UPDATE ON SQLDBA.VT_TABLE
FOR EACH ROW
WHEN (INSERTING OR NEW.FIELD_1 is null and OLD.FIELD_1 is not null or NEW.FIELD_1 <> OLD.FIELD_1)
DECLARE
VAR_FIELD_1 VT_LOG_TABLE.FIELD_1%TYPE;
BEGIN
SELECT SQLDBA.SEQ_LOG_TABLE.NEXtval into VAR_FIELD_1
FROM dual;
INSERT INTO VT_LOG_TABLE
(FIELD_0,VAR_FIELD_1,FIELD_2,FIELD_3,FIELD_1, FIELD_4 )
VALUES( :NEW.FIELD_0,VAR_FIELD_1, :NEW.FIELD_2, :NEW.FIELD_3, :NEW.FIELD_1,SYSDATE);
END TRIGGER_TESTE;
What's the right way to make that condition?
As graceemile says, the WHEN clause doesn't understand INSERTING. I'm not sure if you can rely on old.field_1 is null to indicate an insert since it looks like a nullable field (since new.field_1 can apparently be null). If you can't then you could just move the logic into the block:
create or replace trigger vt_trigger
after insert or update on vt_table
for each row
declare
do_logging boolean := false;
begin
if inserting then
do_logging := true;
elsif (:new.field_1 is null and :old.field_1 is not null)
or (:new.field_1 is not null and :old.field_1 is null)
or (:new.field_1 <> :old.field_1)
then
do_logging := true;
end if;
if do_logging then
insert into vt_log_table (field_0, var_field_1, field_2, field_3,
field_1, field_4)
values (:new.field_0, seq_log_table.nextval, :new.field_2, :new.field_3,
:new.field_1, sysdate);
end if;
end vt_trigger;
/
I've changed the check a bit to this:
elsif (:new.field_1 is null and :old.field_1 is not null)
or (:new.field_1 is not null and :old.field_1 is null)
or (:new.field_1 <> :old.field_1)
... to detect if field_1 has changed from null, to null, or from one non-null value to another; maybe you don't want that, the partial check just looked a bit odd to me. I'm assuming you only want to log if field_1 has changed in any way, or if you're inserting a new row of course.
I just tried something similar and Oracle doesn't like the INSERTING value in the WHEN condition. It appears to be OK with NEW and OLD, even if inserting.
From experimentation, it looks like your trigger will fire on INSERTS if you add this to your WHEN condition:
OR OLD.FIELD_1 IS NULL
So try something like this:
create or replace TRIGGER SQLTEST.TRIGGER_TESTE
AFTER INSERT OR UPDATE ON SQLDBA.VT_TABLE
FOR EACH ROW
WHEN (NEW.FIELD_1 is null and OLD.FIELD_1 is not null
or NEW.FIELD_1 <> OLD.FIELD_1
or OLD.FIELD_1 IS NULL)
DECLARE
... and so on
If that gets too complicated, you can create two triggers: one for UPDATE with the WHEN condition and one for INSERT, without conditions.
You could also try defining the trigger as AFTER INSERT OR UPDATE OF Field_1 ON VT_TABLE:
create or replace trigger vt_trigger
after insert or update of field_1 on vt_table
for each row
begin
insert into vt_log_table (field_0, var_field_1, field_2, field_3,
field_1, field_4)
values (:new.field_0, seq_log_table.nextval, :new.field_2, :new.field_3,
:new.field_1, sysdate);
end vt_trigger;
/