I don't write T-SQL code too often so how do I capture updated row in a trigger?
If I create a trigger for INSERT I can ask for records from inserted as in
Create Trigger [dbo].[tr_test]
on table1
for INSERT
as
declare #id int
select #id = i.RecordId from inserted as I
...
How do I do that for UPDATE?
There are are two trigger or magic tables in sql server that is inserted or deleted table.
If you are creating trigger for Insert
- data comes in only inserted table in deleted table there is no records find
Create Trigger [dbo].[tr_test]
on table1
for INSERT
as
if exists (select 1 from inserted) and not exists(select 1 from deleted)
-- Action which would you want For Insert
If you are creating trigger for Update
- updated data comes in only inserted table and previous data comes in deleted table there is no records find
Create Trigger [dbo].[tr_test]
on table1
for UPDATE
as
if exists (select 1 from inserted) and exists(select 1 from deleted)
-- Action which would you want when your target table has been update
If you are creating trigger for Delete
- Deleted data comes in Deleted table and in case of deleting data it not comes inserted table.
Create Trigger [dbo].[tr_test]
on table1
after Delete
as
if exists (select 1 from deleted ) and not exists(select 1 from inserted)
-- Action which would you want when your target table has been delete
The same way as with an insert trigger. In an update trigger, inserted contains the new values, and deleted contains the old.
Quoth the docs:
The deleted table stores copies of the affected rows during DELETE and UPDATE statements. During the execution of a DELETE or UPDATE statement, rows are deleted from the trigger table and transferred to the deleted table. The deleted table and the trigger table ordinarily have no rows in common.
The inserted table stores copies of the affected rows during INSERT and UPDATE statements. During an insert or update transaction, new rows are added to both the inserted table and the trigger table. The rows in the inserted table are copies of the new rows in the trigger table.
An update transaction is similar to a delete operation followed by an insert operation; the old rows are copied to the deleted table first, and then the new rows are copied to the trigger table and to the inserted table.
Source: DML Triggers, Use the inserted and deleted Tables
Related
After searching many pages I still can't find the answer about re-inserting deleted rows in the same table - not another table.
I have a table named timetable with the primary key made up from 3 columns Schoolcode, Year, Term.
I need for some reason need to insert deleted rows into the same table.
I get the error
Violation of PRIMARY KEY constraint
with the following trigger
ALTER TRIGGER [dbo].[AFTER_delete_]
ON [dbo].timetable
AFTER delete
AS
BEGIN
IF EXISTS (SELECT * FROM deleted)
BEGIN
INSERT INTO timetable
SELECT *
FROM deleted A
WHERE NOT EXISTS (SELECT 1 FROM timetable B
WHERE B.Schoolcode = A.Schoolcode
AND B.Year = A.Year
AND B.Term = A.Term);
END
END
thanks any way.I test the code below and that did work.
ALTER TRIGGER [dbo].[Instead_OfDelSert_Status]
ON [dbo].[Status]
INSTEAD OF delete,insert
AS
BEGIN
PRINT 'You must disable or delete Trigger Instead_OfDelSert_Status to insert or
delete rows!'
END
I have two exactly same tables. My question: is there any way when i'm inserting something in first table automatically sql server to copy this row into another table.
I know that i can do it manually
select * into table1 from table2 where table2ID=#table2ID
But i'm wondering if i can create a table dynamically and set it when a row is inserted, copy row's data into another table also.
So with this way i dont need to run extra code to do that, sql will do this automatically
You can use a TRIGGER which is an object that you link to a table with a particular operation (INSERT, UPDATE, DELETE or any combination of those). The trigger's code will execute on each operation done to the linked table.
Basic example:
CREATE TRIGGER dbo.CopyToTable2 ON Table1
AFTER INSERT -- The trigger will execute after any insert done to Table1
AS
BEGIN
SET NOCOUNT ON
INSERT INTO Table2 (
Column1,
-- OtherColumns
)
SELECT
I.Column1
-- OtherColumns
FROM
inserted AS I -- "inserted" is a special table that references the trigger's tracking table for new or updated records
END
The use of a trigger is appropriate here. It might look something like this:
CREATE TRIGGER SomeTriggerName ON theSourceTable
FOR INSERT
AS
INSERT INTO DestinationTable
(column1, someothercolumn)
SELECT (column1, someothercolumn)
FROM inserted
Surprisingly, I can't find an answer to this on Google, although my terminology may be off. I also didn't see an explanation on MDSN.
Take the following code to perform a simple MERGE:
DECLARE #tbl_1 TABLE (ID int IDENTITY(1,1), person nvarchar(20));
DECLARE #tbl_2 TABLE (ID int IDENTITY(1,1), person nvarchar(20));
INSERT INTO #tbl_1 (person) VALUES ('Bob'),('Ted'),('Brian');
INSERT INTO #tbl_2 (person) VALUES ('Bob'),('Ted'),('Peter');
MERGE INTO
#tbl_2 as tgt
USING
#tbl_1 as src
ON
(tgt.person = src.person)
WHEN MATCHED THEN
UPDATE SET tgt.person = src.person
WHEN NOT MATCHED BY TARGET THEN
INSERT (person) VALUES (src.person)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
OUTPUT
$ACTION,
DELETED.ID,
DELETED.person AS PersonOld,
INSERTED.ID,
INSERTED.person AS PersonNew;
In the results, I see that the ID value of each row is shown against both INSERTED and DELETED rows, where an UPDATE has taken place:
Why is this please? I would expect DELETED.ID to be NULL after an update, with INSERTED.ID representing the UPSERTED row (I have worked with triggers in the past, and assumed MERGE would follow the same approach).
Because MERGE as you seem to be aware is an UPSERT (update + insert).
Now, the INSERTED tables record information for rows added by INSERT & UPDATE comands and the DELETED table contains information for rows that were either updated or deleted.
Taking a look at the MSDN documentation on how to "Use the inserted and deleted Tables":
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table. The
rows in the inserted table are copies of the new rows in the trigger
table.
and
An update transaction is similar to a delete operation followed by an
insert operation; the old rows are copied to the deleted table first,
and then the new rows are copied to the trigger table and to the
inserted table.
Update:
I saw you commented to your question that you realized that the operation is actually a delsert under the hood. And you might be thinking why would that be?
Think about how data is stored in SQL Server. It's stored on 8KB pages and when you update information in a column that is contained in a data page, the entire data page is being re-written, so essentially a delsert.
And the same thing with an INSERT, a new row will go in a data page (and might generate a page split - but that's another subject) and that entire data page will have to be re-written.
I am having one table with 3 f_Key and 1 P_Key with 6054 records.
One record is lost from that table. I am trying to insert record into that table.
The record id is 2352 and last record id is 9560 so,if i insert the record then it is taking 9561 id which is next id of before id.If try to delete the others records then because of F_Key it is not allowing to delete also.If i try to update the 9561 id then it also not allowing to update.
You can use the SET IDENTITY INSERT construct to explicitly insert the PK value in a table with auto-numbering, like so:
set identity_insert #your_table on
insert into your_table (PK_COL_IDENTITY, ...) values (2352, ...)
set identity_insert #your_table off
As per my knowledge , if your ID is auto incremented then you cannot update that ID(key) .The only way to do in your case is TRUNCATE.If you will truncate the table then it will allow to generate new sequence.
You can create a temporary table and migrate the data to temporary table and truncate that parent table and again migrate the data from temporary table to parent table.
Hope it will help you.
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