SQL Update trigger doesn't work as expected - sql

I have a problem with the programming of an Update trigger. I want to create a trigger that copies the row I edited in a new row with the new data and a new ID. The old row should be the same with just a flag change from 0 to 1.
The table look like this:
ID Artikelname PREIS UPDATE_DATE FLAG
1 Tomatoe 3 14.06.2012 16:00 0
2 Apple 1,5 12.05.2012 14:45 0
When I change the price of the first row, the table should look like this:
ID Artikelname PREIS UPDATE_DATE FLAG
1 Tomatoe 3 14.06.2012 16:00 1
2 Apple 1,5 12.05.2012 14:45 0
1 Tomatoe 2 13.07.2012 10:45 0
Here is my trigger so far:
USE [TestDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[Produkt_Update]
ON [dbo].[Produkt]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE #ID int
DECLARE #Artikelname nvarchar(50)
DECLARE #Preis numeric
DECLARE #Flag numeric
DECLARE #max_id int
SET #ID = (SELECT ID FROM inserted)
SET #Artikelname = (SELECT Artikelname FROM inserted)
SET #Preis = (SELECT Preis FROM inserted)
SET #Flag = (SELECT Flag FROM inserted)
SET #max_id = (SELECT MAX(ID) from dbo.Produkt)
SET IDENTITY_INSERT dbo.Produkt ON
INSERT INTO dbo.Produkt
(ID,Artikelname,Preis)
values (#max_id+1,#Artikelname,#Preis)
SET IDENTITY_INSERT dbo.Produkt OFF
UPDATE dbo.Produkt
SET Flag = 1
WHERE ID=#ID
END
With my trigger I can create the new row but the price on the first row sill changes. I don't know how to handle this. Could you help me please?

Please try this and let me know
CREATE TRIGGER [dbo].[Produkt_Update]
ON [dbo].[Produkt]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #MAX_ID INT;
SELECT #MAX_ID=MAX(ID) FROM [Produkt];
declare #tmp Table(ID int, Artikelname varchar(200),
PREIS varchar(200),UPDATE_DATE datetime, FLAG bit)
insert into #tmp
select ID,Artikelname,PREIS,UPDATE_DATE,1 [flag] from deleted;
delete T from [Produkt] T JOIN #tmp I
ON T.ID=I.ID
SET IDENTITY_INSERT [Produkt] ON
INSERT INTO [Produkt] (ID,Artikelname,PREIS,UPDATE_DATE,FLAG)
SELECT #MAX_ID+ROW_NUMBER() OVER(ORDER BY ID) [ID],Artikelname,PREIS,GETDATE(),0
FROM INSERTED
union all
select * from #tmp
SET IDENTITY_INSERT dbo.Produkt OFF
SET NOCOUNT OFF;
END;

You need to insert the price (preis) from the deleted table, not the inserted table

Replace
SET #ID = (SELECT ID FROM inserted)
with
SET #ID = (SELECT ID FROM deleted)

Related

SQL Server trigger interfering with select statement

We have a trigger on a table that fires during an update. We use Entity Framework on the front end. So, we don't want to see the intermediate results (the results from the trigger) when we do an update. I have SET NOCOUNT ON in my trigger:
ALTER TRIGGER [dbo].[trgForMedCMDWorkItemsUpdate]
ON [dbo].[MedCMDWorkItems]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
But when we execute the update, we see the intermediate results first and that throws Entity Framework.
Here is the sql command:
Declare #UserID int
set #UserID = 26
declare #WorkItemID int
Declare #UserRoles TABLE ( roleid INT ,parentroleid INT ,rolename VARCHAR(100) )
INSERT INTO #UserRoles SELECT R.id,R.parentroleid,R.ROLE FROM userroles UR
INNER JOIN roles R ON UR.roleid = R.id WHERE UR.userid = #UserID
Update MedCMDWorkItems with (UPDLOCK) Set AssignedTo = #UserID, AssignedAt =
getdate(), LastUpdatedAt = getdate(), LastUpdatedBy = #UserID, #WorkItemID =
ID
where ID = (SELECT TOP 1 PR.ID FROM MedCMDWorkItems PR --with (UPDLOCK)
JOIN TaskTypes TT on PR.TaskTypeID = TT.ID WHERE PR.AssignedTo IS NULL AND
PR.ClosedAt IS NULL
AND Exists (SELECT 1 FROM #UserRoles WHERE roleid in (852,772)) AND
((PR.WorkItemTypeID = (select 29 FROM #UserRoles UR WHERE UR.RoleID = 852)
AND TT.Task in (SELECT Substring(UR1.rolename, Charindex('|', UR1.rolename)
+ 1, Len(UR1.rolename)) FROM #UserRoles UR1 WHERE UR1.parentroleid in (852))
) OR
(PR.WorkItemTypeID = (select 28 FROM #UserRoles UR WHERE UR.RoleID = 772)
AND TT.Task in (SELECT Substring(UR1.rolename, Charindex('|', UR1.rolename)
+ 1, Len(UR1.rolename)) FROM #UserRoles UR1 WHERE UR1.parentroleid in (772))
) )
ORDER BY (CASE WHEN PR.IsUrgent IS NULL THEN 'False' ELSE PR.IsUrgent END)
DESC, ( CASE WHEN PR.Priority IS NULL THEN 9999 ELSE PR.Priority END ),
PR.CreatedAt)
Select * From MedCMDWorkItems PRI Where ID = #WorkItemID
Here is the trigger:
USE [CombinedWorkflow]
GO
/****** Object: Trigger [dbo].[trgForMedCMDWorkItemsUpdate] Script Date:
4/3/2018 5:20:22 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trgForMedCMDWorkItemsUpdate]
ON [dbo].[MedCMDWorkItems]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
declare #NewAssignedAt DateTime;
declare #OldAssignedAt DateTime;
declare #NewPulledAt DateTime;
declare #ID int;
select #NewPulledAt = i.PulledAt from inserted i;
select #NewAssignedAt = i.AssignedAt from inserted i;
select #OldAssignedAt = d.AssignedAt from deleted d;
select #ID = i.ID from inserted i;
--If work item is being un-assigned
if(#OldAssignedAt is NOT NULL and #NewAssignedAt is NULL)
BEGIN
SET NOCOUNT ON;
Update MedCMDWorkItems
Set PulledAt = NULL
Where ID = #ID
END
--if work item is being assinged
ELSE if(#NewAssignedAt IS NOT NULL)
BEGIN
SET NOCOUNT ON;
Update MedCMDWorkItems
Set FirstAssignedAt = #NewAssignedAt
Where FirstAssignedAt is NULL and ID = #ID
END
-- if work item is being pulled
ELSE if(#NewPulledAt IS NOT NULL)
BEGIN
SET NOCOUNT ON;
Update MedCMDWorkItems
Set AssignedAt = #NewPulledAt
Where AssignedAt is NULL and ID = #ID
SET NOCOUNT ON;
Update MedCMDWorkItems
Set FirstAssignedAt = #NewPulledAt
Where FirstAssignedAt is NULL and ID = #ID
END
Select 1
END
When I execute in SSMS with the trigger on under results, I get 2 results, first indicating 1 row was updated, then a second indicating the results of the query.
If I disable the trigger and execute the command I don't get the first result, and that is my desired behavior with the trigger enabled.
How do I set this trigger up so it fires but does not cause the additional results?
Thanks,
Sammer
you need to use inserted and deleted as tables, they can hold more than one row so you never can fill a variable from it.
In stead you have to build it like this example :
your code (first part of it)
ALTER TRIGGER [dbo].[trgForMedCMDWorkItemsUpdate]
ON [dbo].[MedCMDWorkItems]
AFTER UPDATE AS
BEGIN
SET NOCOUNT ON;
declare #NewAssignedAt DateTime;
declare #OldAssignedAt DateTime;
declare #NewPulledAt DateTime;
declare #ID int;
select #NewPulledAt = i.PulledAt from inserted i;
select #NewAssignedAt = i.AssignedAt from inserted i;
select #OldAssignedAt = d.AssignedAt from deleted d;
select #ID = i.ID from inserted i;
--If work item is being un-assigned
if(#OldAssignedAt is NOT NULL and #NewAssignedAt is NULL)
BEGIN
SET NOCOUNT ON;
Update MedCMDWorkItems
Set PulledAt = NULL
Where ID = #ID
END
can be replaced by something like this (not tested because i dont have your database)
ALTER TRIGGER [dbo].[trgForMedCMDWorkItemsUpdate]
ON [dbo].[MedCMDWorkItems]
AFTER UPDATE AS
BEGIN
SET NOCOUNT ON;
update MedCMDWorkItems
Set PulledAt = NULL
where ID in ( select i.Id
from Inserted i
left join deleted d on i.Id = d.Id
where d.AssignedAt is not null
and i.AssignedAt is null
)
Once you get this it will not be so hard to figure out how to adjust the rest of your trigger
Thanks everyone for the input. I figured out the issue. The trigger had this at the end:
...
END
-- if work item is being pulled
ELSE if(#NewPulledAt IS NOT NULL)
BEGIN
SET NOCOUNT ON;
Update MedCMDWorkItems
Set AssignedAt = #NewPulledAt
Where AssignedAt is NULL and ID = #ID
SET NOCOUNT ON;
Update MedCMDWorkItems
Set FirstAssignedAt = #NewPulledAt
Where FirstAssignedAt is NULL and ID = #ID
END
Select 1
END
No idea what that last line (Select 1) is supposed to be for (I didn't write the trigger :)), but it was causing the unwanted results being displayed.
Sammer

update trigger get the updated primary key

I have a trigger ,but I need to get the updated record's primary key (like as inserting the data SELECT #Id= ##IDENTITY) thus, I can pass it to where condition. How can I do that?
ALTER TRIGGER [dbo].[CariBakiyeBorcAktar]
ON [dbo].[BakimKartiDegisenParcalar]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Id int
DECLARE #CariId int
DECLARE #SId int
DECLARE #MId int
declare #Tutar decimal
declare #Bakiye decimal
declare #s decimal = 0
DECLARE #ParcaId int
--how I can I get the last updateed record Identity like this??
--and pass it to update query as a where condition
SELECT #Id= ##IDENTITY
set #SId=(select SId from CariBakiye where Id =#Id)
select #CariId=tblk.CariId ,#MId=tblk.MId, #SId= tblk.SId,#Tutar=tblk.Tutar from (
SELECT tbl.CariId , tbl.MId,tbl.SId,tbl.Tutar from (select cb.MId,SUM(bk.Tutar) as Tutar,bk.SId,cb.Id as CariId FROM [BakimKartiDegisenParcalar] bk
join CariBakiye cb on cb.SId=bk.SId
where bk.SId =cb.SId group by bk.SId,cb.MId,cb.Id ) as tbl
) as tblk where SId = #SId
set #Bakiye = #s-#Tutar
update CariBakiye set Borc=#Tutar,Bakiye=#Bakiye where Id=#CariId
print #Id
-- Insert statements for trigger here
END
As Martin said, you have to understand that SQL Server triggers are per statement, not per row. So in context of your trigger you have two tables - inserted and deleted, where you could find all information about data updated. If you really want to do per row processing, you could use cursor:
ALTER TRIGGER [dbo].[CariBakiyeBorcAktar] ON [dbo].[BakimKartiDegisenParcalar]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Id int
DECLARE #CariId int
DECLARE #SId int
DECLARE #MId int
declare #Tutar decimal
declare #Bakiye decimal
declare #s decimal = 0
DECLARE #ParcaId int
declare tr_cursor cursor local fast_forward for
select ID from inserted
while 1 = 1
begin
fetch tr_cursor into #Id
if ##fetch_status <> 0 break
set #SId=(select SId from CariBakiye where Id =#Id)
select #CariId=tblk.CariId ,#MId=tblk.MId, #SId= tblk.SId,#Tutar=tblk.Tutar from (
SELECT tbl.CariId , tbl.MId,tbl.SId,tbl.Tutar from (select cb.MId,SUM(bk.Tutar) as Tutar,bk.SId,cb.Id as CariId FROM [BakimKartiDegisenParcalar] bk
join CariBakiye cb on cb.SId=bk.SId
where bk.SId =cb.SId group by bk.SId,cb.MId,cb.Id ) as tbl
) as tblk where SId = #SId
set #Bakiye = #s-#Tutar
update CariBakiye set Borc=#Tutar,Bakiye=#Bakiye where Id=#CariId
print #Id
-- Insert statements for trigger here
end
close tr_cursor
deallocate tr_cursor
END

Suppress result from insert trigger

A MS SQL insert trigger causes some grief as it returns a result. Is there a way to disable any result from being sent? I've used 'set nocount on', but that doesn't seem to do much...
create trigger tr_insert_raw_data
on raw_data
instead of insert
as
begin
set nocount on
declare #query varchar(max);
declare #rev int;
declare #id int;
declare #oldrev int;
declare #contextinfo VARBINARY(128)
select #contextinfo = context_info()
if #contextinfo = 0x00001
begin
insert into raw_data select * from inserted
return
end
select * into #query from inserted
set #id = (select count(distinct id) from raw_data);
set #id += 1;
insert into revisions (username, hostname, ts) values (SYSTEM_USER, HOST_NAME(), GETDATE())
set #rev = (select max(id) from revisions);
select #oldrev = revision_id from inserted;
if #oldrev is null set #oldrev=#rev-1;
select * into #inserted from inserted
update #inserted set revision_id = #rev, id = #id
select * from #inserted
insert into raw_data select * from #inserted
insert into edges (a, b) values (#oldrev, #rev)
end
what kind of a result are you getting?
After a quick review of your query my first thought is that you're getting results due to the
select * from #inserted
line you have (third line from bottom). This line tells me that you are requesting results which is probably why you're getting some?
If you do need values selected from a table to modify or custom select out of, perhaps CTE's will help you out? (Common Table Expressions)

trigger at particular column with condition

I want to check a Column value when update.If its match insert into another table.
My Tables:
My trigger:
CREATE TRIGGER tr_test
ON test1
FOR UPDATE
AS
SET nocount ON
IF ( Update(sname) )
DECLARE #Name NVARCHAR
DECLARE #id INT
SET #id=##IDENTITY
SET #Name=(SELECT sname
FROM test1
WHERE id = #id)
IF( #Name = 'Paras' )
BEGIN
INSERT INTO test2
(loginfo)
VALUES ('success')
END
And my update query is:
update Test1 set Sname='Paras' where ID=1
When I run this update query Nothing is happen.Test2 table is empty.I think problem is ##IDENTITY but not sure.Thanks.
Try this:
CREATE TRIGGER tr_test
ON test1
FOR UPDATE
AS
SET nocount ON
IF ( Update(sname) )
DECLARE #Name NVARCHAR
DECLARE #id INT
SET #id=(select id from inserted)
SET #Name=(SELECT sname
FROM inserted
WHERE id = #id)
IF( #Name = 'Paras' )
BEGIN
INSERT INTO test2
(loginfo)
VALUES ('success')
END
But it's better to do this, an update can update many rows, the above will fail if UPDATE matches many rows, use EXISTS:
CREATE TRIGGER tr_test
ON test1
FOR UPDATE
AS
SET nocount ON
IF( EXISTS(select * From inserted where sname = 'Paras' ) )
BEGIN
INSERT INTO test2
(loginfo)
VALUES ('success')
END
inserted table is the name of the table where the UPDATE's new values goes, deleted table is the name of the table where UPDATE's old values goes

Is there a way to force a trigger to run on an update statement with multiple rows?

I have had to make changes to a trigger and assumed that running an update query like the following would make the trigger execute for all the matched rows. But instead, it only updates the record that it finds.
UPDATE someTable SET someField = someField WHERE someField = 'something';
As a quick solution, I created the following query using a cursor to loop through the records and update each row. It works, and luckily I don't have a really large dataset so it doens't take too long, but it just doesn't seem like the best solution.
DECLARE #id INT;
DECLARE queryCursor CURSOR FOR
SELECT id FROM someTable WHERE someField='something'
OPEN queryCursor
FETCH NEXT FROM queryCursor INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE someTable SET someField = someField WHERE id = #id
FETCH NEXT FROM queryCursor INTO #id
END
CLOSE queryCursor
DEALLOCATE queryCursor
Is there a better way to get a trigger to execute on multiple rows in SQL Server?
Edit: The code from trigger
FOR INSERT, UPDATE
AS
IF UPDATE (LineNumber)
OR UPDATE(LineService)
Begin
DECLARE #CDL VARCHAR(50)
DECLARE #LN VARCHAR(100)
DECLARE #A VARCHAR(25)
SELECT #CDL = CommonDataLink FROM INSERTED
SELECT #A = LineService FROM INSERTED
SET #LN = #CDL + #A
UPDATE CommonData SET ReportedLineNo = #LN WHERE CommonDataLink = #CDL
End
You have to make use of the special table INSERTED for what you want:
UPDATED CODE
FOR INSERT, UPDATE
AS
IF UPDATE (LineNumber)
OR UPDATE(LineService)
Begin
DECLARE #CDL VARCHAR(50)
DECLARE #LN VARCHAR(100)
DECLARE #A VARCHAR(25)
SELECT #CDL = CommonDataLink FROM INSERTED
SELECT #A = LineService FROM INSERTED
SET #LN = #CDL + #A
UPDATE A
SET ReportedLineNo = B.LineService + B.CommonDataLink
FROM CommonData A
INNER JOIN INSERTED B
ON A.CommonDataLink = B.CommonDataLink
End