Can I use or condition on When into a trigger? - database

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;
/

Related

trying to do multiple inserts in one trigger using field dependent on first insert

I am trying to manipulate a bunch of table triggers that start with an insert into one event table (TB A). This insert fires a trigger (T1) that does an insert into a secondary table (TB B). The secondary table has an insert trigger (T2) that does an update on the first table (TB A).
Pardon the confusion but basically I wanted to ensure that for the first trigger, do a second insert in the same table using the values of the first insert.
BEGIN
SET NOCOUNT ON
declare #Time int
declare #DeleteLinger int
select #Time = convert(integer,value) from Systemproperty
where [name] = 'KeepStoreLingerTimeInMinutes'
select #DeleteLinger = convert(integer,value) from Systemproperty
where [name] = 'KeepDeleteLingerTimeInMinutes'
IF (#DeleteLinger >= #Time) SET #Time=#DeleteLinger+1
insert StorageQueue
(TimeToExecute,Operation,Parameter,RuleID,GFlags)
select DateAdd(mi,#Time,getutcdate()), 1, I.ID, r.ID, r.GFlags
from inserted I, StorageRule r
where r.Active=1 and I.Active=0 and (I.OnlineCount > 0 OR
I.OnlineScreenCount > 0)
-- try and get the value that was just inserted into StorageQueue
select #SFlags=S.GFlags FROM StorageQueue S, StorageRule r, inserted I
WHERE r.ID = S.RuleID and I.ID = S.parameter
-- if a certain value do another insert into StorageQueue
If (#SFlags = 10)
INSERT INTO StorageQueue
(TimeToExecute,Operation,Parameter,RuleID,StoreFlags)
VALUES(DateAdd(mi,#Time,getutcdate()), 1, (SELECT parameter
FROM StorageQueue),2, #SFlags)
END
The problem is that there seems to be either an issue that the record is not yet inserted because the variable #SFlags is null or some other trigger accesses the values and makes changes. My question is whether this is a good way to do it. Is it possible to retrieve a value into the variable from within a trigger because it seems whichever way I try it, it doesnt work.

TSQL trigger instead of insert

I need to replace, after an insert in a certain table, one of the column value if it match with a banned value.
example:
INSERT INTO [dbo].[TEST_A]
([COL_A]
,[COL_B])
VALUES
('TEST'
,'TEST1')
TEST1 is a banned value and I want to replace it in 'BANNED'.
This is the trigger I wrote but it seems to working properly well:
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,REPLACE(COL_B, 'TEST1', 'BANNED')
FROM inserted
WHERE INSERTED.COL_B IN ('TEST1')
The error is that if I insert a different value in COL_B no rows are inserted.
Can you give me a solution?
thanks in advance
If you have more values than you want to put in a case statement, you can try using a table to store the banned words and the replacement for them. Then join to that in the insert, something like:
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,CASE WHEN b.banned is null then i.col_b
ELSE b.replacement
END
FROM inserted i
LEFT JOIN Banned b on i.col_B = b.banned
You need to modify your trigger.
CREATE TRIGGER [dbo].[TEST_NAME_INS] ON [dbo].[TEST_A]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO TEST_A
(COL_A
,COL_B)
SELECT
COL_A
,CASE WHEN COL_B ='TEST1' THEN 'BANNED'
WHEN COL_B ='TEST12' THEN 'BANNED34'
ELSE COL_B END
FROM inserted

How to create a Trigger on table to identify whether a particular record been deleted/updated and store the information of entity updating it?

i need to save information in another temp table say , TableTemp having the records being modified and with one more column defining which entity updated it.
You look like you're just discovering, and ask very wide questions. However, here is a possible solution, assuming the below:
a_sqnc is the sequence you will use in TableTemp to keep track of the order of actions in column NO_ORD (even though there is also a D_UPD column with the modification time).
create sequence a_sqnc
minvalue 1
maxvalue 99999999
start with 1
increment by 1
nocache;
TableTemp will have a TABLE_NAME column in order to track changes from different tables. It also have a PK_VALUE and ROW_VALUE where we store the data that changed. Here is the table creation with useful indexes:
create table TableTemp (
table_name VARCHAR2(50) not null,
action VARCHAR2(240) not null,
no_ord NUMBER(12) not null,
nature VARCHAR2(3) not null,
pk_value VARCHAR2(4000),
row_value VARCHAR2(4000),
ori VARCHAR2(250),
c_user VARCHAR2(20),
d_upd DATE
);
create index AP_D_UPD on TableTemp (D_UPD);
create index AP_NO_ORD on TableTemp (NO_ORD);
create index AP_TABLE_NAME on TableTemp (TABLE_NAME);
Say you have a simple table BANK with two columns PK_val (the primary key) and val:
create table BANK (
pk_val VARCHAR2(50) not null,
val VARCHAR2(240) not null
);
alter table BANK
add constraint BK_PK primary key (pk_val)
using index ;
Use DBMS_APPLICATION_INFO.READ_MODULE(w_sess_mod, w_sess_act) to know what module and what action operates: I concatenate both in column ORI in TableTemp;
user Oracle session variable will allow you tracking who did the change in column c_user;
Here is how to create trigger AUD_BNK to track changes in table BANK; it will categorize in 3 actions: DELETE, UPDATE, INSERT (you can remove the INSERT case if needed).
CREATE OR REPLACE TRIGGER "AUD_BNK"
AFTER DELETE OR INSERT OR UPDATE
ON BANQUE
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
w_a VARCHAR2(10);
W_ERRM VARCHAR2(1000);
W_CODE VARCHAR2(1000);
w_n VARCHAR2(200) := 'BANK';
w_id NUMBER := a_sqnc.nextval;
w_act v$session.action%type;
w_mod v$session.module%type;
w_ori TableTemp.ORI%TYPE;
BEGIN
DBMS_APPLICATION_INFO.READ_MODULE(w_mod, w_act);
w_ori := 'Module : '||w_mod ||' ; Action : '||w_act;
----------------------------------
-- test which action is for change
----------------------------------
IF UPDATING
THEN
w_a := 'UPDATE';
ELSIF DELETING
THEN
w_a := 'DELETE';
ELSIF INSERTING
THEN
w_a := 'INSERT';
END IF;
----------------------------------
-- Insert into TableTemp
----------------------------------
If w_a in ('UPDATE', 'DELETE') then
Insert into TableTemp
Select w_n, w_a, w_id, 'OLD', :OLD.pk_val, :OLD.val
, w_ori, user, sysdate
From Dual;
End if;
-- if you update, there is a new value and an old value
If w_a in ('UPDATE', 'INSERT') then
Insert into TableTemp
Select w_n, w_a, w_id, 'NEW', :NEW.pk_val, :NEW.val
, w_ori, user, sysdate
From Dual;
End if;
Exception
When others then
Begin
W_ERRM := SQLERRM;
W_CODE := SQLCODE;
-- try inserting in case of error anyway
Insert into TableTemp
Select w_n, w_a, -1, 'ERR', 'Grrr: '||W_CODE, W_ERRM
, w_ori, user, sysdate
From Dual;
End;
End;
/
Beware!
This way of tracking every change on the table will deeply impair performances if table changes. But it is great for parameter tables that scarcely change.

MS SQL IF with a select value

How do I state - If a field in my table is NULL only then do the update.
For example:
IF customer_day_phone (from my #invoice table) where id_key = #id_key -matches my parameter - is null. Then run my update.
But i'll have multiple rows coming back and I only want to update those who are NULL.
IF select customer_day_phone from #invoice where id_key = #id_key and customer_day_phone is null
BEGIN
...
END
I need the IF statement I can't simply use where day_phone is NULL because its a two part update. The first part updates the value if the field is null the second update formats the data (but I don't want it formatted if it wasn't updated only if it was).
I dont see any reason why you couldn't simply do TWO PART update in a single update statement.
Simply do the following. Update to the "Formatted" value in your first update and avoid running another update statement, just to update it first and then format it.
UPDATE #invoice
SET columnName = 'value'
WHERE customer_day_phone IS NULL --<-- this will only bring nulls
AND id_key = #id_key
Edit
From your update statement I think it should be as simple as .....
update a
set a.customer_day_phone = ISNULL(b.phone,'') + ' ' + ISNULL(customer_day_phone,'')
from #invoice a
join T_PHONE b on a.customer_no = b.customer_no
where b.[type] = 5
and a.customer_day_phone IS NULL
-- and id_key = #id_key --<-- you had this in your first query too
Let me guess maybe it something like this ?
Update #invoice set <fields which you want to update> WHERE id_key = #id_key and customer_day_phone is null
And at this point you dont need IF Statement
Assuming you want to exclude the record update entirely...
UPDATE invoice SET customer_Day_Phone = #InputValue
WHERE customer_day_Phone is null
and id_key = #Id_Key
or if you need to update other values on the record but not phone..
UPDATE invoice SET customer_Day_Phone = case
when customer_Day_Phone is null then #InputValue
else customer_Day_phone end,
field2=#field2value
WHERE id_key = #Id_Key

Comparing fields in a trigger

So I've created a trigger that compares update before and after and determines if specific fields, specified in the where clause, have changed. If so, I insert a snapshot of the prior information into a history table.
The problem is when the field is created with null values (given the business rules, it is legitimately null) and when an update is made, it is unable to evaluate that the string is not equal to the null value. I want to capture that it was sent to us empty and later filled in.
What approach should I use to compare to null fields without impacting performance?
This is for SQL Server 2005
CREATE TRIGGER [prj].[TRG_Master_Projection_Upd_History] ON [prj].[Master_Projections]
AFTER UPDATE
AS
SET NOCOUNT ON -- Prevents the error that gets thrown after inserting multiple records
-- if multiple records are being updated
BEGIN
INSERT INTO prj.History_Projections (ProjectionID, Cancelled, Appeal, Description, Response_PatternID,
Proj_Mail_date, [3602_Mail_Date], Proj_Qty, [3602_Qty], Proj_Resp_Rate,
Bounce_Back, Nickels, Kits, Oversized_RE, ChangeComments,
Modification_Process, Modification_Date, Modification_User)
SELECT D.ProjectionID, D.Cancelled, D.Appeal, D.Description, D.Response_PatternID, D.Proj_Mail_Date,
D.[3602_Mail_Date], D.Proj_Qty, D.[3602_Qty], D.Proj_Resp_Rate, D.Bounce_Back, D.Nickels, D.Kits,
D.Oversized_RE, D.ChangeComments, D.Modification_Process, D.Modification_Date, D.Modification_User
FROM deleted as D
JOIN inserted as I
ON D.ProjectionID = I.ProjectionID
WHERE (I.Cancelled <> D.Cancelled
OR I.Appeal <> D.Appeal
OR I.Description <> D.Description
OR I.Response_PatternID <> D.Response_PatternID
OR I.Proj_Mail_Date <> D.Proj_Mail_Date
OR I.[3602_Mail_Date] <> D.[3602_Mail_Date]
OR I.Proj_Qty <> D.Proj_Qty
OR I.[3602_Qty] <> D.[3602_Qty]
OR I.Proj_Resp_Rate <> D.Proj_Resp_Rate
OR I.Bounce_Back <> D.Bounce_Back
OR I.Nickels <> D.Nickels
OR I.Kits <> D.Kits
OR I.Oversized_RE <> D.Oversized_RE )
END;
SET NOCOUNT OFF;
Unfortunately, you really have to use sentinel values
ISNULL(I.Response_PatternID, 0) <> ISNULL(D.Response_PatternID, 0)
There is no magic unfortunately: you have to compare every value to see any differences.
Saying that, you only touch the INSERTED and DELETED tables so as bad as this is, the main table is not touched. Unless you have update that affect 10000s if rows, it will run OK.
You can use OR too to but this is cumbersome
(I.Response_PatternID <> D.Response_PatternID OR I.Response_PatternID IS NULL AND I.Response_PatternID IS NOT NULL OR I.Response_PatternID IS NOT NULL AND I.Response_PatternID IS NULL)
I'd stick with ISNULL to avoid subtle datatype issues with COALESCE

Resources