Trigger on update of an attribute of a table in SQL Server? - sql

I created computer audit application. When I ran my application, it shows computer accessories on browser like computerName, osVersion, lastAudit, model, totalMemory, processor, userName.
I have created a database in SQL Server 2008 with one table Computers. When a value is inserted into that table, I need to update the table value in the column. In an attempt to try this, I'm using a trigger. However, I do not fully understand how triggers work.
Can someone please show me how to accomplish this.
My table has these columns:
id, computerName, osVersion, lastAudit, model, totalMemory, processor, userName
I know that in this code something wrong or missing but I am not able to complete this. Please help me in this regard.
CREATE TRIGGER update_trigger
ON computers
AFTER UPDATE
AS
BEGIN
declare #id as int
declare #computerName as varchar(100)
declare #osVersion as varchar(100)
declare #lastAudit as datetime
declare #model as varchar(100)
declare #totalMemory float
declare #processor as varchar(100)
declare #userName as varchar(100)
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
if update(id)
BEGIN
insert into computers values(#id,#computerName,#osVersion,#lastAudit,#model,
#totalMemory,#processor,#userName,'Update')
SET NOCOUNT ON;
END
GO

If you want to simply update one or more columns of your existing table when new rows are being inserted (or when they are updated? Not quite clear...), try a trigger like this:
CREATE TRIGGER trigUpdateTable
ON dbo.Computers
AFTER INSERT -- or AFTER UPATE or AFTER INSERT, UPDATE
AS
BEGIN
-- do whatever you want to do on INSERT and/or UPDATE
UPDATE
dbo.Computers
SET
LastAudit = GETDATE()
FROM
dbo.Computers c
INNER JOIN
Inserted i ON c.id = i.id
One very important point to remember: SQL Server triggers are not called per row that is affected - but per statement, and if your INSERT or UPDATE statement affects multiple rows, you'll have multiple entries in the Inserted pseudo table and you need to be able to deal with that fact in your trigger

Related

Determine in value change before updating in Sql Server

I am working on updating of table
Table : User(id,name,Dob,createdDate,lastUpdatedDate)
I want to update lastUpdatedDate when there is change in Name column.
firstly I used update trigger but that is execute after updating of records
some helping link suggest me to use instead of Update trigger but if I use that one it will not update my records after trigger.
any solution?
well I found suitable solution for this problem
in after update trigger there are two table sets one is Inserted and other one is Deleted.
so by getting name from both table sets we can campare there values. if there value is same we will not update lastUpdatedDate and if there is any difference in values then we will update lastUdpatedDate
Create TRIGGER [dbo].[User__Update]
ON [dbo].[User]
AFTER Update
AS
BEGIN
SET NOCOUNT ON;
declare #AttributeValue varchar(max)
declare #OldAttributeValue varchar(max)
SELECT #AttributeValue = INSERTED.Name
FROM INSERTED
select #OldAttributeValue=Deleted.Name
FROM Deleted
if(#OldAttributeValue<>#AttributeValue)
BEGIN
IF UPDATE(Name)
BEGIN
update [User] set LastUpdatedDate=GetDate() where name =#AttributeValue
END
END
END

MSSQL Trigger for update is working only once

I'm going to create a trigger for update. Purpose of this trigger is that If muadurum column is changed , take the old value of mua_tarih in table fkayitlar and insert to another table mua_tarihleri.
My code block like;
ALTER TRIGGER [dbo].[trgr_fkayit_update]
ON [dbo].[fkayitlar]
AFTER UPDATE
AS
DECLARE #mua_durum_once NVARCHAR(10)
DECLARE #mua_durum_sonra NVARCHAR(10)
DECLARE #mua_tarih_once DATE
DECLARE #mua_yapan_once NVARCHAR(25)
DECLARE #kisi_id INT
Take the old value;
SELECT
#kisi_id=kayitid,
#mua_durum_once=muayenedurum,
#mua_tarih_once=muayenetarih,
#mua_yapan_once=mua_yapan
FROM deleted
Take the new value;
SELECT #mua_durum_sonra=muayenedurum FROM inserted
Check if value is changed ; if changed, Insert #mua_tarih to table mua_tarihleri with #kisi_id and #mua_yapan_once
IF #mua_durum_once='OLDU'
AND #mua_durum_sonra='OLMADI'
AND #mua_tarih_once IS NOT NULL
BEGIN
INSERT INTO mua_tarihleri(kayitid,mua_tarihi,mua_yapan)
VALUES(#kisi_id,#mua_tarih_once,#mua_yapan_once)
END
My problem is When I update more than one row in table fkayitlar,Trigger is working, but I see only one inserted row in table mua_tarihleri (only working once). I need to see more than one.(should be working more than once) Are not Triggers working on more than one process? or How can I solve this my problem?
The trigger only occurs once when the table is updated, no matter how many rows are updated. Therefore, you have to write your trigger body to operate on a set of rows, not a single row as you have done.
Should be something like:
ALTER TRIGGER [dbo].[trgr_fkayit_update]
ON [dbo].[fkayitlar]
AFTER UPDATE
AS
INSERT INTO mua_tarihleri(kayitid,mua_tarihi,mua_yapan)
SELECT deleted.kayitid, deleted.muayenedurum, deleted.muayenetarih, deleted.mua_yapan
FROM deleted
JOIN inserted ON deleted.kayitid = inserted.kayitid
WHERE deleted.muayenedurum='OLDU'
AND inserted.muayenedurum='OLMADI'
AND muayenetarih IS NOT NULL

SQL Server 2008 Trigger Errors

I need some help with a database trigger that I just can't get working. Disclosure : I'm not a DBA and don't claim any expertise, but I've been tasked with getting this done..
That said, here is the problem statement:
I have a database with a few tables that we want to track updates/inserts/deletes on. The trigger should catch the "before" and the "after" values of some columns, take both along with some additional information (mentioned below) and add them to a versioncontrol table.
I've put together what I think should work based on tutorial and other information available on various sites, but no matter what I do when I try to create the trigger it always throws an error:
Msg 311, Level 16, State 1, Procedure tr_taskconstructs_U, Line 38
Cannot use text, ntext, or image columns in the 'inserted' and 'deleted' tables.
The column layout for the table I'm applying the trigger to is very simple:
ResourceID (PK, nvarchar(38))
AML (ntext, null)
I want to catch changes to the AML column and insert the changes into a table, as follows:
id (int, not null),
ResourceID (nvarchar(38)),
TableName (nvarchar(50)),
DateTime (datetime),
Type (varchar(20)),
Before (ntext),
After (ntext)
My code is as follows:
CREATE TRIGGER tr_taskconstructs_U ON taskconstructs AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #before nvarchar(max),
#after nvarchar(max),
#resource nvarchar(50)
IF UPDATE(AML)
BEGIN
SELECT #before = d.AML,
#after = i.AML,
#resource = d.ResourceID
FROM inserted i
INNER JOIN deleted d on d.ResourceID = i.ResourceID
INSERT INTO versioncontrol
VALUES (#resource, 'taskconstructs', GetDate(), 'Update', #before, #after)
END
END
GO
It always fails with the error mentioned above. I tried changing the datatypes of the variables, etc, with the same result. I've tried commenting out almost everything and it also has the same error. I'm obviously missing something, but am not able to figure out what.
Any help would be appreciated.
change the table to use the nvarchar(max) datatype instead. Ntext is deprecated anyhow and should not be used going forward
Like said in the other answer ntext, along with text and image are deprecated datatypes which triggers can't really work with. They will be removed for future versions of SQL Server so your best solution would be to change them to NVARCHAR(MAX), VARCHAR(MAX) or VARBINARY(MAX) respectively.
http://msdn.microsoft.com/en-us/library/ms189799.aspx
However, if that's not a possibility for you at the moment - INSTEAD OF trigger can work with them so you can try to intercept the updates and do them from trigger, filling your log table in the process.
But, before I show you example of that, I must point out a big mistake you have in your existing trigger - You have wrote the query that works only on single row from INSERTED which is not good as UPDATE can often update more then 1 row at the time. Even if your app is not designed to allow it, you should never write trigger asuming it is always going to be single row.
So your trigger should have looked like this:
(and should look like this if/after you change that column)
CREATE TRIGGER tr_taskconstructs_U ON taskconstructs AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF UPDATE(AML)
BEGIN
INSERT INTO versioncontrol
SELECT ResourceID, 'taskconstructs', GetDate(), 'Update'. d.AML, i.AML
FROM inserted i
INNER JOIN deleted d on d.ResourceID = i.ResourceID
END
END
GO
Now, for the INSTEAD OF TRIGGER:
CREATE TRIGGER tr_taskconstructs_U ON taskconstructs INSTEAD OF UPDATE
AS
BEGIN
--insert log
INSERT INTO versioncontrol
SELECT i.ResourceID, 'taskconstructs', GetDate(), 'Update', d.AML, i.AML
FROM inserted i
INNER JOIN taskconstructs d on d.ResourceID = i.ResourceID
--update actual data
UPDATE t
SET t.AML = i.AML
from taskconstructs t
INNER JOIN INSERTED i ON i.ResourceID = t.ResourceID
END
GO
SQLFiddle DEMO

SQL Server - Get Inserted Record Identity Value when Using a View's Instead Of Trigger

For several tables that have identity fields, we are implementing a Row Level Security scheme using Views and Instead Of triggers on those views. Here is a simplified example structure:
-- Table
CREATE TABLE tblItem (
ItemId int identity(1,1) primary key,
Name varchar(20)
)
go
-- View
CREATE VIEW vwItem
AS
SELECT *
FROM tblItem
-- RLS Filtering Condition
go
-- Instead Of Insert Trigger
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
SELECT Name
FROM inserted;
END
go
If I want to insert a record and get its identity, before implementing the RLS Instead Of trigger, I used:
DECLARE #ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT #ItemId = SCOPE_IDENTITY();
With the trigger, SCOPE_IDENTITY() no longer works - it returns NULL. I've seen suggestions for using the OUTPUT clause to get the identity back, but I can't seem to get it to work the way I need it to. If I put the OUTPUT clause on the view insert, nothing is ever entered into it.
-- Nothing is added to #ItemIds
DECLARE #ItemIds TABLE (ItemId int);
INSERT INTO vwItem (Name)
OUTPUT INSERTED.ItemId INTO #ItemIds
VALUES ('MyName');
If I put the OUTPUT clause in the trigger on the INSERT statement, the trigger returns the table (I can view it from SQL Management Studio). I can't seem to capture it in the calling code; either by using an OUTPUT clause on that call or using a SELECT * FROM ().
-- Modified Instead Of Insert Trigger w/ Output
CREATE TRIGGER IO_vwItem_Insert ON vwItem
INSTEAD OF INSERT
AS BEGIN
-- RLS Security Checks on inserted Table
-- Insert Records Into Table
INSERT INTO tblItem (Name)
OUTPUT INSERTED.ItemId
SELECT Name
FROM inserted;
END
go
-- Calling Code
INSERT INTO vwItem (Name)
VALUES ('MyName');
The only thing I can think of is to use the IDENT_CURRENT() function. Since that doesn't operate in the current scope, there's an issue of concurrent users inserting at the same time and messing it up. If the entire operation is wrapped in a transaction, would that prevent the concurrency issue?
BEGIN TRANSACTION
DECLARE #ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT #ItemId = IDENT_CURRENT('tblItem');
COMMIT TRANSACTION
Does anyone have any suggestions on how to do this better?
I know people out there who will read this and say "Triggers are EVIL, don't use them!" While I appreciate your convictions, please don't offer that "suggestion".
You could try SET CONTEXT_INFO from the trigger to be read by CONTEXT_INFO() in the client.
We use it the other way to pass info into the trigger but would work in reverse.
Have you in this case tried ##identity? You mentioned both scope_Identity() and identity_current() but not ##identity.

After insert, update trigger not running

I have two triggers After Insert or Update and Instead of Insert. It appears that the after trigger is not running or sending the correct data.
I have verified the correct operation of Z_UpdateStageTable stored procedure and the Instead of Insert trigger. Removing the Instead of Insert trigger doesn't have any affect. The After Insert, Update trigger was working correctly at one time, I haven't made any changes to it. I have tried deleting it and adding it, but it still doesn't run or have the correct data.
Any Ideas?
Instead of Insert:
ALTER TRIGGER [DeleteExistingFilter]
ON [dbo].[Z_MobileSyncFilters]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM Z_MobileSyncFilters WHERE UserID = (SELECT UserID FROM INSERTED);
INSERT INTO Z_MobileSyncFilters
SELECT *
FROM INSERTED;
END
After Insert, Update:
TRIGGER [UpdateStageTable]
ON [dbo].[Z_MobileSyncFilters]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #AllWos AS VARCHAR(5000);
DECLARE #PmWos AS VARCHAR(5000);
DECLARE #RepWos AS VARCHAR(5000);
SET #AllWos = (SELECT AllWos FROM INSERTED);
SET #RepWos = (SELECT AllWos FROM INSERTED);
SET #PmWos = (SELECT AllWos FROM INSERTED);
EXEC Z_UpdateStageTable #AllWos;
EXEC Z_UpdateStageTable #RepWos;
EXEC Z_UpdateStageTable #PmWos;
END
Is there a typo in the SET part of the AFTER trigger? You're selecting the same thing into three different variables.
Rather than confirming the behavior of Z_UpdateStageTable, I'd try to replace it with something dirt simple (a parameterless sql statement, say) to test whether the trigger's being called. It's possible that the sproc's not being called with what you think it's being called with.
You can add PRINT statements to the trigger and manually insert from ManagementStudio/Enterprise Manager to see where the trigger fails.
I see a problem when you insert multiple records in a single statement, as the SELECT FROM Inserted will return more than 1 record.
You can also update the SET statement to SELECT #Var = AllWos FROM Inserted
Hold on a second, if userid is your PK then Z_MobileSyncFilters will not have data yet, this is also an instead of trigger
this wholw block doesn't do anything really, why do you need this trigger?
DELETE FROM Z_MobileSyncFilters WHERE UserID = (SELECT UserID FROM INSERTED);
INSERT INTO Z_MobileSyncFilters
SELECT *
FROM INSERTED;
you second trigger is flawed because it will faile if you have a multi row operation
why do you have 2 insert trigger (1 instead 1 after) on this table?