I'm fairly new in T-SQL but i have a question concerning triggers. I have written a trigger that is used in my stored procedure witch inserts and updates data.
Now the trigger works when any row is updated in table [dbo].[users]
I don't know how to modify this trigger so that if data is inserted into [dbo].[users] table activate the trigger (this works now), but how to acheive an update scenario on the same trigger that if only a specific row is UPDATED in [dbo].[users] table that only then the trigger should be activated.
For example
If a new user is inserted and all rows are inserted in this table - activate trigger
If an old trigger is updated but only a specific field in this table is updated (working_state is the name of a column) then only should the trigger should be activated.
Source code what I have is shown here:
ALTER TRIGGER [dbo].[t_temp_triger_name]
ON [dbo].[users]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
DECLARE #working_state char(1),
#user_code char(11)
DECLARE zm CURSOR FOR
SELECT
working_state,
user_code
FROM
inserted
-- Added update statement that activates the trigger only when a specific -
-- column update is executed
UPDATE [dbo].[users]
SET working_state = 1
FROM [dbo].[users] U
INNER JOIN DELETED D ON U.user_code= D.user_code
WHERE U.working_state<> D.working_state
OPEN zm
FETCH NEXT FROM zm INTO #working_state, #user_code
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC [FNF].[dbo].[NamedProcedure]
#working_state, #user_code
FETCH NEXT FROM zm INTO #working_state, #user_code
END
CLOSE zm
DEALLOCATE zm
END
I have came up with a solution
Added this to my code after declaration
Everything works fine.
IF EXISTS (SELECT 1 FROM deleted)
BEGIN
DECLARE zm CURSOR FOR
SELECT
u.working_state,
u.user_code
FROM
inserted U
LEFT JOIN DELETED D ON U.user_code= D.user_code
WHERE U.working_state<> D.working_state
END
ELSE
BEGIN
DECLARE zm CURSOR FOR
SELECT
working_state,
user_code
FROM
inserted
END
Related
When i select a value in one table, the same value must populate the other table.
For example:
Please see image attached:
The problem i am having is when i update the first field stage (left hand side), to say Stage2, the uStage field stays the same.. It should update to Stage2 as well..
The Ustage table has the same stage values as the Stage table so I know it needs to select the corresponding Value however i have tried all sorts to get this to work but it doesnt want to update:
Here is my Trigger code:
USE [SKY]
GO
/****** Object: Trigger [dbo].[SetIT] Script Date: 2018/10/04 19:52:06 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[SetIT] ON [dbo].[AMGR_Opportunity_Tbl]
AFTER INSERT
AS
DECLARE #RecordId int
DECLARE #Stage varchar(750)
Declare #ID int
begin
--check to see if we have any records in the inserted set
IF EXISTS( SELECT * FROM inserted ) BEGIN
--set up the cursor that we use to iterate over the recordset
DECLARE I CURSOR LOCAL FAST_FORWARD FOR
SELECT Record_Id FROM Inserted;
OPEN I
FETCH NEXT FROM I INTO #RecordId;
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #Stage = STAGE FROM STAGE WHERE ID = #ID;
--insert the UDF value
INSERT INTO O_Ustage(Client_id,Contact_Number,O_Ustage)
SELECT Opp_Id, 0, #Stage
FROM inserted WHERE Record_Id = #RecordId AND Opp_Type = 0;
--update the current stage
UPDATE stage set stage=#stage WHERE
ID=#ID;
FETCH NEXT FROM I INTO #RecordId;
END
CLOSE I
DEALLOCATE I
END
END
GO
Please assist in helping me figure out how to update the USTAGE field.
Below is the data that is in the Stage table
If i change the field 'Stage' below, then 'Ustage' also needs to change.. At the moment it is not doing it:
The opportunity table has 50 columns and stage table 2 columns.
When I change the stage on the opportunity table to Stage2 then the Ustage value must also change to Stage2.. Both values need to correspond
Your trigger code has a lot of problems, but here's one that will definitely cause it not to work:
SELECT #Stage = STAGE FROM STAGE WHERE ID = #ID;
Prior to that line of code, you never populate #ID, so it's value will be NULL. Therefore you will get NULL for #Stage, which is what you later insert into the O_UStage column.
Maybe you meant to use #RecordId on that line instead of #ID?
I have 2 tables:
tblStock (PartNo, StockQty, StockLocation)
tblTransfer (PartNo, StockQty, FromLocation, ToLocation)
When I delete data in tblTransfer, the stock is reversed back to tblStock.
The insert trigger is working fine, but the delete trigger is not working.
Please see my delete trigger:
CREATE TRIGGER [dbo].[UpdateStock]
ON [dbo].[tblTransfer]
FOR DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON;
MERGE tblStock s
USING (SELECT d.PartNo, d.StockLocation
FROM Deleted d) MergeData ON s.PartNo = MergeData.PartNo
AND s.StockLocation = MergeData.FromLocation
WHEN MATCHED THEN
UPDATE SET
s.StockQty = s.StockQty - MergeData.StockQty
s.StockLocation = MergeData.ToLocation
END;
create trigger trg_test
on dbo.tbltransfer
after delete
as
begin
update tbl
set tbl.stockqnty=d.quantity, tbl.stocklocation=d.location
from
tblstock tbl
join
deleted d
on d.partno=tbl.partno
end
I've got a table like table1 (table1_id, col1, some_ID) and another table2 (table2_id, col1, table1_id)
I have a procedure
PROC deleteTable2(#id int)
AS
BEGIN
DELETE table2
WHERE table1_id = #id
END
I'm trying to create a trigger for delete for table1 table, so when I'm trying to delete from table1, the trigger will delete all records in table2.
I wrote the trigger like this:
CREATE TRIGGER deleteTable1 on table1
FOR DELETE
AS
BEGIN
SET NOCOUNT ON
DECLARE #id int = 0
SELECT #id = del.table1_ID from deleted as del
EXECUTE dbo.deleteTable2 #id
It works if there is only one record for some_ID, but if there are more records, it doesn't work. For example:
delete table1
where some_ID='some value'
This SQL query will delete all records in table1 and only first in table2.
It makes sense, so I made a trigger with CURSOR, something like
CREATE TRIGGER deleteTable1 ON table1
FOR DELETE
AS
BEGIN
DECLARE #id int = 0, #CURSOR cursor
SET #CURSOR = CURSOR scroll FOR
SELECT deleted.table1_id
FROM deleted
OPEN #CURSOR
FETCH NEXT FROM #CURSOR into #id
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE dbo.deleteTable2 #id
FETCH NEXT FROM #CURSOR into #id
END
CLOSE #CURSOR
END
But it doesn't work at all... Maybe I just missed something or I don't get some nuances. Thank you.
UPDATE
In fact, This 2 tables are in one db, but I've got 3rd table table3 (table3_id, col1, table2_id). And this table3 is in other db, and they connected by linked server. So when I call stored procedure as I'm trying, this stored procedure calls stored procedure (with cursor) to delete records in table3. That the exact reason why I'm trying to use stored procedure to delete records in table2.
Avoid using all these unnecessary cursors and stored procedure. Simply follow a set based approach and do the following inside your trigger.
CREATE TRIGGER deleteTable1 on table1
FOR DELETE
AS
BEGIN
SET NOCOUNT ON
Delete FROM Table2
WHERE Exists (SELECT 1
FROM deleted del
WHERE del.table1_ID = table2.Table1_ID)
END
UPDATE
Since you have mentioned you would like to delete records from a third table too which is some where on a linked server.
I would suggest you to use the same approach and just add another delete statement in inside the procedure for that third table on the linked server.
CREATE TRIGGER deleteTable1 on table1
FOR DELETE
AS
BEGIN
SET NOCOUNT ON
Delete FROM Table2
WHERE Exists (SELECT 1
FROM deleted del
WHERE del.table1_ID = table2.Table1_ID)
DELETE FROM t3
FROM [LinkedServerName].[DBName].[SchemaName].Table3 t3
WHERE Exists (SELECT 1
FROM deleted del
WHERE del.table1_ID = t3.Table1_ID)
END
CREATE TRIGGER deleteTable1 on table1
FOR DELETE
AS
BEGIN
SET NOCOUNT ON
DELETE FROM table2 WHERE table1_id IN (SELECT table1_id FROM deleted)
END
I setup the below trigger to save the old version of a modified row into an "Archive" table. It works fine as long as the commented part stays commented. Once I uncomment the UPDATE part, the trigger saves the row as it is AFTER the update, instead of BEFORE the update. What should I change ?
ALTER trigger [dbo].[TRG_AFTER_UPDATE_Test]
ON [dbo].[Test]
AFTER UPDATE
AS
BEGIN
INSERT INTO dbo.TestArchive(
[CustId]
,[CustName]
,[SomeValue]
,[Modified]
,[ModifiedBy]
)
SELECT s.[CustId]
,s.[CustName]
,s.[SomeValue]
,s.[Modified]
,s.[ModifiedBy]
FROM dbo.Test s
INNER JOIN deleted d ON s.CustId = d.CustId
--here is the part that makes the INSERT behave badly
/*
UPDATE dbo.Test
SET ModifiedBy = USER_NAME(),
Modified = GETDATE()
FROM dbo.Test s
INNER JOIN Inserted i ON s.CustId = i.CustId
*/
END
It looks like you have an infinite loop going on here with the update...
Your trigger is set to the update event of dbo.Test, and within that trigger you are updating the dbo.Test table on which the trigger is set, which in turn fires the update trigger ad infinitum....
I am not sure if it would work by disabling the trigger within itself before performing the update and then re-enabling it afterwards.
https://msdn.microsoft.com/en-us/library/ms189748.aspx
Try inserting the data from the deleted table and not the actual table. Change the columns in your select from s.[column name] to d.[column name].
I have the following trigger to avoid updating a certain column.
ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[MyTable]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF UPDATE(SomeID)
BEGIN
DECLARE #id INT,
#newSomeID INT,
#currentSomeID INT
SELECT #id = ID, #newSomeID = SomeID
FROM inserted
SELECT #currentSomeID = SomeID
FROM deleted
WHERE ID = #id
IF (#newSomeID <> #currentSomeID)
BEGIN
RAISERROR ('cannot change SomeID (source = [MyTrigger])', 16, 1)
ROLLBACK TRAN
END
RETURN
END
END
Since i'm selecting from inserted and deleted, will this work if someone updates the table using a where clause that encapsulates multiple rows? In other words is it possible for the inserted and deleted table to contain more than one row within the scope of my trigger?
Thanks...
why not use an instead of update trigger and just join to INSERTED and push in all the columns except the one you don't want to update? your approach does not take in account that multiple rows can be affected by an single UPDATE statement.
try something like this:
ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[MyTable]
INSTEAD OF UPDATE
AS
BEGIN
UPDATE m
SET col1=INSERTED.col1
,col2=INSERTED.col2
,col4=INSERTED.col4
FROM [dbo].[MyTable] m
INNER JOIN INSERTED i ON m.PK=i.PK
END
you could also try something like this:
ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[MyTable]
AFTER UPDATE
AS
BEGIN
IF EXISTS(SELECT 1 FROM INSERTED i INNER JOIN DELETED d ON i.PK=d.PK WHERE i.SomeID!=d.SomeID OR (i.SomeID IS NULL AND d.SomeID IS NOT NULL) OR (d.SomeID IS NULL AND i.SomeID IS NOT NULL))
BEGIN
RAISERROR ('cannot change SomeID (source = [MyTrigger])', 16, 1)
ROLLBACK TRAN
RETURN
END
END
This will work for multiple row updates. Also, if the "SomeID" is NOT NULL you can remove the two OR conditions in the IF EXISTS
You need to define a cursor in trigger and get all affected records in cursor and then process it.