I have this delete trigger on an SQL database. The record deletes currently and gets written to an audit table. I have been asked to include in this history table a field from another table that is related to the record being deleted based on SurveyID. I thought I could do something like
select #Status = Status from table where Survey = deleted.Survey
But this is incorrect syntax.
ALTER trigger [dbo].[table_Selfdelete]
on [dbo].[table]
after delete
as
Begin
Set nocount on;
Declare #SurveyId int
Declare #StudentUIC varchar(10)
Declare #Status varchar(10)
select #SurveyId = deleted.SurveyID,
#StudentUIC = deleted.StudentUIC
from deleted
select #Status = Status from tbly when SurveyID = deleted.SurveyID
insert into fupSurveyAudit
values(#SurveyId,#StudentUIC,#Status)
End
Arrgh. I think you want this insert in your trigger (and nothing else):
insert into fupSurveyAudit(SurveyId, StudentUIC, status)
select d.SurveyId, d.StudentUIC, y.status
from deleted d left join
tbly y
on d.SurveyId = y.SurveyId;
Notes:
deleted could contain more than one row, so assuming that it has one row can lead to a run-time error or incorrect results.
A left join is needed in case there is no matching row for the status.
You should always include the columns in an insert
Your archive table should have additional columns, such as an identity column and the date of the insert, which are set automatically (and hence not explicitly part of the insert).
Triggers are fired once for each statement (Delete,insert,update) not for each row inside the statement.
You cannot use variables here because when multiple lines are deleted from the table only one line will be inserted in the Audit table because the variable can only hold one value.
You just need a simple insert from the deleted table into the Audit table something like this....
ALTER trigger [dbo].[table_Selfdelete]
on [dbo].[table]
after delete
as
Begin
Set nocount on;
insert into fupSurveyAudit(SurveyId, StudentUIC,[Status])
select d.SurveyID
,d.StudentUIC
,y.[Status]
from deleted d
INNER JOIN tbly y ON y.SurveyID = deleted.SurveyID
End
Try this
ALTER trigger [dbo].[table_Selfdelete]
on [dbo].[table]
after delete
as
Begin
Set nocount on;
insert into fupSurveyAudit -- Better listed the column list here
select
d.SurveyID, d.StudentUIC, y.Status
from
deleted d JOIN tbly y ON d.SurveyID = y.SurveyID
End
Related
I am new to sql triggers and learning as developing triggers for asp.net application.i am having case where I need to first save table and then edit the same table - this edit create new row in different table but the the problem is every time I edit the table it create new row in different table I want to create row in different table for only first edit.
Dividing my problem for readability.
I have two tables:
Table A and table B
I have written trigger on table A that add row in table B.
Problem:
Every time I edit row in table A a new row get added to table B. (So every edit create new row)
Required result:
I want my trigger to add ONLY one row in table B for the first edit in table A but not for subsequent edits.
I am using update triggers.
Any example with code would be great
Thanks you much in advance .
Create TRIGGER [dbo].[triggerName] ON [dbo].[databaseName]
For Update
As
Begin
DECLARE #i int
DECLARE #d int
DECLARE #action char(6)
DECLARE #Car VARCHAR(20)
IF (##ROWCOUNT = 0) RETURN
SELECT #i = Count(*) From Inserted
SELECT #d = Count(*) From Deleted
SELECT #action = CASE
WHEN (#i <> 0) and (#d <> 0) THEN 'UPDATE'
WHEN (#i = 0) and (#d <> 0) THEN 'DELETE'
WHEN (#i <> 0) and (#d = 0) THEN 'INSERT'
End
SELECT #Car = A From inserted
IF #action = 'UPDATE' AND #Car in ('BMW')
Begin
INSERT INTO Tableb (c,d,f)
Select c,d,f from inserted
End
Your trigger has some flaws in it.
First, You don't need to test if it was fired because of update, insert or delete. The trigger is specified for update, so inserts and deletes will not fire it anyway.
Second, SELECT #Car = A From inserted will raise an error whenever you update more then one row in the table.
Third, As you said, this will insert a record in tableB for every update, while you want it to insert a record only for the first update done (I assume one for the first update on any row, so if you update row 1 then insert, update row 2 then another insert, and update row 1 again don't insert).
I would write it like this:
Create TRIGGER [dbo].[triggerName] ON [dbo].[tableName]
For Update
As
Begin
INSERT INTO Tableb (c,d,f)
Select c,d,f
from inserted i
left join Tableb t ON(i.c = t.c and i.d = t.d and i.f = t.f)
where t.id is null -- or some other non-nullable column
and i.a = 'BMW'
End
You can modify your SQL trigger to execute only after INSERT
CREATE TRIGGER dbo.myTable_Insert
ON dbo.myTable
AFTER INSERT
AS
It is possible to create SQL trigger to run after insert, update or delete as seen in the referred tutorial
I am using SQL Server 2008. I want to create a trigger for update which will fire on update of user table.
Trigger functionality: replace user_tbl updated mobile number to user_work_tbl.
CREATE TRIGGER [dbo].[tr_User_Modified]
ON [dbo].[user_tbl]
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
DECLARE #MobileNo varchar(11)
IF UPDATE (mobile_no)
BEGIN
DECLARE #MobileNo VARCHAR(50)
SELECT #MobileNo = mobile_no
FROM [dbo].user_tbl
UPDATE [dbo].[user_work_tbl]
SET mobile_no = #MobileNo
WHERE [dbo].[user_work_tbl].mobile_no = #oldMobileNo // here I have a problem
END
END;
In the comment "here I have a problem" I need a mobile number which exists in user_tbl before update so that the only row of user_work_tbl gets updated.
Any suggestions to do this are also accepted.
Thanks for your all response
You need to join three tables together in your trigger - user_work_tbl, inserted and deleted. However, its not clear at the moment exactly what conditions are required:
CREATE TRIGGER [dbo].[tr_User_Modified]
ON [dbo].[user_tbl]
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
IF UPDATE (mobile_no)
BEGIN
UPDATE u
SET mobile_no=i.mobile_no
FROM user_work_tbl u
inner join
deleted d
on u.mobile_no = d.mobile_no
inner join
inserted i
on
i.PKCol = d.PKCol --What's the PK of user_tbl?
END
END;
inserted and deleted are pseudo-tables that contain the new and old rows that were affected by a particular statement, and have the same schema as the original table. They're only accessible from within the trigger.
Note, also, that the above trigger is correct, even when multiple rows are updated in user_tbl - provided you can correctly relate inserted and deleted in the final ON clause.
You can get the old phone number from the table deleted and the new one from inserted, but you should use user primary key the update the rows.
I have an SQL Trigger FOR INSERT, UPDATE I created which basically does the following:
Gets a LineID (PrimaryID for the table) and RegionID From the Inserted table and stores this in INT variables.
It then does a check on joining tables to find what the RegionID should be and if the RegionID is not equal what it should be from the Inserted table, then it should update that record.
CREATE TRIGGER [dbo].[TestTrigger]
ON [dbo].[PurchaseOrderLine]
FOR INSERT, UPDATE
AS
-- Find RegionID and PurchaseOrderLineID
DECLARE #RegionID AS INT
DECLARE #PurchaseOrderLineID AS INT
SELECT #RegionID = RegionID, #PurchaseOrderLineID = PurchaseOrderLineID FROM Inserted
-- Find PurchaserRegionID (if any) for the Inserted Line
DECLARE #PurchaserRegionID AS INT
SELECT #PurchaserRegionID = PurchaserRegionID
FROM
(...
) UpdateRegionTable
WHERE UpdateRegionTable.PurchaseOrderLineID = #PurchaseOrderLineID
-- Check to see if the PurchaserRegionID has a value
IF #PurchaserRegionID IS NOT NULL
BEGIN
-- If PurchaserRegionID has a value, compare it with the current RegionID of the Inserted PurchaseOrderLine, and if not equal then update it
IF #PurchaserRegionID <> #RegionID
BEGIN
UPDATE PurchaseOrderLine
SET RegionID = #PurchaserRegionID
WHERE PurchaseOrderLineID = #PurchaseOrderLineID
END
END
The problem I have is that it is not updating the record and I'm guessing, it is because the record hasn't been inserted yet into the PurchaseOrderLine table and I'm doing an update on that. But can you update the row which will be inserted from the Inserted table?
The major problem with your trigger is that it's written in assumption that you always get only one row in INSERTED virtual table.
SQL Server triggers are statement-triggers not row-triggers. You have to take that fact into consideration.
Now if I understand correctly the logic behind this trigger then you need just one update statement in it
CREATE TRIGGER TestTrigger ON PurchaseOrderLine
FOR INSERT, UPDATE
AS
UPDATE l
SET RegionID = u.PurchaserRegionID
FROM PurchaseOrderLine l JOIN INSERTED i
ON l.PurchaseOrderLineID = i.PurchaseOrderLineID JOIN
(
SELECT PurchaseOrderLineID, PurchaserRegionID
FROM UpdateRegionTable -- !!! change this for your proper subquery
) u ON l.PurchaseOrderLineID = u.PurchaseOrderLineID
For this example I've created a fake table UpdateRegionTable. You have to change it to the proper query that returns PurchaseOrderLineID, PurchaserRegionID (in your code you replaced it with ...). Make sure that it returns all necessary rows, not one.
Here is SQLFiddle demo
I think the problem could be that you are making the update to PurchaceOrderLine inside the trigger that is monitoring updates to the same table as well. Try to alter the trigger to just monitor the inserts, than if this works, you can make some changes or break your trigger on two: one for inserts, another for updates.
This has been resolved. I resolved the problem by adding the trigger to another table as the IF #PurchaserRegionID IS NOT NULL was always false.
Ok so i have a fairly basic trigger :
In words, After Insert, Get IP information and update the Inserted row with the new data
CREATE TRIGGER [BasicData.IPInfo.Gather]
ON [BasicData]
AFTER INSERT
AS
BEGIN
DECLARE #City VARCHAR(1000),
#Country VARCHAR(1000),
#IP VARCHAR(1000),
#ROWID UNIQUEIDENTIFIER
SELECT #IP=[IP],#ROWID=[ID] FROM [inserted]
SELECT #Country = [Country], #City= [City]
FROM [IPInfo] WHERE [IP] = #IP
IF (#City IS NOT NULL) AND (#Country IS NOT NULL)
BEGIN -- Never seems to fire
UPDATE [BasicData]
SET [IPCountry]=#Country,[IPCity]=#City
WHERE [ID] = #ROWID
END
ELSE
BEGIN -- Fired correctly
INSERT INTO [IPInfo.Missing] VALUES (#IP)
END
END
Now the problem is, It adds the missing IP information correctly (Only when missing), however, it does not seem to ever update the table when it does, What am i missing?
Ive tweaked it in every possible way i could think of... (My trigger knowledge is rather bad)
There are a few problems:
INSERTED can contain many rows. Your trigger allows for only one.
The trigger is AFTER INSERT, and I guess this might exclude UPDATEs. Try AFTER INSERT, UPDATE
Your IF statement isn't checking for an UPDATE - what if the column is UPDATED to NULL? it won't catch it even though it was an update. What if something inserted NON NULL data? it will think it was an UPDATE.
As far as I know the only way to identify an UPDATE is to join INSERTED and `DELETED on the PK. If there is a match, it's been updated.
Perhaps you could rewrite it like this:
CREATE TRIGGER [BasicData.IPInfo.Gather]
ON [BasicData]
AFTER INSERT, UPDATE
AS
BEGIN
-- Save UPDATES to BasicData
UPDATE [BasicData]
SET [IPCountry]=I.Country,[IPCity]=I.City
FROM [BasicData] UT
INNER JOIN
[inserted] I
ON I.ID = UT.ID
INNER JOIN
Deleted D
ON D.ID = I.ID
INNER JOIN
[IPInfo] IP
ON I.ID = IP.ID
-- Save inserts to Missing
INSERT INTO [IPInfo.Missing] (IP)
SELECT IP FROM
INSERTED I
WHERE NOT EXISTS (SELECT 1 FROM DELETED D WHERE D.ID = I.ID)
END
I've got a problem with a trigger that i can't figure out.
Assume i have two tables, Stu_Table2 & Stu_log. Stu_table2 has some columns, one of which is an automatically generated primary key [stu_id]. The link between the two tables is [stu_name]=[user_id]
The below code works fine for Updates & Deletions (as the primary key already exists). But i'm stuck on the insert - how can i insert the automatically generated primary key from stu_name to log table if it hasn't been generated yet?
Stu_name columns, [stu_id] [Stu_name] [Stu_class]
Stu_log columns, [user_id] [stu_name]
obviously this isn't a real world example, just testing proof of concept.
ALTER TRIGGER [dbo].[stu_testtrigger]
ON [dbo].[Stu_Table2] FOR INSERT, UPDATE, DELETE
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with caller queries SELECT statements.
-- If an update/insert/delete occurs on the main table, the number of records affected
-- should only be based on that table and not what records the triggers may/may not
-- select.
SET NOCOUNT ON;
--
-- Variables Needed for this Trigger
--
DECLARE #stu_ID int
DECLARE #stu_name varchar(15)
DECLARE #stu_class int
--
-- Determine if this is an INSERT,UPDATE, or DELETE Action
--
DECLARE #Action as char(1)
DECLARE #Count as int
SET #Action = 'I' -- Set Action to 'I'nsert by default.
SELECT #Count = COUNT(*) FROM DELETED
if #Count > 0
BEGIN
SET #Action = 'D' -- Set Action to 'D'eleted.
SELECT #Count = COUNT(*) FROM INSERTED
IF #Count > 0
SET #Action = 'U' -- Set Action to 'U'pdated.
END
if #Action = 'D'
-- This is a DELETE Record Action
--
BEGIN
SELECT #Stu_id =[stu_id]
,#Stu_name = [stu_name]
FROM DELETED
DELETE [dbo].[stu_log]
WHERE [user_id]=#stu_id
END
Else
BEGIN
--
-- Table INSERTED is common to both the INSERT, UPDATE trigger
--
SELECT #stu_id =[stu_id]
,#stu_name = [stu_name]
FROM INSERTED
if #Action = 'I'
-- This is an Insert Record Action
--
--THIS IS WHERE I'm STUCK i think!!!
BEGIN
INSERT INTO [stu_log]
([user_id]
,[description])
VALUES
(#stu_id
,#stu_name)
END
else
-- This is an Update Record Action
--
BEGIN
UPDATE [stu_log]
SET [user_id] = #stu_id
,[description] = #Stu_name
WHERE [user_id]=#stu_id
END
END
HELP!
Since you seem to want to carry out distinctly different actions for inserts, updates and deletes, I'm not sure why you're cramming all of the actions into a single trigger. I'd just have:
CREATE TRIGGER [dbo].[stu_testtrigger_I]
ON [dbo].[Stu_Table2] AFTER INSERT
AS
INSERT INTO stu_log ([user_id],[description])
SELECT stu_id,stu_name from inserted
GO
CREATE TRIGGER [dbo].[stu_testtrigger_D]
ON [dbo].[Stu_Table2] AFTER DELETE
AS
DELETE FROM stu_log WHERE [user_id] IN (
SELECT stu_id from deleted)
GO
CREATE TRIGGER [dbo].[stu_testtrigger_U]
ON [dbo].[Stu_Table2] AFTER UPDATE
AS
UPDATE l SET user_name = i.user_name
FROM
stu_log l
inner join
inserted i
on l.[user_id] = i.stu_id
GO
Notes:
This works for multi-row inserts, updates and deletes, which your original didn't
I've said AFTER instead of FOR, to make it clearer to you that these actions occur after any activity in Stu_Table2 has already occurred (e.g. the identity value has already been generated, which seems to be your concern).
You should note, however, that AFTER and FOR are synonymous. You'd only get different behaviour if we were doing an INSTEAD OF trigger.
I removed the pointless [user_id] = #stu_id setting from the UPDATE. Given the WHERE clause of this update (or my join equivalent, above), those two must already be equal.