Identity of inserted/updated row in trigger - sql-server-2005

I have the following trigger but need ti find the identity of the row so I don't update all records in the table. How can I get the identity of the affected row?
BEGIN
UPDATE tb_Division SET LastModified = GetDate() WHERE "id of inserted/updated row"
END

Since a trigger in MS SQL Server does not distinguish between single-record and multi-record operations, you should JOIN the table with the INSERTED pseudo table or use a subselect:
UPDATE tb_Division
SET LastModified = GETDATE()
WHERE id IN (SELECT id FROM INSERTED)
id being the primary key column of your table.

Have you looked at the id of the inserted logical table? You have to be careful when using triggers, as a trigger may be operating on more than one row:
UPDATE tb_Division AS td
SET LastModified = GetDate()
FROM INSERTED AS i
WHERE td.id = = i.id
See here for more details, and from MSDN:
DML triggers use the deleted and inserted logical (conceptual) tables. They are structurally similar to the table on which the trigger is defined, that is, the table on which the user action is tried. The deleted and inserted tables hold the old values or new values of the rows that may be changed by the user action. For example, to retrieve all values in the deleted table, use:

Mind you - a trigger can deal with a ton of rows at once - you'll have to take that into account!
You need to join your table to be updated with the Inserted pseudo-column on that ID field:
UPDATE dbo.tb_Division
SET LastModified = GetDate()
FROM Inserted i
WHERE tb_Division.Id = i.Id
or something like that.

Related

How to write trigger to update row in another table?

I have two tables:
CREATE TABLE EventsCnfig
(
Id int,
InspectionId int,
Event int
);
And this:
CREATE TABLE Inspections
(
Id int,
IsRepaired Bit
);
InspectionId in EventsCnfig table is foreign key of Inspections table with relation of one to many.
Here is SQL plunker
I need to create trigger when any row in EventsCnfig table updated the value of the column Event to -1 or inserted new row with the Event value -1 the row in Inspections table with appropriate Id has to update IsRepaired value in column to 1(true).
How can I write the trigger to implement the desired logic?
I would write two triggers - one for UPDATE, another for INSERT - if you try to do this in a single trigger, the code gets messy because of the checks for "is this an INSERT or UPDATE operation?" etc. - don't do that....
AFTER UPDATE trigger:
CREATE TRIGGER dbo.TrgEventsConfigUpdate
ON dbo.EventsConfig
AFTER UPDATE
AS
UPDATE insp
SET IsRepaired = 1
FROM dbo.Inspections insp
INNER JOIN Inserted i ON i.InspectionId = insp.Id
INNER JOIN Deleted d ON d.Id = i.Id
WHERE i.[Event] = -1 AND d.[Event] <> -1
Basically, after an update, you need to look at the Inserted and Deleted pseudo tables which contain the updated rows - if the new row (after the update) has a value of -1, while the old row (before the update) did not --> then the column Event has been updated to -1 and thus the IsRepaired in the table Inspections needs to be set to 1 (true).
AFTER INSERT trigger:
CREATE TRIGGER dbo.TrgEventsConfigInsert
ON dbo.EventsConfig
AFTER INSERT
AS
UPDATE insp
SET IsRepaired = 1
FROM dbo.Inspections insp
INNER JOIN Inserted i ON i.InspectionId = insp.Id
WHERE i.[Event] = -1
Same idea - just a bit simpler, since there's no "old" row to compare to: if the column in the list of inserted rows has a value of -1, then update the Inspections table for those InspectionId values to be 1.
Here you can read all about triggers:
How to use update trigger to update another table?
https://msdn.microsoft.com/en-us/library/ms189799.aspx
http://www.sqlteam.com/article/an-introduction-to-triggers-part-i
There should be more than enough information to build the trigger by yourself.
If you have a "finished" trigger that does not work the way you want, you can post it here and the community will help you out.

Creating SQL trigger that only affects specific row

I have two tables created inventory and customer_sales. Individuals working in the warehouse can't have access to the customer_sales table.
So I added an additional field to inventory table called num_sales.
I want to create a trigger functions that whenever a new customer sale is added. It will correspond to the specific inventory_item that was sold's row and not the entire table.
This is what I have so far.
ALTER TABLE inventory
ADD num_sales INT
UPDATE movies SET num_sales = 0;
ALTER TABLE movies ALTER COLUMN num_rentals INT NOT NULL;
GO
CREATE TRIGGER tr_movies_num_rentals_add
ON customer_sales FOR INSERT
AS
BEGIN
UPDATE inventory
SET num_sales = num_sales + (SELECT sale_status_code FROM INSERTED)
WHERE 1 = (SELECT sale_status_code FROM INSERTED);
END;
Note:
sale_status_code values: 1=sold, 2=reserved, 3=lost.
UPDATE: I am using Microsoft SQL server management studio. I am newbie and this is a question I have for school.
First assume inserted willhave multiple rows. SQL server triggers do not operate row by row. YOu need to join to inserted not use a subquery. Next if you want this to happen on insert, update and delete, then you need to use more than an INSert trigger. And the delete trigger should use a different formula than inserted and updated becasue you will be subtracting the value from the deleted table from the inventory total.

Does my trigger updates all the records

I have written a simple trigger which sets a column value with the id column value.
This is my trigger.
CREATE TRIGGER SubSectionsPrioritytrigger
ON SubSections
AFTER INSERT
AS
UPDATE dbo.SubSections
SET Priority = Id
After writing this trigger that, does this updates all the records after each insert. Or the only created new row.
Could some one provide any info on this.
Thanks.
To Update just the inserted row, you can do this:
UPDATE dbo.SubSections
SET SubSections.Priority = SubSections.Id
FROM INSERTED
WHERE INSERTED.Id = dbo.SubSections.Id
It does exactly what you wrote
UPDATE dbo.SubSections
SET Priority = Id
Any row will be updated.
You can change this to
UPDATE dbo.SubSections
SET SubSections.Priority = Inserted.Id
FROM INSERTED
WHERE INSERTED.id = SubSections.id
this will affect only the inserted rows.
to update just the last inserted row to what ever value you want you need to specify that row id which makes that row unique, from other rows.
and in your trigger you have not specified a where clause which causes all rows in the table to be updated.
whenever a any operation/event occurs on the table which has trigger associated with it mean a new record is inserted/updated or deleted an magic table is created in memory of SQL Server and we can access that magic table with the keyword "Inserted" in case of insert or Update and "Deleted" in case of delete.
so your query should be like this.
UPDATE dbo.SubSections
SET SubSections.Priority = SubSections.Id
FROM INSERTED
WHERE INSERTED.Id = dbo.SubSections.Id

Insert trigger will not work properly

I am trying to create a database trigger that will update certain characters in a field for a table when a user inserts data into the table...
Ex.
ID EXCHANGE LEADRT
1 new L-3
2 new 3
3 new 5
So I would want to leave id 1 alone because the format for the LEADRT is correct but ids 2 and 3 are not.
CREATE TRIGGER triggerupdate ON PoleUnits FOR INSERT,
UPDATE AS
if not exists (select * from Poleunits where LEADRT like '%L-%')
update PoleUnits set LEADRT = STUFF (LEADRT, 1, 0,'L-');
Any ideas why I can't get this to work or better suggestions on how to accomplish this?
In insert and update triggers you have access to a specific table called inserted where the rows to be inserted/updated are held. Those are not real tables, they are just logical tables with the same structure as the table on which the trigger fired.
Your current logic works on the original table, thus working with all the existing data, but not with the data you are actually inserting, i.e. it will update everything except the data you actually want updated. Something like this could work:
CREATE TRIGGER triggerupdate ON PoleUnits
FOR INSERT, UPDATE AS
update PoleUnits
set LEADRT = STUFF (PoleUnits.LEADRT, 1, 0,'L-')
from PoleUnits
inner join inserted -- this is basically a self join
on PoleUnits.ID = inserted.ID
where PoleUnits.LEADRT not like '%L-%'
This will only update those rows in PoleUnits that are being inserted, and only if their LEADRT field is not in the L- format.

what is the correct syntax for creating a database trigger for insert, modify and delete

i have what seems like a basic scenario for a db trigger in SQL server and i am running into an issue.
i have table Users (id, name, phone, etc) and i have tables UsersHistory (id, user_id action, fields, timestamp)
i want a database trigger where anytime inserts, updates or deletes into Users, i want a new record created in UsersHistory with the user id and the action that was done (insert new, updated fields, deleted id. Basically an audit log table.
this is how far i got, but i can't figure out how to:
Get the id on modify and deletes and also
How to get a list of fields that have changed and the action that was committed (insert, delete, update)
CREATE TRIGGER Update_Users_History
ON Users
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- Insert statements for trigger here
insert into UsersHistory (user_id, [action], [fields], timestamp)
select max(id) as user_id, {action ??},{fields??} getdate() from Users)
END
GO
any suggestions?
The easiest might be to just simply create three triggers - one for each operation:
CREATE TRIGGER trgUserInsert
ON dbo.User AFTER INSERT
AS BEGIN
INSERT INTO dbo.UserHistory............
END
CREATE TRIGGER trgUserDelete
ON dbo.User AFTER DELETE
AS BEGIN
INSERT INTO dbo.UserHistory............
END
CREATE TRIGGER trgUserUpdate
ON dbo.User AFTER UPDATE
AS BEGIN
INSERT INTO dbo.UserHistory............
END
That way, things are simple and you easily understand what you're doing, plus it gives you the ability to turn off a trigger for a single operation, if you e.g. need to insert or delete a huge list of items.
Inside the trigger, you have two "pseudo-tables" - Inserted (for INSERT and UPDATE) and Deleted (for UPDATE and DELETE). These pseudo tables contain the values for the newly inserted values (or the updated ones in UPDATE), or the ones that were deleted (for DELETE) or have been updated (the old values, before the update, for the UPDATE operation).
You need to be aware that a trigger will be called once even if you update a huge number of rows, e.g. Inserted and Deleted will typically contain multiple rows.
As a sample, you could write a "AFTER INSERT" trigger like this (just guessing what your table structure might be....):
CREATE TRIGGER trgUserInsert
ON dbo.User AFTER INSERT
AS BEGIN
INSERT INTO
dbo.UserHistory(UserID, Action, DateTimeStamp, AuditMessage)
SELECT
i.UserID, 'INSERT', getdate(), 'User inserted into table'
FROM
Inserted i
END
You are looking for a way to find out which "action" this trigger caused? I don't see any way to do this - another reason to keep the three trigger separate. The only way to find this out would be to count the rows in the Inserted and Updated tables:
if both counts are larger than zero, it's an UPDATE
if the Inserted table has rows, but the Deleted does not, it's an INSERT
if the Inserted table has no rows, but the Deleted does, it's a DELETE
You're also looking for a "list of fields that were updated" - again, you won't have any simple solution, really. You could either just loop through the fields in the "Users" table that are of interest, and check
IF UPDATE(fieldname) ......
but that gets a bit tedious.
Or you could use the COLUMNS_UPDATED() function - this however doesn't give you a nice list of column names, but a VARBINARY in which each column is basically one bit, and if it's turned on, that column was updated. Not very easy to use.....
If you really want to create a single, big trigger, this could serve as a basis - it detects what operation has caused the trigger to fire, and will insert entries into your User_History table:
CREATE TRIGGER trgUser_Universal
ON dbo.Users
AFTER INSERT, UPDATE, DELETE
AS BEGIN
DECLARE #InsHasRows BIT = 0
DECLARE #DelHasRows BIT = 0
IF EXISTS(SELECT TOP 1 * FROM INSERTED)
SET #InsHasRows = 1
IF EXISTS(SELECT TOP 1 * FROM DELETED)
SET #DelHasRows = 1
DECLARE #TriggerAction VARCHAR(20)
IF #InsHasRows = 1 AND #DelHasRows = 1
SET #TriggerAction = 'UPDATE'
ELSE
IF #InsHasRows = 1
SET #TriggerAction = 'INSERT'
ELSE
SET #TriggerAction = 'DELETE'
IF #InsHasRows = 1
INSERT INTO dbo.UsersHistory(user_id, [action], [fields], timestamp)
SELECT i.UserId, #TriggerAction, null, getdate()
FROM INSERTED i
ELSE
INSERT INTO dbo.UsersHistory(user_id, [action], [fields], timestamp)
SELECT d.UserId, #TriggerAction, null, getdate()
FROM DELETED d
END
I haven't included the figuring out which fields have been updated part just yet - that's left as an exercise to the reader :-)
Does that help at all?
There are two "tables" that are used in the trigger. One is DELETED and one is INSERTED. When you delete a row, that row is captured in the DELETED table. When you insert a row, that row is captured in the INSERTED table. When you update a row, the old row is in the DELETED table, and the new row is in the INSERTED table. The DELETED and INSERTED tables have the same schema as the table on which you are adding the trigger.
You might check out this solution that will create a query for you that will make all the auditing triggers you want, as well as the table in which to store the audits, excluding any selected tables. It will only do UPDATE triggers, but could easily be modified to make INSERT and DELETE triggers as well.