Reputation: 2553
Inherited some work and have created a trigger. From what I've read I have done stuff I shouldn't have (declared variable etc) but the trigger works for update that affect one row only. If the update affects more than one row it fails.
Basically the trigger stores the 'deleted old/previous' values of specific fields from tblMachine into tblAudit.
My Trigger is...
USE [MyDB]
GO
/****** Object: Trigger [dbo].[tr_StatusChange_tblMachine] Script Date: 12/07/2017 09:00:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_StatusChange_tblMachine] ON [dbo].[tblMachine]
AFTER UPDATE
AS BEGIN
SET XACT_ABORT ON;
SET NOCOUNT ON;
SET ROWCOUNT 0;
DECLARE @NewStatus nvarchar(20);
DECLARE @OldStatus nvarchar(20);
DECLARE @ChangeMade nvarchar(50);
IF UPDATE (MachineStatus)
BEGIN
SET @NewStatus = (SELECT i.MachineStatus FROM inserted i FULL OUTER JOIN deleted d ON i.ID = d.ID);
SET @OldStatus = (SELECT d.MachineStatus FROM deleted d FULL OUTER JOIN inserted i ON i.ID = d.ID);
SET @ChangeMade = CONCAT ( @OldStatus, '-', @NewStatus );
-- Status Changed
IF @OldStatus <> @NewStatus
BEGIN
INSERT INTO dbo.tblAudit (InsertedDate, Month, Year, SupportTeam, MachineName, MachineStatus,
DateBuilt, DismantledDate, ServiceCheck, ServiceCheckBit, ChangeMade, ChangedBy)
SELECT getdate() AS InsertedDate ,(datepart(month,getdate())) AS Month, (datepart(Year,getdate())) AS Year,
i.SupportTeam, i.MachineName, i.MachineStatus, i.DateBuilt, i.DismantledDate,
CASE i.ServiceCheck WHEN 0 THEN 'No' ELSE 'Yes' END AS ServiceCheck, i.ServiceCheck, @ChangeMade, i.ChangedBy
FROM inserted i JOIN deleted d
ON i.MachineName = d.MachineName
WHERE i.SupportTeamID <> 'mbe';
END;
-- NO STATUS CHANGE
IF @OldStatus = @NewStatus
BEGIN
-- NO STATUS CHANGE
-- Service Check may have changed!
-- INSERT INTO AUDIT IF ServiceCheck has changed AND MachineStatus is NOT DecommissionedX or Dismantled.
INSERT INTO dbo.tblAudit (InsertedDate, Month, Year, SupportTeam, MachineName, MachineStatus,
DateBuilt, DismantledDate, ServiceCheck, ServiceCheckBit, ChangeMade, ChangedBy)
SELECT getdate() AS InsertedDate, (datepart(month,getdate())) AS Month, (datepart(Year,getdate())) AS Year,
i.SupportTeam, i.MachineName, i.MachineStatus, i.DateBuilt, (SELECT getdate() AS DismantledDate),
CASE i.ServiceCheck WHEN 0 THEN 'No' ELSE 'Yes' END AS ServiceCheck, i.ServiceCheck, 'SC Changed - Status Not Changed', i.ChangedBy
FROM inserted i JOIN deleted d
ON i.MachineName = d.MachineName
WHERE i.SupportTeamID <> 'mbe' And i.ServiceCheck <> d.ServiceCheck
AND (i.MachineStatus <> 'DecommissionedX' AND i.MachineStatus <> 'Dismantled');
END;
END;
END
From my searches I have discovered that Triggers fire once per statement and not per row affected. I have read that a trigger can be modified to check how many rows are being updated and then use this information to do actions based on each row. Unfortunately I have not found a way to do this that I understand.
My forte is web front-end coding and my SQLServer coding is limited. The code needs to be in SQL and not the front end as there are some other tools that update these records apart from a web front-end.
Any help in adapting this trigger so that it also copes with multiple update in one statement would be appreciated.
Upvotes: 0
Views: 167
Reputation: 32687
I think this does it. The trick is to just use MachineStatus from your inserted and deleted tables directly in the insert into your audit table rather than trying to calculate a universal old and new status up front.
USE [MyDB]
GO
/****** Object: Trigger [dbo].[tr_StatusChange_tblMachine] Script Date: 12/07/2017 09:00:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tr_StatusChange_tblMachine] ON [dbo].[tblMachine]
AFTER UPDATE
AS BEGIN
SET XACT_ABORT ON;
SET NOCOUNT ON;
SET ROWCOUNT 0;
IF UPDATE (MachineStatus)
BEGIN
INSERT INTO dbo.tblAudit (InsertedDate, Month, Year, SupportTeam, MachineName, MachineStatus,
DateBuilt, DismantledDate, ServiceCheck, ServiceCheckBit, ChangeMade, ChangedBy)
SELECT getdate() AS InsertedDate ,(datepart(month,getdate())) AS Month, (datepart(Year,getdate())) AS Year,
i.SupportTeam, i.MachineName, i.MachineStatus, i.DateBuilt, i.DismantledDate,
CASE i.ServiceCheck WHEN 0 THEN 'No' ELSE 'Yes' END AS ServiceCheck, i.ServiceCheck, CONCAT(d.MachineStatus, '-', i.MachineStatus), i.ChangedBy
FROM inserted i JOIN deleted d
ON i.MachineName = d.MachineName
WHERE i.SupportTeamID <> 'mbe'
-- Status Changed
AND i.MachineStatus <> d.MachineStatus;
-- NO STATUS CHANGE
-- Service Check may have changed!
-- INSERT INTO AUDIT IF ServiceCheck has changed AND MachineStatus is NOT DecommissionedX or Dismantled.
INSERT INTO dbo.tblAudit (InsertedDate, Month, Year, SupportTeam, MachineName, MachineStatus,
DateBuilt, DismantledDate, ServiceCheck, ServiceCheckBit, ChangeMade, ChangedBy)
SELECT getdate() AS InsertedDate, (datepart(month,getdate())) AS Month, (datepart(Year,getdate())) AS Year,
i.SupportTeam, i.MachineName, i.MachineStatus, i.DateBuilt, (SELECT getdate() AS DismantledDate),
CASE i.ServiceCheck WHEN 0 THEN 'No' ELSE 'Yes' END AS ServiceCheck, i.ServiceCheck, 'SC Changed - Status Not Changed', i.ChangedBy
FROM inserted i JOIN deleted d
ON i.MachineName = d.MachineName
WHERE i.SupportTeamID <> 'mbe' And i.ServiceCheck <> d.ServiceCheck
AND (i.MachineStatus <> 'DecommissionedX' AND i.MachineStatus <> 'Dismantled')
-- NO STATUS CHANGE
AND (i.MachineStatus = d.MachineStatus);
END;
END
Upvotes: 0
Reputation: 2300
Well this may not be the BEST option, but one way to solve this would be to use a temp table to store which machine changed and then join to the inserted or deleted table. Such as:
ALTER TRIGGER [dbo].[tr_StatusChange_tblMachine] ON [dbo].[tblMachine]
AFTER UPDATE
AS BEGIN
SET XACT_ABORT ON;
SET NOCOUNT ON;
SET ROWCOUNT 0;
CREATE TABLE #tmp(MachineName nvarchar(50), NewStatus nvarchar(20) NULL, OldStatus nvarchar(20) NULL)
INSERT INTO #tmp(MachineName, NewStatus)
SELECT MachineName,MachineStatus from inserted
UPDATE #tmp SET OldStatus = d.MachineStatus
From deleted d
INNER JOIN #tmp t on t.MachineName = d.MachineName
---You could delete from #tmp where OldStatus is null to ensure this was an update
-- Status Changed
INSERT INTO dbo.tblAudit (InsertedDate, Month, Year, SupportTeam, MachineName, MachineStatus,
DateBuilt, DismantledDate, ServiceCheck, ServiceCheckBit, ChangeMade, ChangedBy)
SELECT getdate() AS InsertedDate ,(datepart(month,getdate())) AS Month, (datepart(Year,getdate())) AS Year,
i.SupportTeam, i.MachineName, i.MachineStatus, i.DateBuilt, i.DismantledDate,
CASE i.ServiceCheck WHEN 0 THEN 'No' ELSE 'Yes' END AS ServiceCheck, i.ServiceCheck, @ChangeMade, i.ChangedBy
FROM inserted i JOIN #tmp t
ON i.MachineName = t.MachineName and t.NewStatus <> t.OldStatus
WHERE i.SupportTeamID <> 'mbe' ;
-- NO STATUS CHANGE
-- Service Check may have changed!
-- INSERT INTO AUDIT IF ServiceCheck has changed AND MachineStatus is NOT DecommissionedX or Dismantled.
INSERT INTO dbo.tblAudit (InsertedDate, Month, Year, SupportTeam, MachineName, MachineStatus,
DateBuilt, DismantledDate, ServiceCheck, ServiceCheckBit, ChangeMade, ChangedBy)
SELECT getdate() AS InsertedDate, (datepart(month,getdate())) AS Month, (datepart(Year,getdate())) AS Year,
i.SupportTeam, i.MachineName, i.MachineStatus, i.DateBuilt, (SELECT getdate() AS DismantledDate),
CASE i.ServiceCheck WHEN 0 THEN 'No' ELSE 'Yes' END AS ServiceCheck, i.ServiceCheck, 'SC Changed - Status Not Changed', i.ChangedBy
FROM inserted i JOIN #tmp t
ON i.MachineName = t.MachineName and t.OldStatus = t.NewStatus
WHERE i.SupportTeamID <> 'mbe' And i.ServiceCheck <> d.ServiceCheck
AND (i.MachineStatus <> 'DecommissionedX' AND i.MachineStatus <> 'Dismantled');
END
Again you will need to check the syntax as I am just modifying your code, but this is the gist of the idea.
Upvotes: 1