IF UPDATE() in SQL server trigger - sql

If there's:
IF UPDATE (col1)
...in the SQL server trigger on a table, does it return true only if col1 has been changed or been updated?
I have a regular update query like
UPDATE table-name
SET col1 = 'x',
col2 = 'y'
WHERE id = 999
Now what my concern is if the "col1" was 'x' previously then again we updated it to 'x'
would IF UPDATE ("col1") trigger return True or not?
I am facing this problem as my save query is generic for all columns, but when I add this condition it returns True even if it's not changed...So I am concerned what to do in this case if I want to add condition like that?

It returns true if a column was updated. An update means that the query has SET the value of the column. Whether the previous value was the same as the new value is largely irelevant.
UPDATE table SET col = col
it's an update.
UPDATE table SET col = 99
when the col already had value 99 also it's an update.

Within the trigger, you have access to two internal tables that may help. The 'inserted' table includes the new version of each affected row, The 'deleted' table includes the original version of each row. You can compare the values in these tables to see if your field value was actually changed.

Here's a quick way to scan the rows to see if ANY column changed before deciding to run the contents of a trigger. This can be useful for example when you want to write a history record, but you don't want to do it if nothing really changed.
We use this all the time in ETL importing processes where we may re-import data but if nothing really changed in the source file we don't want to create a new history record.
CREATE TRIGGER [dbo].[TR_my_table_create_history]
ON [dbo].[my_table] FOR UPDATE AS
BEGIN
--
-- Insert the old data row if any column data changed
--
INSERT INTO [my_table_history]
SELECT d.*
FROM deleted d
INNER JOIN inserted i ON i.[id] = d.[id]
--
-- Use INTERSECT to see if anything REALLY changed
--
WHERE NOT EXISTS( SELECT i.* INTERSECT SELECT d.* )
END
Note that this particular trigger assumes that your source table (the one triggering the trigger) and the history table have identical column layouts.

What you do is check for different values in the inserted and deleted tables rather than use updated() (Don't forget to account for nulls). Or you could stop doing unneeded updates.

Trigger:
CREATE TRIGGER boo ON status2 FOR UPDATE AS
IF UPDATE (id)
BEGIN
SELECT 'DETECT';
END;
Usage:
UPDATE status2 SET name = 'K' WHERE name= 'T' --no action
UPDATE status2 SET name = 'T' ,id= 8 WHERE name= 'K' --detect

To shortcut the "No actual update" case, you need also check at the beginning whether your query affected any rows at all:
set nocount on; -- this must be the first statement!
if not exists (select 1 from inserted) and not exists (select 1 from deleted)
return;

SET NOCOUNT ON;
declare #countTemp int
select #countTemp = Count (*) from (
select City,PostCode,Street,CountryId,Address1 from Deleted
union
select City,PostCode,Street,CountryId,Address1 from Inserted
) tempTable
IF ( #countTemp > 1 )
Begin
-- Your Code goes Here
End
-- if any of these "City,PostCode,Street,CountryId,Address1" got updated then trigger
-- will work in " IF ( #countTemp > 1 ) " Code)

This worked for me
DECLARE #LongDescDirty bit = 0
Declare #old varchar(4000) = (SELECT LongDescription from deleted)
Declare #new varchar(4000) = (SELECT LongDescription from inserted)
if (#old <> #new)
BEGIN
SET #LongDescDirty = 1
END
Update table
Set LongDescUpdated = #LongDescUpdated
.....

Related

Using IF UPDATE on SQL Trigger when handling multiple inserted/updated records

I use this SQL Server trigger to look for insert/update of multiple records from a specific table and put it into another queue table (for processing later).
ALTER TRIGGER [dbo].[IC_ProductUpdate] ON [dbo].[StockItem]
AFTER INSERT, UPDATE
AS
BEGIN
SELECT RowNum = ROW_NUMBER() OVER(ORDER BY ItemID) , ItemID
INTO #ProductUpdates
FROM INSERTED;
DECLARE #MaxRownum INT;
SET #MaxRownum = (SELECT MAX(RowNum) FROM #ProductUpdates);
DECLARE #Iter INT;
SET #Iter = (SELECT MIN(RowNum) FROM #ProductUpdates);
WHILE #Iter <= #MaxRownum
BEGIN
-- Get Product Id
DECLARE #StockItemID INT = (SELECT ItemID FROM #ProductUpdates WHERE RowNum = #Iter);
-- Proceed If This Product Is Sync-able
IF (dbo.IC_CanSyncProduct(#StockItemID) = 1)
BEGIN
-- Check If There Is A [ProductUpdate] Queue Entry Already Exist For This Product
IF ((SELECT COUNT(*) FROM IC_ProductUpdateQueue WHERE StockItemID = #StockItemID) > 0)
BEGIN
-- Reset [ProductUpdate] Queue Entry
UPDATE IC_ProductUpdateQueue
SET Synced = 0
WHERE StockItemID = #StockItemID
END
ELSE
BEGIN
-- Insert [ProductUpdate] Queue Entry
INSERT INTO IC_ProductUpdateQueue (StockItemID, Synced)
VALUES (#StockItemID, 0)
END
END
SET #Iter = #Iter + 1;
END
DROP TABLE #ProductUpdates;
END
This works fine, however I only want the above trigger to react if certain columns were updated.
The columns I am interested in are:
Name
Description
I know I can use the following T-SQL syntax to check if a column really updated (during update event) like this:
IF (UPDATE(Name) OR UPDATE(Description))
BEGIN
// do something...
END
But, I am not sure how to incorporate this into the above trigger, since my trigger handles multiple rows being updated at same time also.
Any ideas? At which point in the trigger could i use IF (UPDATE(colX))?
First, I would suggest to have one separate trigger for each operation - one for INSERT, and another for UPDATE. Keeps the code cleaner (less messy IF statements and so forth).
The INSERT trigger is pretty simple, since there's nothing to check for updating - and there's absolutely no need for a temporary table and a slow WHILE loop - just two simple, set-based statements and you're done:
CREATE TRIGGER [dbo].[IC_ProductInsert] ON [dbo].[StockItem]
AFTER INSERT
AS
BEGIN
-- update the queue for those entries that already exist
-- those rows that *DO NOT* exist yet are not being touched
UPDATE puq
SET Synced = 0
FROM dbo.IC_ProductUpdateQueue puq
INNER JOIN Inserted i ON puq.StockItemID = i.StockItemID
-- for those rows that don't exist yet - insert the values
INSERT INTO dbo.IC_ProductUpdateQueue (StockItemID, Synced)
SELECT
i.StockItemID, 0
FROM
Inserted i
WHERE
NOT EXISTS (SELECT * FROM dbo.IC_ProductUpdateQueue puq
WHERE puq.StockItemID = i.StockItemID)
END
The UPDATE trigger needs one extra check - to see whether or not one of the two columns of interest has changed. This can be handled quite easily by combining the Inserted pseudo table with the new values (after the UPDATE), and the Deleted pseudo table with the "old" values (before the UPDATE):
ALTER TRIGGER [dbo].[IC_ProductUpdate] ON [dbo].[StockItem]
AFTER UPDATE
AS
BEGIN
-- update the queue for those entries that already exist
-- those rows that *DO NOT* exist yet are not being touched
UPDATE puq
SET Synced = 0
FROM dbo.IC_ProductUpdateQueue puq
INNER JOIN Inserted i ON puq.StockItemID = i.StockItemID
INNER JOIN Deleted d ON d.StockItemID = i.StockItemID
WHERE
i.Name <> d.Name OR i.Description <> d.Description
-- for those rows that don't exist yet - insert the values
INSERT INTO dbo.IC_ProductUpdateQueue (StockItemID, Synced)
SELECT
i.StockItemID, 0
FROM
Inserted i
INNER JOIN
Deleted d ON d.StockItemID = i.StockItemID
WHERE
i.Name <> d.Name OR i.Description <> d.Description
AND NOT EXISTS (SELECT * FROM dbo.IC_ProductUpdateQueue puq
WHERE puq.StockItemID = i.StockItemID)
END
You can join to deleted and use where I.Name <> D.Name...
https://www.mssqltips.com/sqlservertip/2342/understanding-sql-server-inserted-and-deleted-tables-for-dml-triggers/

SQL Trigger update another table

I am newbie to triggers... can anybody help me with a trigger?
I have Table:
Name | Number
I want to write a trigger when my table receives a query like
update MyTable
set Number = Number + 1
where Name = 'myname'
When this query is running, the trigger should update another table for example:
Update MyTable 2
set Column = 'something'
where Name = 'myname (above name)
Thank you very much !
You will need to write an UPDATE trigger on table 1, to update table 2 accordingly.
Be aware: triggers in SQL Server are not called once per row that gets updated - they're called once per statement, and the internal "pseudo" tables Inserted and Deleted will contain multiple rows, so you need to take that into account when writing your trigger.
In your case, I'd write something like:
-- UPDATE trigger on "dbo.Table1"
CREATE TRIGGER Table1Updated
ON dbo.table1 FOR UPDATE
AS
BEGIN
-- update table2, using the same rows as were updated in table1
UPDATE t2
SET t2.Column = 'something'
FROM dbo.Table2 t2
INNER JOIN Inserted i ON t2.ID = i.ID
END
GO
The trick is to use the Inserted pseudo table (which contains the new values after the UPDATE - it has the exact same structure as your table the trigger is written for - here dbo.Table1) in a set-based fashion - join that to your dbo.Table2 on some column that they have in common (an ID or something).
create a trigger on table 1 for update:
CREATE TRIGGER dbo.update_trigger
ON table1
AFTER UPDATE
AS
BEGIN
DECLARE #Name VARCHAR(50)
SELECT #Name=Name FROM INSERTED
Update MyTable 2
SET Column = 'something'
WHERE Name = #Name
END
GO
try this ;)

How to create history using trigger, column name, before and after value, who modified, date and time?

I have a two tables one is main and other one is for history, having the same schema with different records apart from the unique one.
I want to create a query which can tell me which column was updated and what the before and after values along with who has updated and what time.
Please see below. Can anyone help me to get this done using SQL?
UniqueID Field Modified Before Value After Value updatedby: Change Date
111 Company Name Exxon Mobile ExxonMobileTest Dev 1/13/2014
122 Account Category Focused Pursuit Jeff 1/13/2014
Make and audit table that mirrors the one you want to log, then every new row you insert into the audit signifies a change in data, use this trigger:
CREATE TRIGGER [dbo].[mytrigger]
ON [dbo].[mytable]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #ERR_TXT char(255),#ER int,#RC int
if (select COUNT(*) from inserted) <> 0 -- validate the new data
begin
-- do stuff to check new data if needed
end
if (select count(*) from inserted) = (select count(*) from deleted) -- update
begin
INSERT INTO [DB].[dbo].[myAudit]
... (all columns)
SELECT ... (all columns)
from inserted
end
if (select count(*) from inserted) = 0 and (select count(*) from deleted) <> 0 -- delete
begin
INSERT INTO [DB].[dbo].[myAudit]
... (all columns)
SELECT ... (all columns)
from deleted
end
if (select count(*) from inserted) <> 0 and (select count(*) from deleted) = 0 -- insert
begin
INSERT INTO [DB].[dbo].[myAudit]
... (all columns)
SELECT ... (all columns)
from inserted
end
END
This works well for me. You can use some of the server functions to see who did the transactions, like SUSER_ID() & GETDATE().

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

Whats Select '1' for in the following stored proceedure

BEGIN
IF EXISTS(SELECT * FROM Table1 WHERE ID=#ID)
BEGIN
UPDATE Table1 SET Name=#Name WHERE ID=#ID
SELECT '1'
END
ELSE
SELECT '0'
END
Is this the row no. of the table or what ?
Also "IF EXISTS" is checking what ? the table or if the ID exists or not ??
It looks like whoever wrote that Stored Procedure is using that as a return value to indicate success or failure.
Doing things that way will result in a single row with a single column being returned for each call to the procedure.
The correct way to handle this would be to actually use the return value of the stored procedure, rather than returning the single column single row:
BEGIN
IF EXISTS(SELECT * FORM Table1 WHERE ID = #ID)
BEGIN
UPDATE Table1 SET Name = #Name WHERE ID = #ID
RETURN 1
END
RETURN 0
END
The IF EXISTS is checking if there is a row in Table1 with the given ID. If there is a row it will update that row with the given name. The Select "1" will return "1" and Select "0" returns "0". The "1" or "0" would indicate if the row was found or not.
Presumably some calling code checks this value to determine if a row was updated or not.
Rather than checking and updating (two table accesses) you might as well do this.
UPDATE Table1 SET Name=#Name WHERE ID=#ID
SELECT CASE WHEN ##Rowcount = 0 THEN 0 ELSE 1 END
If id is the PK then you can just do
UPDATE Table1 SET Name=#Name WHERE ID=#ID
SELECT ##Rowcount
Note as long as SET NOCOUNT is not on then the number of rows affected will get passed back to the client application anyway.
Select '1' is used to indicate that Table1 contains the id value #ID (a parameter) was updated. Select '0' indicates that Table1 does not contain the id value #ID.