How to use multiple triggers? - sql

DROP TRIGGER IF EXISTS N2Trigger
CREATE TRIGGER N2Trigger
ON dbo.Date
FOR INSERT, DELETE
AS
BEGIN
SELECT 'Inserted Datebase' as MESSAGE
SELECT 'Deleted Database' as MESSAGE
END
DELETE FROM dbo.[Date] WHERE ID = 1
Here is my code I just want when I use insert statement return 'Inserted Datebase' as MESSAGE
When I use delete statement return 'Deleted Database' as MESSAGE

The easiest way to check what action fired the trigger is to inspect the inserted and deleted pseudo-tables. If the trigger is only on DELETE/INSERT and not on update, then the logic is simply:
CREATE TRIGGER dbo.trFlarb ON dbo.flarb
FOR INSERT, DELETE
AS
BEGIN
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
SELECT 'Inserted.';
END
IF EXISTS (SELECT 1 FROM deleted)
BEGIN
SELECT 'Deleted.';
END
END
Example db<>fiddle
Now, of course, Marc is right: triggers aren't for returning or printing output. This is just a demonstration that you can use those checks to then perform whatever logic you need to perform in the event of either action.
That said, if you have two distinctly different things you want to do depending on whether it's an insert or a delete, why not just create two separate triggers?
CREATE TRIGGER dbo.tr_I_Flarb ON dbo.flarb
FOR INSERT
AS
BEGIN
SELECT 'Inserted.';
END
GO
CREATE TRIGGER dbo.tr_D_Flarb ON dbo.flarb
FOR DELETE
AS
BEGIN
SELECT 'Deleted.';
END
GO
Note that SELECT will only "work" on your system if you haven't turned on the disallow results from triggers Server Configuration Option. Again, you should try to explain what you really want to do in the event of an insert or update, because the end goal can't be to print or return "Inserted" or "Deleted."

Related

Sql Oracle 12c Trigger

I need some help.
I'm trying to create a Trigger that execute a procedure whenever insert, delete or update operations are done on a specific table.
This is the trigger
CREATE OR REPLACE NONEDITIONABLE TRIGGER NQDI.GAV_TRG
AFTER INSERT or UPDATE or DELETE ON D_GAV
FOR EACH ROW
BEGIN
PRC_FILL_D_GAV(:old.report_name);
END;
Unofortunately, since the trigger starts before any commit has been done and I need to read from the same table, it gives me the 'D_GAV table is being modified can't be read' error.
Besides, the FOR EACH ROW makes the trigger start for every record changed, while I want the trigger to start only at the end, when every update, insert or delete has been committed, but I haven't find a way to preserve the :old.report_name while doing this.
I know I could do what I want with an "up and running process", but I'd like to avoid that. Is there any other solution that I'm overlooking?
You want a compound trigger. After each row event you insert the data into an array. And after the statement you loop through the data and call your procedure.
create or replace trigger nqdi.gav_trg
for insert or update or delete on d_gav compound trigger
type type_table_of_gav is table of d_gav%rowtype;
v_gavs type_table_of_gav := type_table_of_gav();
after each row is
begin
v_gavs.extend(1);
v_gavs(v_gavs.count).report_name := coalesce(:old.report_name, :new.report_name);
end after each row;
after statement is
begin
for i in 1 .. v_gavs.count loop
prc_fill_d_gav(v_gavs(i).report_name);
end loop;
end after statement;
end gav_trg;

Disable Trigger for a particular DELETE Query

I have a ruby app. The app is doing the insert,update and delete on a particular table.
It does 2 kinds of INSERT, one insert should insert a record in the table and also into trigger_logs table. Another insert is just to insert the record into the table and do nothing. Another way to put it is, one kind of insert should log that the 'insert' happened into another table and another kind of insert should just be a normal insert. Similarly, there are 2 kinds of UPDATE and DELETE also.
I have achieved the 2 types of INSERT and UPDATE using a trigger_disable. Please refer to the trigger code below.
So, when I do a INSERT, I will set the trigger_disable boolean to true if I don't want to log the trigger. Similarly I am doing for an UPDATE too.
But I am not able to differentiate between the 2 kinds of DELETE as I do for an INSERT or UPDATE. The DELETE action is logged for both kinds of DELETE.
NOTE: I am logging all the changes that are made under a certain condition, which will be determined by the ruby app. If the condition is not satisfied, I just need to do a normal INSERT, UPDATE or DELETE accordingly.
CREATE OR REPLACE FUNCTION notify_#{#table_name}()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
changed_row_id varchar(100);
BEGIN
IF TG_OP = 'DELETE' THEN
-- When the trigger is due to a delete
IF (OLD.trigger_disable IS NULL)
OR (OLD.trigger_disable = false) THEN
-- Prevent the trigger if trigger_disable is 'true'
-- The Problem is here: This insertion into the
-- trigger_logs table happens
-- for all the delete statements.
-- But during certain deletes I should not
-- insert into trigger_logs
INSERT INTO trigger_logs (table_name, action, row_id, dirty)
VALUES (
'#{#table_name}',
CAST(TG_OP AS Text),
OLD.id,
true
) RETURNING id into changed_row_id;
END IF;
RETURN OLD;
ELSE
-- The trigger is due to a Insert or Update
IF (NEW.trigger_disable IS NULL)
OR (NEW.trigger_disable = false) THEN
-- Prevent the trigger if trigger_disable is 'true'
INSERT INTO trigger_logs (table_name, action, row_id, dirty)
VALUES (
'#{#table_name}',
CAST(TG_OP AS Text),
NEW.id,
true
) RETURNING id into changed_row_id;
ELSE
NEW.trigger_disable := false;
END IF;
RETURN NEW;
END IF;
END
I'm going to take a stab in the dark here and guess that you're trying to contextually control whether triggers get fired.
If so, perhaps you can use a session variable?
BEGIN;
SET LOCAL myapp.fire_trigger = 'false';
DELETE FROM ...;
COMMIT;
and in your trigger, test it:
IF current_setting('myapp.fire_trigger') = 'true' THEN
Note, however, that if the setting is missing from a session you won't get NULL, you'll get an error:
regress=> SELECT current_setting('myapp.xx');
ERROR: unrecognized configuration parameter "myapp.xx"
so you'll want to:
ALTER DATABASE mydb SET myapp.fire_trigger = 'true';
Also note that the parameter is text not boolean.
Finally, there's no security on session variables. So it's not useful for security audit, since anybody can come along and just SET myapp.fire_trigger = 'false'.
(If this doesn't meet your needs, you might want to re-think whether you should be doing this with triggers at all, rather than at the application level).

BEFORE DELETE trigger

How to stop from deleting a row, that has PK in another table (without FK) with a trigger?
Is CALL cannot_delete_error would stop from deleting?
This is what I've got so far.
CREATE TRIGGER T1
BEFORE DELETE ON Clients
FOR EACH ROW
BEGIN
SELECT Client, Ref FROM Clients K, Invoice F
IF F.Client = K.Ref
CALL cannot_delete_error
END IF;
END
Use an 'INSTEAD OF DELETE' trigger.
Basically, you can evaluate whether or not you should the delete the item. In the trigger you can ultimately decide to delete the item like:
--test to see if you actually should delete it.
--if you do decide to delete it
DELETE FROM MyTable
WHERE ID IN (SELECT ID FROM deleted)
One side note, remember that the 'deleted' table may be for several rows.
Another side note, try to do this outside of the db if possible! Or with a preceding query. Triggers are downright difficult to maintain. A simple query, or function (e.g. dbo.udf_CanIDeleteThis()') can be much more versatile.
If you're using MySQL 5.5 or up you can use SIGNAL
DELIMITER //
CREATE TRIGGER tg_fk_check
BEFORE DELETE ON clients
FOR EACH ROW
BEGIN
IF EXISTS(SELECT *
FROM invoices
WHERE client_id = OLD.client_id) THEN
SIGNAL sqlstate '45000'
SET message_text = 'Cannot delete a parent row: child rows exist';
END IF;
END//
DELIMITER ;
Here is SQLFiddle demo. Uncomment the last delete and click Build Schema to see it in action.

firebird - after insert or update trigger

I have a calculation in my DB need to update "field1" for "table1" after the update trigger.
The problem that updating that field will cause the after update trigger to fire and execute a lengthy procedure and display errors.
please advise how to update the "field1" after the "After update" trigger has been executed and without making the "after update" trigger to execute again.
I know that I can not use NEW with After trigger.
Thanks
One can use a custom locking mechanism based on context variables which prevent from repeating invocation of AFTER UPDATE trigger.
CREATE TRIGGER au FOR table
AFTER UPDATE
POSITION 0
AS
BEGIN
IF RDB$GET_CONTEXT('USER_TRANSACTION', 'MY_LOCK') IS NULL THEN
BEGIN
RDB$SET_CONTEXT('USER_TRANSACTION', 'MY_LOCK', 1);
...
Do your update operations here
...
RDB$SET_CONTEXT('USER_TRANSACTION', 'MY_LOCK', NULL);
END
WHEN ANY DO
BEGIN
RDB$SET_CONTEXT('USER_TRANSACTION', 'MY_LOCK', NULL);
EXCEPTION;
END
END
The obvious answer is to switch to the BEFORE UPDATE trigger, as pointed out by J Cooper... however, if there is some reason you absolutely have to use AFTER UPDATE then you have to set up a flag which tells that the field needs to be recalculated and check it in your trigger. One way to do it would be to set the field to NULL in BEFORE trigger and then check against NULL in AFTER trigger, ie
CREATE TRIGGER bu_trigger BEFORE UPDATE
BEGIN
-- mark field for recalculation if needed
IF(update invalidates the field1 value)THEN
NEW.field1 = NULL;
END
CREATE TRIGGER au_trigger AFTER UPDATE
BEGIN
-- only update if the field is NULL
IF(NEW.field1 IS NULL)THEN
UPDATE table1 SET field1 = ...;
END
But using this technique you probably have to use lot of IF(OLD.field IS DISTINCT FROM NEW.field)THEN checks in triggers to avoid unnessesary updates in your triggers.
simple solution...
fire the update only if the NEW.FIELD1 value is really new, like this:
CREATE TRIGGER au FOR table1
AFTER UPDATE
POSITION 0
AS
DECLARE VARIABLE TMP AS NUMBER(15,5); -- SAME DATATYPE OF FIELD1
BEGIN
-- MAKE YOUR CALCULATION
TMP=CALCULATEDVALUE;
-- UPDATE THE ROW ONLY IF THE VALUES IS CHANGED
IF (TMP<>NEW.FIELD1) UPDATE TABLE1 SET FIELD1=:TMP WHERE KEY=NEW.KEY; -- PAY ATTENTION IF TMP OR NEW.FIELD1 CAN BE NULL. IN THIS CASE USE COALESCE OR A DIFFERENCE COMPARISON
END
Solution: Use BEFORE UPDATE TRIGGER instead of AFTER UPDATE TRIGGER
CREATE TRIGGER some_trigger BEFORE UPDATE ... etc

Oracle Trigger on Insert or Delete or Update

Trying to create an Oracle trigger that runs after a table is updated in any way. I've been googling this all morning and came up with this:
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
delete from gb_transaction where gb_tide = :new.ITST_ITEM_TIDE_CODE;
select TD_IDENTIFIER INTO v_isbn from TD_ITEM_DESCRIPTION where TD_TIDE = :new.ITST_ITEM_TIDE_CODE;
select * INTO v_qty from V_AD_ON_HAND where ITST_ITEM_TIDE_CODE = :new.ITST_ITEM_TIDE_CODE;
insert into gb_transaction(gb_tide, gb_isbn, gb_used_on_hand, gb_new_on_hand)
values(:new.ITST_ITEM_TIDE_CODE, v_isbn, v_qty.USED_ON_HAND, v_qty.NEW_ON_HAND);
END;
/
I'm trying to keep it to a single record per TIDE_CODE in the new table.
V_AD_ON_HAND is a view that pulls an inventory count.
gb_transaction is my new table where I'm logging these events
Comparing it to other peoples code it looks like it should run but I'm getting "Warning: Trigger created with compilation errors."
The problem, I believe is with the :new designations for a delete trigger. There is, after all, no NEW value as the record is expunged. You can only access the :OLD values on delete.
if you section the trigger by operation, you can do this.
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
IF INSERTING or UPDATING then
... insert your existing code
ELSE
... do something similar with the :old values for the deleting case
end if;
END;
/
Incidentally, it is generally helpfull if you tell us WHAT the error is, not just that you had one. If compiling via SQL*Plus script, after the forward slash call to compile the trigger after the "end;" statement, add a line that says:
SHOW ERRORS TRIGGER YOUR_TRIGGER_NAME;