Trigger to update foreign key field after insert on same table - sql

I have two tables:
Table1 (surveyid [PKID], surveyname)
Table2 (visitid [PKID], surveyname, surveyid [FKID - refers to Table1]).
After inserting a new row into Table2, I would like to update Table2.surveyid with the surveyid from Table1, based on matching surveyname.
I thought it maybe wasn't possible (or good practice?) to create a trigger to update the same table. But I seem to have created a trigger that will do this. The problem is that after insert, the trigger updates the surveyid for every row, instead of just the newly inserted rows.
This trigger code works, but how do I ensure the trigger only updates the surveyid for newly inserted rows, and not all rows?
CREATE TRIGGER tr_update_table2_fk
ON Table2
AFTER INSERT
AS
BEGIN
UPDATE Table2
SET surveyid = (SELECT t1.surveyid
FROM Table1 t1
WHERE t1.surveyname = Table2.surveyname)
END;

Thank you MattM and DaleK, you've helped me figure out the answer. I was adding the inserted table into the subquery where clause before, instead of the query where clause. This does the trick:
CREATE TRIGGER tr_update_table2_fk
on Table2
AFTER INSERT
AS
BEGIN
UPDATE Table2 SET
surveyid = (
SELECT t1.surveyid
FROM Table1 t1
WHERE t1.surveyname = Table2.surveyname
)
WHERE Table2.visitid IN (SELECT visitid FROM inserted)
END;

Yes, the inserted table is the answer.
I'd use it to capture the visitids of the inserted rows, then filter by them in a WHERE clause on the end of your UPDATE statement in your trigger.
E.g.
CREATE OR ALTER TRIGGER tr_update_table2_fk
ON Table2
AFTER INSERT
AS
BEGIN
DROP TABLE IF EXISTS #VisitIds ;
CREATE TABLE #VisitIds ( [id] INT ) ;
INSERT INTO #VisitIds ( [id] )
SELECT [visitid]
FROM inserted ;
UPDATE Table2
SET [surveyid] =
(
SELECT t1.[surveyid]
FROM Table1 AS t1
WHERE t1.[surveyname] = Table2.[surveyname]
)
WHERE [visitid] IN ( SELECT [id] FROM #VisitIds ) ;
END
GO

Related

how to see difference between 2 tables

I have 2 tables: 1 temp and the other one is my main table.
Each day I would update my temp table and I want to update my main table based on the changes I made from the temp table.
Example: The first temp table contains an id and name. Then I insert the value from temp into the main table. But when I made changes from my temp like insert another id and name, I want my main table to compare and only insert the unique id from the temp table.
As you said, it seems like you have a table object named as temp table. If this is the case then you may use after insert trigger on temp table to insert new inserted value in your main table.
CREATE TRIGGER AfterINSERTTrigger on [Temptable]
FOR INSERT
AS DECLARE #id INT,
#col1 VARCHAR(50),
.
.
SELECT #id = ins.id FROM INSERTED ins;
SELECT #col1 = ins.col1 FROM INSERTED ins;
.
.
INSERT INTO [MainTable](
[id]
,[col1]
.
.)
VALUES (#id,
#col1,
.
.
.
);
PRINT 'We Successfully Fired the AFTER INSERT Triggers in SQL Server.'
GO
Similarly you can update your table on update of record in temptable using update trigger. You may find this link on more info on trigger. LINK
OR
If you are creating temp table object to get the new inserted record then use simple not in or not exists clause to get the newly inserted record.
Using NOT IN
insert into maintable ( id, col1, ...)
select Id , col1, .... from temptable
where id not in (select id from maintable)
Using NOT EXISTS
insert into maintable ( id, col1, ... )
select id, col1, ... from temptable as temp
where not exists (select id from maintable as main where main.id=temp.id)
You can use NOT EXISTS as follows
INSERT into main_table(
id, name,
...
)
SELECT
id,name,
...
FROM temp_table t
WHERE
NOT EXISTS(
SELECT 1
FROM main_table m
WHERE m.id = t.id
)
Cheers!!

How to optimize a trigger?

CREATE TRIGGER T
ON TABLE_2
AFTER INSERT
AS
DECLARE #bought_t int,
#name_t varchar(20)
SELECT #name_t = name_t
FROM inserted
SELECT #bought_t = bought_t
FROM TABLE_1
WHERE name_t = #name_t
IF #bought_t < 100
BEGIN
UPDATE TABLE_1
SET bought_t = #bought_t + 1
WHERE TABLE_1.name_t = #name_t
END
ELSE
ROLLBACK TRANSACTION
The column (TABLE_1) I'm making the update to after the insert happens in the 'TABLE_2' is supposed to hold values between 50 and 100. So I'm asking If this trigger is as professional and optimized as It could be? or I have some flaws that could lead to bugs/security issues.
Basically, you need to completely rewrite your trigger to be set-based and to be able to work with multiple rows in the Inserted pseudo table.
Fortunately, that also makes it easier - in my opinion - try something like this:
CREATE TRIGGER T
ON TABLE_2
AFTER INSERT
AS
UPDATE T1
SET bought_t = bought_t + 1
FROM TABLE_1 T1
INNER JOIN Inserted i ON i.name_t = T1.name_t
WHERE T1.bought_t < 100
UPDATE: demo to prove this works:
-- create the two tables
CREATE TABLE TABLE_2 (ID INT NOT NULL IDENTITY(1,1), ProdName VARCHAR(50))
CREATE TABLE TABLE_1 (ProdName VARCHAR(50), Bought INT)
GO
-- create trigger on "TABLE_2" to update "TABLE_1"
CREATE TRIGGER T2Insert
ON TABLE_2
AFTER INSERT
AS
UPDATE T1
SET Bought = Bought + 1
FROM TABLE_1 T1
INNER JOIN Inserted i ON T1.ProdName = i.ProdName
WHERE T1.Bought < 100
GO
-- initialize TABLE_1 with some seed data
INSERT INTO dbo.TABLE_1 (ProdName, Bought)
VALUES ( 'Prod1', 0), ('Prod2', 20), ('Prod3', 40), ('Prod4', 40), ('Prod100', 100)
-- insert new values into TABLE_2
INSERT INTO dbo.TABLE_2 (ProdName)
VALUES ('Prod1'), ('Prod100'), ('Prod2'), ('Prod4')
-- get data to check
SELECT * FROM dbo.TABLE_1
This renders output:
As you can easily see:
Prod1, Prod2, Prod4 that were inserted caused an update of the value Bought
Prod100 which was also inserted did not cause an update of Bought
UPDATE #2: if you need to be able to insert multiple identical values at once, you need to slightly enhance your trigger like this:
CREATE TRIGGER T2Insert
ON TABLE_2
AFTER INSERT
AS
-- declare table variable to hold names and update counts
DECLARE #UpdateCount TABLE (Name VARCHAR(50), UpdCount INT)
-- from the "Inserted" table, determine which names are being
-- inserted how many times using GROUP BY
INSERT INTO #UpdateCount (Name, UpdCount)
SELECT ProdName, COUNT(*)
FROM Inserted
GROUP BY ProdName
-- now join to this temporary table, and update as many times
-- as needed (instead of +1 for all cases)
UPDATE T1
SET Bought = Bought + uc.UpdCount
FROM TABLE_1 T1
INNER JOIN #UpdateCount uc ON uc.Name = T1.ProdName
WHERE T1.Bought < 100
GO

Delete From Where In Select, will no results in the select delete anything?

I have some Delete statements in a stored procedure to delete some child records in other tables, and eventually delete the ID passed to the stored procedure.
I'm concerned about what happens if one of the select statements used with the Delete returns nothing, will this delete anything on that table?
Example
DELETE FROM [tblPurchases]
WHERE [ID] IN (SELECT [ID] FROM #PurchaseIDs)
In the case (from your example), when SELECT [ID] FROM #PurchaseIDs will not return anything, nothing will be deleted from tblPurchases because ID in (empty_set) condition will not be met.
by the way - you can easily check it by yourself, for example like this:
declare #t1 table (ID int)
insert into #t1 (ID)
select 1
union all
select 2
union all
select 3
declare #t2 table (ID int)
insert into #t2 (ID)
select 1
delete from #t1 where ID in (select ID from #t2 where ID > 1)
select * from #t1
Answer is no , nothing will be deleted. Nothing is "in" an empty collection, so nothing will be deleted.

Adding multiple values to a Table in SQL using a trigger and audit table

The issue I am having is when I insert more than one value into a table or delete a value that exists more than once in a table. I am unsure how to work around this issue.
`CREATE TRIGGER [dbo].[Q5Trigger]
ON [dbo].[WF]
AFTER INSERT, DELETE
AS
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'AuditTable')
BEGIN
CREATE TABLE [dbo].[AuditTable](
Word VARCHAR(100),
Frequency INT,
Date DATETIME,
Type VARCHAR(100)
)
END
IF EXISTS (SELECT * FROM inserted)
BEGIN
INSERT INTO AuditTable VALUES((SELECT Word FROM inserted),(SELECT Frequency FROM inserted), CURRENT_TIMESTAMP, 'Inserted')
END
IF EXISTS (SELECT * FROM deleted)
BEGIN
INSERT INTO AuditTable VALUES((SELECT Word FROM deleted),(SELECT Frequency FROM deleted), CURRENT_TIMESTAMP, 'Deleted')
END`
You misunderstand how triggers and INSERTED and DELETED work. When you insert 50 records into a table with a trigger, the trigger gets called once and the INSERTED table has 50 records in it. To do what you are doing, you must insert into the 1 table ALL the records of the other table.
INSERT INTO AuditTable (Word,Frequency,LogWhen,LogType)
SELECT Word,Frequency, CURRENT_TIMESTAMP, 'Inserted' FROM inserted
Your delete would be very similar to this.

My Trigger not work

I have trigger after insert in Table1 will insert in Table2 , after update columnA in Table1 will update in Table2 Status_Column where Table1.id=table2.id, after delete row in table1 will delete row in table2 where table1.id=table2.id.
create TRIGGER mytrigger
ON Table1
after UPDATE,INSERT,DELETE
AS
Begin
if update (columnA)
BEGIN
SET NOCOUNT ON;
declare #ID int
select #ID = ID from DELETED
UPDATE Table2
SET T2.Status_Column ='Updated'
From Table2 T2
inner join Table1 T1 on T1.ID = T2.ID
where T1.ID = #ID
End
else if exists (select * from inserted)
Begin
declare #table2_ID int
select #table2_ID = ID from inserted
insert into table2 (ID,Status_Column) values (#table2_ID,'New')
End
else if exists (select ID from deleted)
Begin
delete from table2
from table2 T2 , Deleted d
where t2.id= d.id
End
End
Only the delete statement working :(
Your fundamental flaw is that you seem to expect the trigger to be fired once per row - this is NOT the case in SQL Server. Instead, the trigger fires once per statement, and the pseudo table Deleted might contain multiple rows.
Given that that table might contain multiple rows - which one do you expect will be selected here??
select #ID = ID from DELETED
It's undefined - you might get the values from arbitrary rows in Deleted.
You need to rewrite your entire trigger with the knowledge the Deleted WILL contain multiple rows! You need to work with set-based operations - don't expect just a single row in Deleted !