SQL Server Update Trigger - sql-server

I have never used triggers before in SQL server and I have looked around online but haven't found an answer to my question. Basically I am trying to write an Trigger that will run after a record has been updated in a table. This trigger will then update two additional tables based on the record that was updated in the first table.
The primary table with the trigger on it will be updating one record using a query like this:
UPDATE E.SM_T_RList
SET IsActive = 0
WHERE Guid = #Guid
I then want the trigger to do something like this:
ALTER TRIGGER [E].[IsActiveUpdate]
ON [E].[SM_T_RList]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE E.SM_T_BInfo
SET IsActive = 0
WHERE Guid = #Guid
UPDATE E.SM_T_RMachines
SET IsActive = 0
WHERE GUID = #GUID
END
The Guid that I want updated is being used by the primary table. But I can't figure out how I get the #Guid that I want updated into the trigger? Please help.
Thanks

Both the answers already posted suffer from the same problem - they're marking the other rows as Inactive, whenever any update occurs on your base table
Something like:
ALTER TRIGGER [E].[IsActiveUpdate]
ON [E].[SM_T_RList]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE E.SM_T_BInfo
SET IsActive = 0
WHERE Guid IN (SELECT Guid FROM INSERTED where IsActive=0)
UPDATE E.SM_T_RMachines
SET IsActive = 0
WHERE Guid IN (SELECT Guid FROM INSERTED where IsActive=0)
END
Would be more appropriate

Triggers in SQL Server operate on sets of rows not individual rows. You access these via the inserted and deleted pseudo tables. Assuming that you might want the value of isactive to cascade when previously inactive rows were made active you could use something like this.
ALTER TRIGGER [E].[IsActiveUpdate]
ON [E].[SM_T_RList]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE E.SM_T_BInfo
SET IsActive = i.IsActive
FROM INSERTED i JOIN E.SM_T_BInfo e
ON e.Guid = i.Guid
UPDATE E.SM_T_RMachines
SET IsActive = i.IsActive
FROM INSERTED i JOIN E.SM_T_BInfo e
ON e.Guid = i.Guid
END

Related

Trigger that updates a column on the row where I clicked update

So I have this table:
ID Name IsDeleted
1 test True
2 test2 False
I currently have the following trigger:
CREATE TRIGGER [dbo].[Trigger_test_IsDeleted]
ON [dbo].[test]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.test
SET IsDeleted = 1
WHERE ID = 1;
END
When I click the delete button on a row in my index view, I need this trigger to launch and set IsDeleted to 1 (or True) on that exact row. However, doing it with the above trigger will obviously delete only the row source where ID = 1. And if I leave out the WHERE statement, it sets IsDeleted for all rows to true.
I need my trigger to dynamically determine where "delete" is being pressed and only delete that row.
I know that in MySQL, you can accomplish this by using the following in a delete trigger:
SET new.IsDeleted = 1
Is there a way to get the same effect as "new." in SQL Server triggers?
And since we're on the topic, I want to achieve the same thing, but with altering the current code in the controller. Here is what it looks like now:
//POST - Delete
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int? id)
{
var test = await _db.Test.FindAsync(id);
if (test== null)
{
return View();
}
_db.Test.Remove(test);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
This code will achieve an ordinary delete operation on the database. I want to alter it to set "IsDeleted" to 1 instead of deleting the record.
If you want to update a flag instead of delete (which some people call it SOFT DELETE), you should update your trigger like this:
CREATE TRIGGER [dbo].[Trigger_test_IsDeleted]
ON [dbo].[test]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.test
SET deleted.IsDeleted = 1
WHERE ID = deleted.ID; --<-- This is the key point
END
You need to examine the 'deleted' virtual table, and perform the update accordingly. EG:
create table test(id int primary key, IsDeleted bit default 0)
go
CREATE OR ALTER TRIGGER [dbo].[Trigger_test_IsDeleted]
ON [dbo].[test]
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.test
SET IsDeleted = 1
WHERE ID in (select id from deleted);
END
go
insert into test(id, IsDeleted) values (1,0),(2,0),(3,0)
delete from test where id = 2
select * from test
outputs
id IsDeleted
----------- ---------
1 0
2 1
3 0
(3 rows affected)

SQL AFTER INSERT,UPDATE Trigger with condition if update specific column

I would like to create a trigger that inserts new datasets into the FREE08 table (see example code) either after insert on FREE03. This part works. But i also want to insert new dataset into FREE08 only if specific column (FK2) of FREE03 is updated. Thought this works with the "IF UPDATE(FK2) statement in the trigger.
What happens is i got a new dataset in FREE08 everytime i have any update in FREE03.
How can i get that?
Thanks for your support
USE [DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[Einsatzhistorie Wechsler]
ON [dbo].[FREE03]
AFTER INSERT,UPDATE
AS
SET NOCOUNT ON;
IF UPDATE(FK2)
BEGIN
INSERT INTO FREE08
(
FK1,FK2,TEXT3,DATE1,TEXT2,DATE4
)
Select FK2,FK1,ID,DATE1,TEXT1,DATE11 From inserted
end
I've never found a real use for the UPDATE function, since it tells you whether a column was subject to UPDATE (I.e. appeared in the SET clause), not whether data actually changed. In addition, of course, triggers fire once per statement, not once per row, so your trigger may be dealing with a mixture of rows, some of which had their FK2 changed and some not.
It's usually better to use deleted and compare the previous/current values:
CREATE TRIGGER [dbo].[Einsatzhistorie Wechsler]
ON [dbo].[FREE03]
AFTER INSERT,UPDATE
AS
SET NOCOUNT ON;
INSERT INTO FREE08
(
FK1,FK2,TEXT3,DATE1,TEXT2,DATE4
)
Select i.FK2,i.FK1,i.ID,i.DATE1,i.TEXT1,i.DATE11
From inserted i
left join deleted d
on
i.ID = d.ID and
i.FK2 = d.FK2
where d.ID is null
Here I've assumed a) that ID represents an immutable primary key for the table and b) That FK2 isn't nullable.

How to deal with update of PK in ON INSTEAD OF UPDATE Trigger

I am trying to construct an INSTEAD OF UPDATE trigger that modifies one of the columns being inserted, before doing the insert. Works fine if the PK of the table is not changed during the update.
If the PK of the table is updated, I don't know how to write an update statement to update the appropriate rows. How do I know which rows to update.
CREATE Table MyTest(ID int NOT NULL PRIMARY KEY,
Name NVARCHAR(40) NOT NULL);
GO
INSERT INTO MyTest VALUES(1,'Joe');
INSERT INTO MyTest VALUES(2,'Scott');
INSERT INTO MyTest VALUES(3,'Dan');
INSERT INTO MyTest VALUES(4,'Becky');
GO
CREATE TRIGGER Update_Please
ON MyTest
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON
IF EXISTS (SELECT id
FROM INSERTED
EXCEPT
SELECT id
FROM DELETED)
RAISERROR ('now what?',-1,-1);
/* ======================================================
oh heck! The PK in one or more rows was updated
in the update statement. How do I construct
an update statement w/o joining to inserted on the PK?
====================================================== */
ELSE
BEGIN
-- ALTER A COLUMN
UPDATE M
SET name = CASE WHEN I.NAME = 'Mike' THEN 'The Dude' ELSE I.NAME END
FROM MyTest M
JOIN INSERTED I ON I.id = M.id;
END
END
GO
UPDATE MyTest SET Name = 'Mike' WHERE id > 2; --works great!
UPDATE MyTest SET ID = 9 WHERE Name = 'Joe'; --how do I complete the update?
As I wrote in my comment, I don't think you can do that in an instead of update trigger, however it's quite easy to do that with a for update. The reason is the difference between the two triggers - the instead of update trigger is fired before the underlying table is updated, while the for update trigger is fired after that. This means that whatever values you have in your inserted table are the same values you have in your actual table - so just change the trigger from instead of update to a for update:
CREATE TRIGGER Update_Please
ON MyTest
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE M
SET name = CASE WHEN I.NAME = 'Mike' THEN 'The Dude' ELSE I.NAME END
FROM MyTest M
JOIN INSERTED I ON I.id = M.id;
END
GO

Sql CretedDate Trigger

This is the query I used for creating a trigger to update the CreatedDate column of my table "websites"
create trigger [dbo].[trgrforcreateddate] on [dbo].[Websites]
after insert
as
update dbo.websites
set CreatedDate=getdate() from websites w inner join inserted i on w.website=i.website where w.website=i.website
It worked, only one should get updated with Created date (actually, the expected row is updated). But as a result I see
" rows updated"
Why?
For this you should be using a default constraint on CreatedDate instead of a trigger.
alter table dbo.websites add constraint df_websites_CreatedDate default getdate() for CreatedDate;
The trigger is not joining on a unique id, if it was you would see only 1 row affected for each insert. You should also use set nocount on; to prevent extra row result messages from being returned, but in this case it was good that it was not set so that you noticed the error.
alter trigger [dbo].[trgrforcreateddate] on [dbo].[Websites]
after insert
as
begin;
set nocount on;
update w
set w.CreatedDate=getdate()
from dbo.websites w
inner join inserted i
on w.id = i.id;
end;

Creating a trigger on SQL Server 2008 R2 to catch a record update

Using Great Plains here and one of our users keeps screwing up customer data so we want to put a trigger on the customer table and so we can find out who it is.
Anyway, I created a table called audit_RM00101 as follows:
DATE nchar(10)
CUSTNMBR char(15)
CUSTNAME char(65)
UPSZONE char(3)
SALSTERR char(15)
USERID nchar(100)
I want to capture those same fields from the table I want to audit so I wrote the trigger as follows:
CREATE TRIGGER CatchCustomerRegionUpdate
ON RM00101
FOR UPDATE
AS
DECLARE #UserID VARCHAR(128)
SELECT #UserID = system_user
INSERT INTO audit_RM00101
SELECT DATE, CUSTNMBR, CUSTNAME, UPSZONE, SALSTERR, #UserID FROM UPDATED
The trigger gets created just fine but when I try to test it by updating a customer record in Great Plains, Great Plains throws up an ugly error and the trigger doesn't get fired.
What am I doing wrong here?
Thanks.
in a trigger, you get the DELETED and INSERTED tables, there is no UPDATED, so replace FROM UPDATED with FROM INSERTED
also try to fix your USERID column, your audit_RM00101.USERID is a nchar(100) while #UserID is a VARCHAR(128).
EDIT based on OPs comment: Ah, so there is no way to audit when a table is updated by using a trigger?
in a trigger when deleting, DELETED is populated, but INSERTED is empty
in a trigger when updating, DELETED is populated with the original value, and INSERTED is populated with the newly updated values
in a trigger when inserting, DELETED is empty, but INSERTED has the newly inserted values
There is no UPDATED in SQL Server; just inserted and deleted.
Also, it makes sense to add IF ##ROWCOUNT = 0 RETURN in the very beginning of triger's body.
When UPDATE takes place, both inserted and deleted tables are not empty. You may add the following code to make sure you handle UPDATE, not insert/delete:
IF EXISTS(SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
BEGIN
-- handle update
END ;
It's not really important for your trigger because you specify just FOR UPDATE, it would be important if you had, for instance, FOR UPDATE, INSERT, DELETE.
we have only two magic tables called INSERTED and DELETED
update indirectly is a Delete statement followed by Insert statement. so you have to update the column's value which is present in INSERTED.
CREATE TRIGGER CatchCustomerRegionUpdate
ON RM00101
AFTER UPDATE
AS
BEGIN
DECLARE #INSERTED INT, #DELETED INT
SET #INSERTED = SELECT COUNT(*) FROM INSERTED
SET #DELETED = SELECT COUNT(*) FROM DELETED
IF #INSERTED = 1 AND #DELETED = 1
BEGIN
UPDATE TABLE1
SET COL1 = INSERTED_COL1
WHERE IDCOL = INSERTED_IDCOL
END
END

Resources