Enable trigger after another trigger is fired - sql

I am new to triggers and I am in a position where I have to use them. I have an azure db with two triggers on a table, one on insert, one on update.
Insert: fires when a record is inserted to a table. Copies one column to another:
CREATE TRIGGER [dbo].[tr_Set_Adjusted_StartDateTime]
ON [dbo].[Work]
AFTER INSERT
AS
BEGIN
UPDATE dbo.Work
SET [ActualStartDateTime] = [work].[StartDateTime]
FROM inserted
WHERE dbo.Work.WorkUID = inserted.WorkUID;
END
Update trigger (fires when the record gets updated):
CREATE TRIGGER [dbo].[tr_Set_Actual_EndDateTime]
ON [dbo].[Work]
AFTER UPDATE
AS
IF((Select [ActualEndDateTime] from Deleted) is null)
BEGIN
UPDATE dbo.Work
SET [ActualEndDateTime] = GETUTCDATE()
FROM deleted
WHERE dbo.Work.WorkUID = deleted.WorkUID;
END
The second trigger should only execute once: the first time the record is updated. Because the stored procedure that inserts the record doesn't populate all the columns.
The second trigger didn't originally have the IF statement. But there is an admin site that can manipulate the db and set off the update trigger.
The IF statement is now automatically firing the update trigger right away.
Is there a way to disable the update trigger if it is executed BY another trigger? Or only enable the update trigger after the record has been created?

If you have an After Update trigger on a table, it will fire each time there is an update on that table. You cannot tell trigger to Only Fire once.
But there is a way around to it. You can add a field in that table , a BIT field and SET its value to 1 in your trigger, Never manipulate this field in directly. And inside your UPDATE trigger
UPDATE w
SET [ActualEndDateTime] = GETUTCDATE()
,[Tr_Update] = 1
FROM deleted d INNER JOIN dbo.Work w ON w.WorkUID = d.WorkUID
WHERE [Tr_Update] = 0
AND [ActualEndDateTime] IS NULL
On a side note you are checking if the user hasn't put any date you want to add Current Datetime to [ActualEndDateTime] column. and since this is an after update trigger if you just execute the above statement with WHERE clause [ActualEndDateTime] IS NULL,
It would update [ActualEndDateTime] to current datetime when a row is updated for the first time and next time because [ActualEndDateTime] field would not be null it would simply filter it out anyway.
UPDATE w
SET [ActualEndDateTime] = GETUTCDATE()
FROM deleted d INNER JOIN dbo.Work w ON w.WorkUID = d.WorkUID
WHERE [ActualEndDateTime] IS NULL

Related

Creating a trigger to update multiple records after insert sql server 2008

Well basically I need this trigger to work after a user inserts multiple records into the database. So that when an optionID of 0 is inserted and the IsoptionalID = 1, then set the OptionID = NULL
CREATE TRIGGER ThisDatabase
ON OtherTable
AFTER INSERT
AS
BEGIN
DECLARE #OPTIONID INT
SET #OPTIONID = OtherTable.OPTIONID
DECLARE #ISoptional INT
SET #ISoptional = OtherTable.ISoptional
CASE #optionID WHEN 0 and #ISoptional = 1 set update OtherTable set optionid = null end
END
I am not sure about the case itself either.
Thank you in advance
This depends on the key field(s) of the table, but SQL Server triggers always work on the entire data set being modified (Inserted, Updated, or Deleted). So the trigger would something more like:
CREATE TRIGGER ThisDatabase
ON OtherTable
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE ot
SET ot.OptionID = NULL
FROM OtherTable ot
INNER JOIN INSERTED ins
ON ins.KeyField = ot.KeyField
WHERE ins.OptionID = 0
AND ins.IsOptional = 1
END
The INSERTED table has the rows that were either Inserted or Updated (current version).
The DELETED table has the rows that were either Deleted or Updated (old version).
So, the INSERTED and DELETED tables are pre-filtered to only the changed records, but they are not updatable (since the event already happened due to this being an AFTER trigger and SQL Server not having a BEFORE trigger) so you need to do the UPDATE on the real table.
It isn't really clear what you want to do, but here's a skeleton. Just note:
Triggers are created on the table which is being affected (not an Other table)
You can certainly update another table as a consequence of a trigger. This is typically done through a join.
Use the inserted and deleted pseudo-tables to identify the record(s) which have been inserted, updated or deleted.
CREATE TRIGGER TR_TableBeingInsertedInto
ON TableBeingInsertedInto
AFTER INSERT
AS
BEGIN
UPDATE OtherTable
-- What you actually want to do here isn't clear to me
SET OtherTable.OPTIONID =
CASE i.OptionID
WHEN 0 THEN NULL
ELSE OtherTable.OPTIONID
END
FROM OtherTable
-- Inserted has the same schema as TableBeingInsertedInto
INNER JOIN INSERTED i
ON OtherTable.SomeCommonKey = i.SomeCommonKey;
END

SQL Server Trigger not triggered after insert and update

I want to copy the contents of a column into another column in the same table. Therefore, I created this trigger:
ALTER TRIGGER [dbo].[kennscht_copy_to_prodverpt]
ON [dbo].[Stammdaten]
AFTER INSERT
AS
UPDATE Stammdaten
SET PRODVERPT = (SELECT KENNSCHT FROM INSERTED)
WHERE SNR = (SELECT SNR FROM INSERTED);
But when I use an UPDATE on the table to update KENNSCHT to a different value, PRODVERPT is not updated as well. Now, you could argue that is because the trigger is on AFTER INSERT and not AFTER UPDATE, but when I change it so it's triggered by UPDATE and INSERT, whenever I update any row, I get an error message from SQL Server
Cannot update row because it would make the row not unique or update multiple rows (2 rows) [sic]
What is going on here? Either the trigger doesn't do anything, or it's messing up the whole table, making it un-updateable.
Update: I also tried the following trigger:
UPDATE s
SET s.PRODVERPT = i.KENNSCHT
FROM Stammdaten s
INNER JOIN INSERTED i ON i.SNR = s.SNR;
But it has exactly the same behaviour. If I use only AFTER INSERT, nothing changes and if I use AFTER INSERT, UPDATE I get the error message above.
Update 2: There are no multiple rows in the table, I already checked that because I thought it might be connected to the issue.
If you run this trigger as an AFTER UPDATE trigger, it runs recursively, since it always issues another UPDATE statement against the same table, which leads to another execution of the trigger.
To work around this, you either need to make the update trigger an INSTEAD OF UPDATE trigger or test if the KENNSCHT column was modified at all. For the latter you can use the UPDATE() function like this:
ALTER TRIGGER [dbo].[kennscht_copy_to_prodverpt_after_update]
ON [dbo].[Stammdaten]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
IF (UPDATE(KENNSCHT))
BEGIN
UPDATE s
SET s.PRODVERPT = i.KENNSCHT
FROM Stammdaten s
INNER JOIN INSERTED i ON i.SNR = s.SNR
END
END

SQL Server 2008 created/last updated trigger issue

I am using this trigger in order to fill in the CreatedDate and LastUpdated columns (of type datetime) in a table:
CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT
AS
UPDATE [LasMTest]
SET [LasMTest].Created = getdate(),
[LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID]
GO
When I check the table, the dates are off by just a fraction of a second.
Created LastModified ID
2013-03-19 09:24:32.920 2013-03-19 09:24:32.930 4
2013-03-19 09:26:39.890 2013-03-19 09:26:39.900 5
How can I modify the trigger so that they are both the exact time?
It's the interaction of your two triggers that's causing the problem.
If, instead, you set both columns to default to getdate() and ditch your insert trigger, it should work - the INSERT won't also cause an UPDATE.
The alternative is to author your INSERT trigger as an INSTEAD OF trigger that performs an INSERT rather than an UPDATE (and thus, also, avoids the UPDATE trigger firing).
If you do want to write it as an INSTEAD OF trigger, it would be something like:
CREATE TRIGGER trCreatedDate ON [LasMTest]
INSTEAD OF INSERT
AS
INSERT INTO LasMTest (/* Column List */,Created,LastModified)
SELECT /* Column List */,getdate(),getdate() from inserted
GO
INSERTs into the triggering table in an INSTEAD OF trigger don't (thankfully) cause the trigger to be fired recursively. If ID is an IDENTITY column, then it should appear in the column lists above (it hasn't been generated yet).
Try this:
CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT
AS
Declare #CurrentDate Datetime
Set #CurrentDate=getdate()
UPDATE [LasMTest] SET [LasMTest].Created=#CurrentDate,[LasMTest].LastModified=#CurrentDate
FROM [LasMTest] INNER JOIN Inserted ON [LasMTest].[ID]= Inserted.[ID]
GO
You basically want to avoid the UPDATE trigger when doing the update from INSERT. This is called Nested Triggers. One easy solution is to use CONTEXT_INFO() to communicate to the nested UPDATE trigger code that you are already in an INSERT trigger so it suppresses itself:
CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT
AS
SET CONTEXT_INFO 0xDEADBEEF;
UPDATE [LasMTest]
SET [LasMTest].Created = getdate(),
[LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
SET CONTEXT_INFO NULL;
GO
CREATE TRIGGER trModifiedDate ON [LasMTest]
FOR UPDATE
AS
DECLARE #context varbinary(128);
SET #context = CONTEXT_INFO();
IF #context is NULL
BEGIN
UPDATE [LasMTest]
SET [LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
END
GO
However such an approach is fragile. An exception can leave the context_info set and suppress all subsequent UPDATE triggers in the session. This requires adding TRY/CATCH blocks. And you always run the risk of an application using CONTEXT_INFO for its own purposes and ruining your scheme.
Another solution is to make the UPDATE trigger smart. It can check the UPDATE(Created) inside the UPDATE trigger and suppress any action if the Created column was modified. This works by convention, because you know that the only place that updates the Created column is the INSERT trigger:
CREATE TRIGGER trModifiedDate ON [LasMTest]
FOR UPDATE
AS
IF NOT UPDATE(Created)
BEGIN
UPDATE [LasMTest]
SET [LasMTest].LastModified = getdate()
FROM [LasMTest]
INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
END
GO

Mixing update/insert records in a single invocation of "Instead of insert, update" - Is that possible?

I would like to know whether an "instead of insert, update" trigger can be called to both "UPDATE" A records in the table while "INSERTING" B records in a single invocation?
Actually, I'm just curious to know whether SQL Server could call the mentioned trigger for updating and inserting records, at once? or it just calls the trigger once for updating some records, and once for inserting some others?
Consider this, for example:
ALTER TRIGGER [dbo].[MyTable_DoWhatever]
ON [dbo].[MyTable]
INSTEAD OF INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Updating BIT = 0;
IF EXISTS(SELECT * FROM INSERTED i INNER JOIN DELETED d ON i.Id = d.Id) SET #Updating = 1
END
Does the above #Updating flag valid to determine whether the trigger is called for "INSTEAD OF INSERTED" or "INSTEAD OF UPDATE"?
Thanks.

Prevent trigger from firing

I have the following trigger
First trigger:
ALTER TRIGGER [dbo].[DIENSTLEISTUNG_Update]
ON [dbo].[DIENSTLEISTUNG]
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #intNewID int
INSERT INTO [DIENSTLEISTUNG]
(DESCRIPTION, QUANTITY,
PRICE, AZ_MO, AZ_DI,AZ_MI,AZ_DO,AZ_FR,
AZ_SA,AZ_SO,DIENSTLEISTUNGSART_ID,
UPDATE_USER, UPDATE_DATE,
PERMISSIONS, KONTRAKTPOSITION,ITEMNUMBER,
PRIORITY, VALID)
SELECT i.DESCRIPTION, i.QUANTITY, i.PRICE, i.AZ_MO,
i.AZ_DI,i.AZ_MI,i.AZ_DO,i.AZ_FR,
i.AZ_SA,i.AZ_SO,i.SERVICETYPE_ID, i.UPDATE_USER,GETDATE(),
i.PERMISSIONS, i.KONTRAKTPOSITION,i.ITEMNUMBER, i.PRIORITY, 'Y'
FROM INSERTED i
JOIN deleted d ON i.ID=d.ID
WHERE i.PRICE<>d.PRICE
or i.DESCRIPTION<>d.DESCRIPTION
IF ( UPDATE (PRICE) OR UPDATE (DESCRIPTION) )
UPDATE S
SET s.VALID = 'N'
FROM SERVICE s
JOIN INSERTED i ON I.ID = S.ID
IF UPDATE(PRIORITY)
UPDATE s
SET s.PRIORITY= i.PRIORITY
FROM SERVICE s
JOIN INSERTED i ON i.ID = s.ID
SET NOCOUNT OFF;
END
The first Trigger copies an entire row with a new ID if a change in the original row happens, also the trigger set a flag. The old row gets the flag VALID = 'N' and the new row gets the flag VALID = 'Y'. The trigger only creates a new row if PRICE or DESCRIPTION are updated. So far so good.
My problem is that if I want to update the PRIORITY in the new row the trigger fires again and sets the flag to VALID = 'N'. That should not happen. I want only to update the priority without creating a new row or update a another column.
Thanks for help
You cannot prevent a trigger from firing - if it's present and not disabled, it will fire. That's how triggers work.
What you can do is check inside your trigger which columns have been updated. So you could do something like this in your one single trigger:
CREATE TRIGGER [dbo].[DIENSTLEISTUNG_Update]
ON [dbo].[DIENSTLEISTUNG]
FOR UPDATE
AS
IF UPDATE(PRICE)
... (do what you need to do if PRICE is updated)...
IF UPDATE(DESCRIPTION)
... (do what you need to do if DESCRIPTION is updated)...
IF UPDATE(PRIORITY)
... (do what you need to do if PRIORITY is updated)...
Use the UPDATE() function to check whether a given column has been updated - and if so, act on it. See the MSDN docs on how to use the UPDATE() function.
You can make triggers fire only on certain columns or one colomn.
like this.
CREATE TRIGGER tr_something ON myTable
FOR INSERT, UPDATE
AS
IF UPDATE(myColumn)
BEGIN
-- do what you want
END
post below gives more details, seems I'm to slow :)
What you can do is set the context info of the session which you are in like this:
SET Context_Info 0x55555
And then in your trigger check for the context info to decide what to do:
DECLARE #Cinfo VARBINARY(128)
SELECT #Cinfo = Context_Info()
IF #Cinfo = 0x55555
RETURN