I have two tables
Customer
CustomerUpdate
Structure of both tables are like this
Customer table's structure
CustomerName | CustomerId
CustomerUpdate table's structure
NewCustomerName | NewCustomerId | OldCustomerName
I have few values inserted in the Customer table. Whenever I should update the data in this table I want that the existing as well as new data should be triggered into new table CustomerUpdate.
For this I created a trigger but this is only pulling the updated data, it's not pulling the existing data..
CREATE TRIGGER trgAfterUpdate
ON [dbo].Customer
FOR UPDATE
AS
SET NOCOUNT ON
declare #NewCustomerName nchar(20);
declare #NewCustomerId nchar(20);
declare #OldCustomerName nchar(20);
declare #audit_action varchar(100);
select #NewCustomerName = i.CustomerName from inserted i;
select #NewCustomerId = i.CustomerId from inserted i;
select #OldCustomerName = c.CustomerName
from Customer c
where CustomerId = #NewCustomerId;
if update(CustomerName)
set #audit_action='Updated Record -- After Update Trigger.';
if update(CustomerId)
set #audit_action='Updated Record -- After Update Trigger.';
insert into CustomerUpdate(NewCustomerName, NewCustomerId, OldCustomername)
values(#NewCustomerName, #NewCustomerId, #OldCustomerName);
PRINT 'AFTER UPDATE Trigger fired.'
GO
Please help me out
First, selecting from the table being modified when an update trigger is executing will get the new value. These are AFTER triggers (rather than INSTEAD triggers) and therefore the update has already happened by the time the trigger fires (although it can be rolled back). If you need the old value, you should select from the DELETED pseudo-table.
Second, as pointed out by #marc_s in comments, your trigger has the hidden assumption that only one row is affected by each update. This may very well be a valid assumption for your environment, if your application only ever updates one row at a time, but in the general case, every trigger should be ready to handle the case where many rows are affected by a single update. Writing your triggers to handle multiple rows is good practice.
Third, all of your sequentially executing code is pretty much unnecessary. The old value and the new value can be retrieved and inserted all at once:
CREATE TRIGGER trgAfterUpdate
ON [dbo].Customer
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON
insert into CustomerUpdate(NewCustomerName, NewCustomerId, OldCustomername)
-- case 1: ID unchanged
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
JOIN Deleted D on I.CustomerID=D.CustomerID
UNION ALL
-- case 2: ID changed, Name unchanged
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
JOIN Deleted D on I.CustomerName=D.CustomerName
WHERE I.CustomerID<>D.CustomerID
UNION ALL
--case 3: ID changed, Name changed
SELECT I.CustomerName, I.CustomerID, D.CustomerName
FROM Inserted I
LEFT JOIN Deleted D on I.CustomerID=D.CustomerID OR I.CustomerName=D.CustomerName
WHERE D.CustomerID IS NULL;
END
Related
I am creating a trigger whenever a purchase event gets inserted or updated in the PurchaseEvent table below.
The actual product count (completed purchases) is currently not tracked in a table, it is dynamically calculated each time by performing a SUM on the PurchaseEvent table like:
SELECT SUM(Quantity) FROM PurchaseEvent WHERE ProductID = #ProductID and PurchaseStatus = 'COMPLETED'
Below is the main PurchaseEvent table and the new table I just created:
PurchaseEvent
- PurchaseEventID
- ProductID
- Quantity
- PurchaseStatus ( COMPLETED, CANCELLED)
ProductCount
- ProductID
- ProductCount
I am creating a trigger to handle the insert or update events.
A row can be updated to change the PurchaseStatus.
CREATE TRIGGER trg_UpdateProductCount
ON PurchaseEvent
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
// TODO
END
Since a trigger can be run for a single record or multiple, what is the best way to handle this?
I was thinking:
UPDATE pc
SET rc.PurchaseCount = rc.PurchaseCount + SUM(i.ProductID)
FROM PurchaseCount pc
INNER JOIN Inserted i ON i.ProductID = pc.ProductID
WHERE i.PurchaseStatus = 'COMPLETED'
But then I have to somehow handle the CANCELLED records also?
Is it possible to just increment/decrement the PurchaseCount table or is the only
way to do this accurately is to SUM the original table each and every time a record is updated/inserted? (and just ignore the records in the INSERTED table)
I have a table called dsReplicated.matDB and a column fee_earner. When that column is updated, I want to record two pieces of information:
dsReplicated.matDB.mt_code
dsReplicated.matDB.fee_earner
from the row where fee_earner has been updated.
I've got the basic syntax for doing something when the column is updated but need a hand with the above to get this over the line.
ALTER TRIGGER [dsReplicated].[tr_mfeModified]
ON [dsReplicated].[matdb]
AFTER UPDATE
AS
BEGIN
IF (UPDATE(fee_earner))
BEGIN
print 'Matter fee earner changed to '
END
END
The problem with triggers in SQL server is that they are called one per SQL statement - not once per row. So if your UPDATE statement updates 10 rows, your trigger is called once, and the Inserted and Deleted pseudo tables inside the trigger each contain 10 rows of data.
In order to see if fee_earner has changed, I'd recommend using this approach instead of the UPDATE() function:
ALTER TRIGGER [dsReplicated].[tr_mfeModified]
ON [dsReplicated].[matdb]
AFTER UPDATE
AS
BEGIN
-- I'm just *speculating* here what you want to do with that information - adapt as needed!
INSERT INTO dbo.AuditTable (Id, TriggerTimeStamp, Mt_Code, Old_Fee_Earner, New_Fee_Earner)
SELECT
i.PrimaryKey, SYSDATETIME(), i.Mt_Code, d.fee_earner, i.fee_earner
FROM Inserted i
-- use the two pseudo tables to detect if the column "fee_earner" has
-- changed with the UPDATE operation
INNER JOIN Deleted d ON i.PrimaryKey = d.PrimaryKey
AND d.fee_earner <> i.fee_earner
END
The Deleted pseudo table contains the values before the UPDATE - so that's why I take the d.fee_earner as the value for the Old_Fee_Earner column in the audit table.
The Inserted pseudo table contains the values after the UPDATE - so that's why I take the other values from that Inserted pseudo-table to insert into the audit table.
Note that you really must have an unchangeable primary key in that table in order for this trigger to work. This is a recommended best practice for any data table in SQL Server anyway.
I have an UPDATE statement that, long story short, runs every half minute. This statement is in Powershell so don't mind the variable syntax...
UPDATE dbo.MobileLeases
SET IPAddress = '$($l.IPAddress)',
OwnerName = '$($l.OwnerName)',
Building = '$($l.Building)',
TimeOn = '$($l.Time)',
LeaseExpiry = '$($l.LeaseExpiry)',
Phone = '$($l.Phone)',
OwnerEmail = '$($l.OwnerEmail)'
WHERE PhysicalAddress = '$($l.DeviceID)';
This part works great and I have no problem updating these columns. It updates multiple rows each time it runs.
I have an update trigger I am trying to put on this table, dbo.MobileLeases. When the Building column is updated in dbo.MobileLeases, I want to perform an INSERT into another table, dbo.LeaseAudit.
The insert into seems to work for the first update after the trigger is in place. After that, the scheduled UPDATE statement (from Powershell) stops working! Which is confusing to me, but here is the trigger...
CREATE TRIGGER [dbo].[trigger_LeaseAudit]
ON [dbo].[MobileLeases]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF UPDATE (Building)
BEGIN
INSERT INTO dbo.LeaseAudit(PhysicalAddress, DeviceName, OwnerName, Building, TimeOn, IPAddress)
SELECT
i.PhysicalAddress, i.DeviceName, i.OwnerName, i.Building,
i.TimeOn, i.IPAddress
FROM
MobileLeases AS m
INNER JOIN
inserted AS i on m.PhysicalAddress = i.PhysicalAddress
INNER JOIN
deleted AS d on m.PhysicalAddress = d.PhysicalAddress
WHERE
m.Building <> d.Building
END
END
As you can see I am trying to INSERT INTO dbo.LeaseAudit when (Building) is updated on dbo.MobileLeases, WHERE the old update value (d.Building) is different to the new value (m.Building).
I guess my biggest question is how is this trigger breaking the initial update query? With the trigger in place, the scheduled update statement stops working. When I remove the trigger, everything works again.
Thank you for any advice.
The answer is that the UPDATE transaction was failing as a whole.
The update trigger configured on tableA is designed to insert a new row on tableB, when tableA.column1 is updated.
The insert statement includes a column that was set as a PK on tableB. Obviously, the insert statement could not insert a duplicate value in the PK column, so the entire update was bombing.
The solution for me was to remove the PK attribute from tableB.PK. It is an audit table, so I should not miss it.
Thank you
I have a table which have 31 Columns
PrimaryField,PersNr, a1, a2, a3, a4, ...
in the a1 - a31 fields are values. The software generate automaticly each month one row.
The user can now from the software update a field or delete it (Update to NULL).
But now I need a Trigger which make an insert in a new table for each changed field. The Problem is the comparison from the before row and the changed row. How can I get the changed field when the User Update the Row?
Here is some code you can run that should work:
CREATE TRIGGER TriggerName --The name of your trigger
ON Table1 --The table where the update happens
AFTER UPDATE
AS
INERT INTO --you new tabe namegoes here
()--your new table fields go here
SELECT --only the fields you have in the parenthesis above in the same order
FROM Inerted I
The way most triggers are is that as soon as a value is inseted updated or deleted the trigger has a special vlaue in this case fr update and inserted actions the table is called Inserted and that will grab the most current value entered.
In a SQL Server trigger, you have access to Inserted and Deleted pseudo tables; those keep the data that has been modified: the first one stores the new values, the second the old ones.
CREATE TRIGGER [dbo].[MyTableUpdate
ON [dbo].[MyTable]
AFTER UPDATE
AS
IF ##ROWCOUNT = 0
RETURN
IF NOT UPDATE (a1) AND NOT UPDATE(a2) AND NOT UPDATE(a3) AND NOT UPDATE...
RETURN
SELECT YourCOlumns FROM Inserted
SELECT YourColumsn FROM Deleted
--Compare the values as you prefer
Thank you all for helping me.
In the morning when I stand up I got an Idea :)
The point was:
1. DECLARE AND Set Variables:
DECLARE #ai1 VARCHAR(30);
DECLARE #ad1 VARCHAR(30);
....
SET #ai1 = (SELECT a1 from inserted);
SET #ad1 = (SELECT a1 from deleted);
...
To Compare every #ai and #ad
IF NOT #ai1 = #ad1 OR (#ai1 IS NULL AND #ad1 IS NOT NULL) OR (#ai1 IS NOT NULL AND #ad1 IS NULL)
BEGIN
... Insert...
END
IF NOT #ai2 = #ad2...
Thats the Trick. But thank you all very much!!!
DECLARE #IsDeleted AS BIT = 0;
SELECT #IsDeleted = IsDeleted from Updated
IF #IsDeleted=1
BEGIN
UPDATE Reviews
SET IsDeleted = #IsDeleted
WHERE CompanyID = 1
END;
I want to update Reviews if the Company IsDeleted column is updated to 1.
How to determine is updating value is '1'
It's error SELECT #IsDeleted = IsDeleted from Updated
The pseudoatables available in a trigger are inserted or deleted, there is no updated table.
Since you stated t-sql, you should understand that in SQL server triggers operate on batches not single rows. This trigger needs to be completely rewriten to handle multiple record updates. Do not set anything to the value of a scalar variable in a trigger from the inserted or deleted tables. JOin to them instead (and of course when you are updating, don't ever write an update statement without excluding any records where the current value matches the new value. Silly to update a million records when only 2 have changed. Performance is critical ina trigger, so it is even more important there not to update things that don't need updating.