Deadlock when trigger is being triggered - sql-server

I have a simple trigger that is to update the modifiedtime column when the record have been changed (Updated or Inserted)
However i the server keeps going into a deadlock
CREATE TRIGGER updateDatetime
ON Table1
AFTER UPDATE, INSERT
AS BEGIN
SET NOCOUNT ON;
UPDATE Table1
SET LastModified = GETDATE()
FROM INSERTED i
WHERE i.ID = Table1.ID
SET NOCOUNT OFF;
END
MSSQL Server is not overly busy, but it can update / insert on Table1 maybe 10 transactions a minute.

Related

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;

Syntax for SQL Trigger to Insert Data in another DB and also to update any field in another db adfter a field is edited

Here is the scenario - I will be specific. I have a "bridged" DB on Sql Called [Fulcrum_Xfer] I use this bridged because the Main Db called [Fulcrum UAT] is using a bigint datatype for some fields and thereby displaying in my Access front end "#Deleted data" in all fields - This behavior CANNOT be changed in the present design (the bigint has to stay) so I have the exact table name and fieldnames in my [Fulcrum_Xfer] DB - the OrderNO field in Orders table in [Fulcrum_Xfer] is int and there is no primary key
What I need to have done by tomorrow under threat of some "you let us down" is the following
the table that initially gets data inserted or updated into is called Orders and is in the [Fulcrum_Xfer] database that structure is as follows
OrderNo int Unchecked
OrderDate smalldatetime Unchecked
ApplicationTenantLinkId int Unchecked
OrderStatus int Unchecked
the table that receives the triggered data from Orders in FulCrum_Xfer is called Orders and it is in Database Fulcrum UAT
The structure is
OrderNo bigint Unchecked Primarykey
OrderDate smalldatetime Unchecked
ApplicationTenantLinkId Bigint Unchecked
OrderStatus int Unchecked
I need two trigger statements that will insert a new record into Orders in [Fulcrum UAT] after I insert it into Orders in [FulCrum_Xfer]
and
I need a trigger that will update any field in Orders in [Fulcrum UAT] when I make a change in Orders in [Fulcrum_Xfer]
I do not know where the trigger goes other than maybe Database Triggers in [FulCrum_XFer] but I get freaked out by the template syntax (do not think I need all that) and I do not know how to write the syntax for each task
I am a very experienced VB / VBA developer and have used ADO for building and calling stored procedures on SQL but have never had to do this type of task on the SQL Server - please do not treat me like a dunce - but this is very important in my job right now.
Well, of cousre I have no way to test it, but I think this is how you'd write the INSERT trigger:
USE [Fulcrum_Xfer]
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER dbo.trOrders_Insert
ON dbo.Orders
AFTER INSERT AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Just INSERT everything from the [inserted] pseudotable into
--the target table
INSERT INTO [Fulcrum UAT].dbo.Orders
(OrderNo, OrderDate, ApplicationTenantLinkId, OrderStatus)
SELECT OrderNo, OrderDate, ApplicationTenantLinkId, OrderStatus
FROM inserted;
END
GO
Copy and paste this into a Query window in Management Studio and execute it.
Here's how I'd do the UPDATE trigger. Again, untested ...
USE [Fulcrum_Xfer]
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER dbo.trOrders_Update
ON dbo.Orders
AFTER UPDATE AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Just UPDATE everything matching [inserted] pseudotable
--into the target table
--(NOTE: This assumes that UPDATES will never change the PK/OrderNo)
UPDATE [Fulcrum UAT].dbo.Orders
SET OrderDate = ins.OrderDate,
ApplicationTenantLinkId
= ins.ApplicationTenantLinkId,
OrderStatus = ins.OrderStatus
FROM [Fulcrum UAT].dbo.Orders As tar
JOIN inserted as ins ON tar.OrderNo = ins.OrderNo;
--(also, performance may not be great as the JOIN columns are
-- different datatypes)
END
GO
Given:
Database db1 with table t1 and fields (username1, password1)
Database db2 with table t2 and fields (username2, password2)
If you want insert or update t2 in db2 when changes (assume insert) occurred in db1, you should add trigger to t1 in db1:
CREATE TRIGGER `trigger_name` AFTER INSERT ON `t1`
FOR EACH ROW INSERT INTO `db2`.`t2`( `username2`, `password2`)
VALUES ( new.username1, new.password1)
(I am assuming both databases are hosted on the same server.)
Try This
USE BioStar;// Which data base you want to create trigger
GO
CREATE TRIGGER trgAfterInsertnew ON [dbo].[TB_EVENT_LOG]
FOR INSERT
AS
declare #nDateTime int;
declare #nReaderIdn int;
declare #nEventIdn int;
declare #nUserID int;
declare #nIsLog smallint;
declare #nTNAEvent smallint;
declare #nIsUseTA smallint;
declare #nType smallint;
select #nDateTime=i.nDateTime from inserted i;
select #nDateTime=i.nReaderIdn from inserted i;
select #nEventIdn=i.nEventIdn from inserted i;
select #nUserID=i.nUserID from inserted i;
select #nIsLog=i.nIsLog from inserted i;
select #nTNAEvent=i.nTNAEvent from inserted i;
select #nIsUseTA=i.nIsUseTA from inserted i;
select #nType=i.nType from inserted i;
insert into [HRM].dbo.Device_Data
(nDateTime,nReaderIdn,nEventIdn,nUserID,nIsLog,nTNAEvent,nIsUseTA,nType)
values(#nDateTime,#nDateTime,#nEventIdn,#nUserID,#nIsLog,#nTNAEvent,#nIsUseTA,#nType);
--set #audit_action='Inserted Record -- After Insert Trigger.';
PRINT 'AFTER DELETE TRIGGER fired.'
GO

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

SQL Server Update Trigger

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

Resources