SQL Server Update Trigger, Get Only modified fields
Inside the trigger, you can use COLUMNS_UPDATED()
like this in order to get updated value
-- Get the table id of the trigger
--
DECLARE @idTable INT
SELECT @idTable = T.id
FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id
WHERE P.id = @@procid
-- Get COLUMNS_UPDATED if update
--
DECLARE @Columns_Updated VARCHAR(50)
SELECT @Columns_Updated = ISNULL(@Columns_Updated + ', ', '') + name
FROM syscolumns
WHERE id = @idTable
AND CONVERT(VARBINARY,REVERSE(COLUMNS_UPDATED())) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0
But this snipet of code fails when you have a table with more than 62 columns.. Arth.Overflow...
Here is the final version which handles more than 62 columns but give only the number of the updated columns. It's easy to link with 'syscolumns' to get the name
DECLARE @Columns_Updated VARCHAR(100)
SET @Columns_Updated = ''
DECLARE @maxByteCU INT
DECLARE @curByteCU INT
SELECT @maxByteCU = DATALENGTH(COLUMNS_UPDATED()),
@curByteCU = 1
WHILE @curByteCU <= @maxByteCU BEGIN
DECLARE @cByte INT
SET @cByte = SUBSTRING(COLUMNS_UPDATED(), @curByteCU, 1)
DECLARE @curBit INT
DECLARE @maxBit INT
SELECT @curBit = 1,
@maxBit = 8
WHILE @curBit <= @maxBit BEGIN
IF CONVERT(BIT, @cByte & POWER(2,@curBit - 1)) <> 0
SET @Columns_Updated = @Columns_Updated + '[' + CONVERT(VARCHAR, 8 * (@curByteCU - 1) + @curBit) + ']'
SET @curBit = @curBit + 1
END
SET @curByteCU = @curByteCU + 1
END
Find modified fields in Update Trigger
Unfortunately I'm not aware of any reliable built-in function that can give you an indication if the value of a column actually changed when an update statement fired a trigger.
However, there is pretty simple select statement you can execute to see what values have changed in what rows:
SELECT IIF(ISNULL(NULLIF(I.Col, D.Col), NULLIF(D.Col, I.Col)) IS NULL, 0, 1) As Col_Updated
FROM Inserted I
JOIN Deleted D
ON I.PrimaryKey = D.PrimaryKey
The NULLIF
function will return null
if both columns are equal. If not, it returns the value of the first column.
The ISNULL
function will return the first argument that is not null
, or null
if both arguments are null.
Using ISNULL
on the result of two NULLIF
functions where the column values are reversed will result in null
if the values of both columns are the same, even if they both are null. If the values are different, even if one of them is null and the other is not, then ISNULL
will return a value. Therefor, all you have to do is check if the ISNULL
returned a value or null - if it return null
, you know the column's value was not changed. If it returns a value, then it is changed.
Of course, for non-nullable columns you can simplify the condition like this:
SELECT IIF(I.Col <> D.Col, 1, 0) As Col_Updated
FROM...
However I would recommend against that, since if someone will change the column to allow null, the trigger code will have to change to support that - and in my experience, that's a bug waiting to happen - trigger code will probably not be changed, leading to false-negatives.
You can see a live demo on rextester.
Update
Another simple method you can use is this:
IIF((I.Col IS NULL AND D.Col IS NULL) OR (I.Col = D.Col), 0, 1) as IsUpdated.
This method relies on the fact that comparing null to anything else will result with unknown
which is basically equivalent to false
.
It's slightly more readable IMHO.
How to get names of the 'only updated columns' in a trigger?
Personally I hate triggers and try to avoid them most of the time ;-)
However, the only way I know of is with the function COLUMNS_UPDATED() inside your trigger code. See also examples like: https://sites.google.com/site/bugsarewelcome/home/columns_updated
SQL update trigger only when column is modified
You have two way for your question :
1- Use Update Command in your Trigger.
ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
ON [dbo].[SCHEDULE]
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
IF UPDATE (QtyToRepair)
BEGIN
UPDATE SCHEDULE
SET modified = GETDATE()
, ModifiedUser = SUSER_NAME()
, ModifiedHost = HOST_NAME()
FROM SCHEDULE S INNER JOIN Inserted I
ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
WHERE S.QtyToRepair <> I.QtyToRepair
END
END
2- Use Join between Inserted table and deleted table
ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
ON [dbo].[SCHEDULE]
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
UPDATE SCHEDULE
SET modified = GETDATE()
, ModifiedUser = SUSER_NAME()
, ModifiedHost = HOST_NAME()
FROM SCHEDULE S
INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber
WHERE S.QtyToRepair <> I.QtyToRepair
AND D.QtyToRepair <> I.QtyToRepair
END
When you use update command for table SCHEDULE
and Set QtyToRepair
Column to new value, if new value equal to old value in one or multi row, solution 1 update all updated row in Schedule table but solution 2 update only schedule rows that old value not equal to new value.
How to log changed values in Update trigger
Here's one way using COLUMNS_UPDATED. Trigger does not depend on column names, so you can add or remove columns without problem. I have added some comments in the query
create trigger audit on Items
after update
as
begin
set nocount on;
create table #updatedCols (Id int identity(1, 1), updateCol nvarchar(200))
--find all columns that were updated and write them to temp table
insert into #updatedCols (updateCol)
select
column_name
from
information_schema.columns
where
table_name = 'Items'
and convert(varbinary, reverse(columns_updated())) & power(convert(bigint, 2), ordinal_position - 1) > 0
--temp tables are used because inserted and deleted tables are not available in dynamic SQL
select * into #tempInserted from inserted
select * into #tempDeleted from deleted
declare @cnt int = 1
declare @rowCnt int
declare @columnName varchar(1000)
declare @sql nvarchar(4000)
select @rowCnt = count(*) from #updatedCols
--execute insert statement for each updated column
while @cnt <= @rowCnt
begin
select @columnName = updateCol from #updatedCols where id = @cnt
set @sql = N'
insert into [Events] ([RecordId], [EventTypeId], [EventDate], [ColumnName], [OriginalValue], [NewValue], [TenantId], [AppUserId], [TableName])
select
i.Id, 2, GetUTCDate(), ''' + @columnName + ''', d.' + @columnName + ', i.' + @columnName +', i.TenantId, i.UpdatedBy, ''Item''
from
#tempInserted i
join #tempDeleted d on i.Id = d.Id and isnull(Cast(i.' + @columnName + ' as varchar), '''') <> isnull(Cast(d.' +@columnName + ' as varchar), '''')
'
exec sp_executesql @sql
set @cnt = @cnt + 1
end
end
I have changed data type of TableName
column of Events
table to nvarchar
.
SQLite modified trigger which only only updates modified field if values have actually changed
Instead of the operator !=
use the operator IS NOT
which works when one or both of the operands are NULL
:
......................................................
WHERE (OLD.order_id=NEW.order_id)
AND (
(OLD.order_date IS NOT NEW.order_date) OR
(OLD.grand_total_in_dollars IS NOT NEW.grand_total_in_dollars) OR
(OLD.ship_to IS NOT NEW.ship_to) OR
(OLD.store IS NOT NEW.store) OR
(OLD.more_href IS NOT NEW.more_href) OR
(OLD.grand_total IS NOT NEW.grand_total)
);
Trigger for rows where specific column was changed
You need to compare the inserted
and deleted
tables, joining by primary key, and verify the results are different.
Note that the UPDATE()
function only tells you if the column was present in the UPDATE
, not if the value actually changed.
CREATE TRIGGER testTrigger
ON table
AFTER UPDATE
AS
SET NOCOUNT ON;
IF(UPDATE(KeyProperty))
BEGIN
UPDATE t
SET Flag = True
FROM (
SELECT i.Id, i.KeyProperty
FROM inserted i
EXCEPT
SELECT d.Id, d.KeyProperty
FROM deleted d
) i
JOIN table t ON t.Id = i.Id
END
GO
Related Topics
What Are the Benefits of Using Database Cursor
Extracting Hours from a Datetime (SQL Server 2005)
SQL Speed Up Performance of Insert
Automate Version Number Retrieval from .Dtsx Files
How to See the Values of a Table Variable at Debug Time in T-Sql
Get Execution Time of Postgresql Query
Can an Inner Join Offer Better Performance Than Exists
Best Equivalent for Isinteger in SQL Server
Plsql Insert into with Subquery and Returning Clause
Select Corresponding to Row from the Same Table SQL Server
Sql: Capitalize First Letter Only
Combine Two Tables for One Output
Is There a Product Function Like There Is a Sum Function in Oracle SQL
When to Denormalize a Database Design
Transactsql to Run Another Transactsql Script