SQL Server trigger validation to insert or update - sql

I have to develop a trigger for multiple value update for the Change_Table that contains two columns Article_C (primary key) and Status_C. The trigger activates when the Status_C column is updated and it needs to either insert or update the Target_Tablewhich columns are Article_T (primary key) and Status_T.
The Status column for both tables is a NOT NULL int
For insert validation: insert all Change_Table values (Articles & Status) that do not exist into the Target_Table.
For update condition: if the articles exist in the Target_Table, pass the updated status value to that table.
Status values are 1 or 2.
My current implementation:
CREATE TRIGGER [dbo].[Article_Trigger]
ON [dbo].[Change_Table]
AFTER UPDATE
NOT FOR REPLICATION
AS
IF UPDATE (Status_C)
BEGIN
INSERT INTO Target_Table (Article_T, Status_T)
SELECT Article_C, Status_C
FROM Change_Table
WHERE NOT EXISTS (SELECT * FROM Target_Table
WHERE Change_Table.Article_C = Target_Table.Article_T
AND Change_Table.Status_C = Target_Table.Status_T)
END
This is a rough idea of the insert but it only works once when the Change_Table is first updated and the target table is empty. After that I get an error "Violation of PRIMARY KEY constraint" due to duplicate primary key after updating the status for a second time due to the insert condition.
Query that gave error after second execution:
update Change_Table set status_c = 1 where Article_C in (1000,1003)
How can I modify this query to implement the update status condition to this trigger?

I think you could use deleted or inserted tables. These 2 virtual tables are working nicely with trigger. In you example, you may not need deleted table as you are not logging the historical changes.
CREATE TRIGGER [dbo].[Article_Trigger]
ON [dbo].[Change_Table]
AFTER UPDATE
NOT FOR REPLICATION
AS
IF UPDATE (Status_C)
BEGIN
--insert if not exist
INSERT INTO Target_Table
SELECT A.Article_C, A.Status_C
FROM inserted as A --the updated records from the source table will exist in the inserted virtual table
LEFT JOIN Target_Table as B
ON B.Article_T = A.Article_C
WHERE B.Article_T IS NULL
--update if exist
UPDATE A
SET A.Status_T = B.Article_C
FROM Target_Table as A
INNER JOIN inserted as B --the updated records from the source table will exist in the inserted virtual table
ON B.Article_C = A.Article_T
END

Related

Deadlock on inserting a new row

I have a table that stores the keys of other tables for a many-to-many relationship. Example:
create table for_example(a uuid, b uuid, primary key(a, b));
Multiple transactions can insert data into a table at the same time. In this case, it may turn out that two transactions are inserting the same data. In this case, one of these transactions will see that the record already exists and will wait for the parallel transaction to complete its work in order to continue its work. But it may turn out that before the lock occurred, the second transaction had already inserted any data into this table, and in this case, if the first transaction tries to insert new data that the second transaction has already inserted, a deadlock will occur. Example:
begin;
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '002e202d-c564-45cd-a0a7-d8fe8b930d99' and b = '4ab9d0ed-80c7-42ee-a641-59834a61f06d';
--if not exists then insert
insert into for_example(a, b)
values('002e202d-c564-45cd-a0a7-d8fe8b930d99', '4ab9d0ed-80c7-42ee-a641-59834a61f06d')
ON CONFLICT DO NOTHING;
we pass at this time to the second transaction
begin;
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '4ab9d0ed-80c7-42ee-a641-59834a61f06d' and b = '002e202d-c564-45cd-a0a7-d8fe8b930d99';
--if not exists then insert
insert into for_example(a, b)
values('4ab9d0ed-80c7-42ee-a641-59834a61f06d', '002e202d-c564-45cd-a0a7-d8fe8b930d99')
ON CONFLICT DO NOTHING;
we return to the first transaction and try to insert the data that the second transaction has already inserted
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '4ab9d0ed-80c7-42ee-a641-59834a61f06d' and b = '002e202d-c564-45cd-a0a7-d8fe8b930d99';
--if not exists then insert
insert into for_example(a, b)
values('4ab9d0ed-80c7-42ee-a641-59834a61f06d', '002e202d-c564-45cd-a0a7-d8fe8b930d99')
ON CONFLICT DO NOTHING;
blocking occurs, we are waiting for the second transaction to finish its work, but at this time the second transaction is trying to insert data that the first transaction has already inserted
--check exists row in table before insert in condition "IF"
select 1 from for_example where a = '002e202d-c564-45cd-a0a7-d8fe8b930d99' and b = '4ab9d0ed-80c7-42ee-a641-59834a61f06d';
--if not exists then insert
insert into for_example(a, b)
values('002e202d-c564-45cd-a0a7-d8fe8b930d99', '4ab9d0ed-80c7-42ee-a641-59834a61f06d')
ON CONFLICT DO NOTHING;
error occurs: [40P01] ERROR: deadlock detected.
Is it possible to somehow check for the existence of a row in a table with the condition that it may already have been inserted by a parallel transaction?
This is a real issue we're having on a production server right now.

I want to make a table audit/shadow table only when the email column of my table is updated

when i update one row (email) the trigger sends all of the data to the shadow table I only want the one row that was updated what am i doing wrong in mt trigger code below?
CREATE TRIGGER AspNetUsersEmail_trigger
ON AspNetUsers
AFTER INSERT, UPDATE
AS
IF ( UPDATE (Email) )
BEGIN
INSERT INTO [dbo].[AspNetUserEmailAudit]([UserId],[UserName],[Email],[NormalizedEmail],[FirstName],[LastName])
SELECT Id,[UserName],[Email],[NormalizedEmail],[FirstName],[LastName] FROM AspNetUsers
END;
UPDATE [Elearn2].[dbo].[AspNetUsers]
SET Email = 'isaac#gmail.com'
WHERE [Id] = 'A1785377-E3BA-483A-8600-024CA5885951'
you should use inserted table:
CREATE TRIGGER AspNetUsersEmail_trigger
ON AspNetUsers
AFTER INSERT, UPDATE
AS
IF ( UPDATE (Email) )
BEGIN
INSERT INTO [dbo].[AspNetUserEmailAudit]([UserId],[UserName],[Email],[NormalizedEmail],[FirstName],[LastName])
SELECT Id,[UserName],[Email],[NormalizedEmail],[FirstName],[LastName]
FROM INSERTED
END;
read more about these special tables here
In MS SQL you have to select the data from the table inserted.
The tables deleted and inserted exists in triggers and contains the deleted and updated / inserted rows.

How to write trigger to insert and update another table

I want to write trigger. When there is an entry in table1, table2 should get inserted with same values. And in some field in table1 is updated then respective field must get updated in table2. How can I know whether value is inserted or updated?
I am using inserted to insert value.
Please guide me.
Here is how you will know whether a value is inserted or updated in a trigger like this:
on INSERT, the inserted dynamic table is populated with the new values
on UPDATE, the inserted dynamic table is populated with the new values of the records that were updated, and the deleted dynamic table is poulated with the old values of the records that were updated
So basically, if the deleted table contains the id (assuming you have an id column) as in the inserted table, you can be assured that it was an UPDATE that caused the trigger. If the deleted table is empty, conversely, it was an INSERT.
use this trigger to solve your problem..
create TRIGGER [dbo].[insert_Assets_Tran]
ON [dbo].[AssetMaster]
AFTER INSERT , UPDATE
AS BEGIN
DECLARE #isnum TINYINT;
SELECT #isnum = COUNT(*) FROM inserted;
IF (#isnum = 1)
INSERT INTO AssetTransaction
select [AssetId],[Brandname],[SrNo],[Modelno],[Processor],[Ram],[Hdd],[Display],[Os],[Office],[Purchasedt]
,[Expirydt],[Vendor],[VendorAMC],[Typename],[LocationName],[Empid],[CreatedBy],[CreatedOn],[ModifiedBy]
,[ModifiedOn],[Remark],[AssetStatus],[Category],[Oylstartdt],[Oylenddt],[Configuration]
,[AStatus],[Tassign]
FROM inserted;
ELSE
RAISERROR('some fields not supplied', 16, 1)
WITH SETERROR;
END

INSERT trigger to update a field that is NOT NULL in the same table

I have a situation where I need to merge two product tables into one and need to keep both id fields. The Id field is the pk and an identity column. On insert I want to update the prodId to match the Id of the newly inserted row. This is what I have but I get an error saying that I cannot insert null into ProductId. What am I doing wrong?
ALTER TRIGGER SyncId
ON Product
FOR INSERT
AS
BEGIN
DECLARE #ID INT
SET #ID = (SELECT ID FROM Inserted)
UPDATE Product SET
ProdId = #ID
WHERE
Id = #ID
END
You could create an INSTEAD OF trigger for the INSERT. You would then have to recreate the actual INSERT logic. You might need a view of the table for the trigger to sit on though... I don't recall if an INSERT within the trigger will work or not and can't test it right now.
Also, your trigger as written will only work if a single row is being updated. You should always write your triggers to be able to handle multiple rows in the INSERTED and DELETED tables.

SQL Trigger to update a field on insert if the inserted value conflicts with the unique constraint

I have a Table called Albums which contains a field IsDeleted and a field "Name".
Name is a unique key. My Question is:
Can I create a trigger which updates IsDeleted to False if it is set to True and the same name is inserted into the table by the user?
Of course the insert statement should somehow be changed to an update statement.
I'm Using MSSQL 2008 and I'm very new to triggers and SQL ingeneral
Thanks for your help!
Yes, you can do this with an instead of trigger. Example:
create trigger albums_ins_trig on albums instead of insert as begin
-- update albums which already exist and are being inserted again
update albums set IsDeleted=0 where IsDeleted=1
and exists (select * from inserted where albums.name=inserted.name)
-- insert new albums
insert into albums select * from inserted
where not exists (select * from albums where albums.id=inserted.id)
end