sql trigger after insert,update same table - sql

My data is imported through the external system into the SQL Server.
I want to write a trigger that allows all data to be added to the database by default unless they have special conditions to be updated (they must be updated).
I mean, we should search on all input records and if we have some duplicate data (based on the above conditions), the insert operation should not be performed and only the new input data should replace the old data (Update Action).
Could you please help me?

Try this
CREATE TRIGGER TR_YourTable ON dbo.YourTable
FOR INSERT
AS
SET NOCOUNT ON;
DELETE tbl
FROM dbo.YourTable AS tbl
JOIN inserted ON inserted.ID = tbl.ID
WHERE inserted.ID in (select ID from dbo.YourTable);
GO

Related

Trigger works based on type of insert SQL Server

I am working in SQL Server Management Studio v18, and I have the following trigger:
CREATE TRIGGER [dbo].[update_surface]
ON [dbo].[my_table]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #surface_m2 REAL
SET #surface_m2 = (SELECT cast(round(CAST(Dimension1*Dimension2 as decimal)/ cast(1000000 as decimal),3,3) as decimal(10,3)) AS surface FROM my_table WHERE Surface_m2 IS NULL)
UPDATE dbo.my_table SET Surface_m2 = #surface_m2
END
I have two columns in my_table, which are Dimesion1 and Dimension2. I want that the trigger multiplies them, and set the result to other column in the same table, which is Surface_m2, whenever this column is null. The trigger does his function, but based on the type of insert I do:
If I insert a row in my_table by the graphic environment the trigger works as I wish. With each new row, Surface_m2 has his own result.
But if I insert by INSERT INTO my_table VALUES ().... (query) the trigger updates Surface_m2 column of all previous rows with the result of each new insert.
Why is the trigger working like that? Is there any other simple way to do what I am trying to do?
Thanks.
Insert trigger gives you actual values that are inserted in a special table called... "inserted".
So what you need to do is join this table against your main table and perform the logic needed, no variables required.
Something like this untested code
create trigger...
begin
UPDATE t
SET Surface_m2 = cast(round(CAST(t.Dimension1*t.Dimension2 as decimal)
/ cast(1000000 as decimal),3,3) as decimal(10,3))
from dbo.my_table t
inner join inserted i
ON i.ID = t.ID
WHERE t.Surface_m2 IS NULL
end
This begs the question though, why can't you just insert the Surface_m2 value yourself. Or even better, change Surface_m2 to be a computed column if it's always depends on Dimension1 and Dimension2

SQL Server 2005 'INSTEAD OF' DELETE Trigger - Not deleting Source Records

I created a trigger in SQL Server 2005 that inserts records into a history table whenever a deletion occurs in the source table. The records are getting inserted, but they are not getting deleted from the source table.
Here is my trigger:
CREATE TRIGGER TRG_EdiHistory
ON dbo.EDI10000
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
SET IDENTITY_INSERT dbo.EDI10500 ON;
INSERT INTO EDI10500(File_Id, Tp_Id, File_Name, File_Size, File_Data, Rec_Date, Content_Type, Update_Flag)
SELECT
File_Id, Tp_Id, File_Name, File_Size, File_Data,
Rec_Date, Content_Type, Update_Flag
FROM
DELETED
END
GO
I had to use an INSTEAD OF trigger because my tables contains Image type columns.
Please if anyone has any idea why this is happening.
Thank you.
* UPDATE *
CREATE TRIGGER TRG_EdiHistory
ON dbo.EDI10000
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
SET IDENTITY_INSERT dbo.EDI10500 ON;
INSERT INTO EDI10500 ([File_Id],Tp_Id,[File_Name],File_Size
,File_Data,Rec_Date,Content_Type,Update_Flag)
SELECT [File_Id], Tp_Id, [File_Name], File_Size, File_Data,
Rec_Date, Content_Type, Update_Flag
FROM DELETED
DELETE FROM dbo.EDI10000
WHERE EXISTS (SELECT 1
FROM DELETED
WHERE [FILE_ID] = dbo.EDI10000.[File_Id])
END
GO
Instead of Triggers , fire Instead of the triggering action. In your Case this instead of Trigger fires and Inserts data into your history table.
Note this trigger fires instead of the Delete command. So if you also want to Delete rows you will need to add Delete statement inside this trigger.
Having said this I think instead of Instead Trigger if you simply define an After Trigger with same definition as your this instead of trigger will makes things pretty simple. It will delete the rows from table and then insert rows into your history table as you are expecting it to work.
Update
Since you have mentioned you cannot use Image Data type inside an After Trigger in sql server 2005, I am not aware of this limitation. Well in this case you can simply add a delete statement inside your this existing instead of trigger.
DELETE FROM dbo.table
WHERE EXISTS (SELECT 1
FROM deleted
WHERE Pk_Column = table.PK_Column)
Instead of delete replaces the standard action of the DELETE statement.
http://technet.microsoft.com/en-us/library/ms191208(v=sql.105).aspx
Have a look here how to do what you really want:
How to create a before delete trigger in SQL Server?

Can SQL server instead of update force insert a new row

Is it possible in SQL Server 2008+ to force an UPDATE statement on table to be transformed into INSERT statement, thus creating new row with the old and updated columns?
Yes this is a typical scenario for an INSTEAD OF UPDATE TRIGGER.
Create the following trigger on your table and it will insert a row for each update made on your table. you can have a bit more logic inside your trigger but this is just a basic definition just to give you some idea.
Inside your INSTEAD of UPDATE trigger you will have access to two system tables Inserted and deleted.
Inserted table will hold new values for the row that was being updated by the Update statement.
Deleted table will hold Old values for the row that was being updated by the Update statement.
Demo Trigger
CREATE TRIGGER tr_Table_Instead_Of_Update
ON TABLE_NAME
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO TABLE_NAME(Col1, Col2 , Col3)
SELECT Col1, Col2 , Col3
FROM inserted
END

Forbid insert into table on certain conditions

I have a SQL Server 2008 database. There are three terminals connected to it (A, B, C). There is a table SampleTable in the database, which reacts to any terminal activity. Every time there is some activity on any terminal, logged on to this DB, the new row is inserted into SampleTable.
I want to redirect traffic from one (C) of the three terminals to write to table RealTable and not SampleTable, but I have to do this on DB layer since services that write terminal activity to DB are in Black Box.
I already have some triggers working on SampleTable with the redirecting logic, but the problem is that rows are still being inserted into SampleTable.
What is the cleanest solution for this. I am certain that deleting rows in an inserting trigger is bad, bad, bad.
Please help.
Edit:
Our current logic is something like this (this is pseudo code):
ALTER TRIGGER DiffByTerminal
ON SampleTable
AFTER INSERT
AS
DECLARE #ActionCode VARCHAR(3),
#ActionTime DATETIME,
#TerminalId INT
SELECT #ActionCode = ins.ActionCode,
#ActionTime = ins.ActionTime,
#TerminalId = ins.TerminalId
FROM inserted ins
IF(#TerminalId = 'C')
BEGIN
INSERT INTO RealTable
(
...
)
VALUES
(
#ActionCode,
#ActionTime,
#TerminalId
)
END
In order to "intercept" something before a row gets inserted into a table, you need an INSTEAD OF trigger, not an AFTER trigger. So you can drop your existing trigger (which also included flawed logic that assumed all inserts would be single-row) and create this INSTEAD OF trigger instead:
DROP TRIGGER DiffByTerminal;
GO
CREATE TRIGGER dbo.DiffByTerminal
ON dbo.SampleTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.RealTable(...) SELECT ActionCode, ActionTime, TerminalID
FROM inserted
WHERE TerminalID = 'C';
INSERT dbo.SampleTable(...) SELECT ActionCode, ActionTime, TerminalID
FROM inserted
WHERE TerminalID <> 'C';
END
GO
This will handle single-row inserts and multi-row inserts consisting of (a) only C (b) only non-C and (c) a mix.
One of the easiest solution for you is INSTEAD OF trigger. Simply stating, it's trigger that "fires" on very action you decide and lets you "override" the default behavior of the action.
You can override the INSERT, DELETE and UPDATE statements for specific table/view (you use it a lot with views that combine data from different tables and you want make the view insert-able) using INSTEAD OF trigger, where you can put your logic. inside the trigger you can then call again to INSERT when it's appropriate, and you don't have to worry about recursion - INSTEAD OF triggers won't apply on statements from inside the trigger code itself.
Enjoy.

two triggers on insert of same table

Here is one very interesting problem. I am using SQL Server 2008.
I have two triggers on one common table say 'CommonTable'. one trigger is on update and other one is on insert/update/delete.
In first trigger "Trigger1", I do the checks/rollback sometime change the new inserted value based on business logic.
here is sample code
-
CREATE TRIGGER [dbo].[Trigger1] ON [dbo].[CommonTable]
FOR UPDATE
UPDATE [CommonTable]
SET
[StatusCode] = 'New Value'
WHERE
[RecId] = 'rec id value'
In second trigger "Trigger2", I store the new inserted/deleted/updated value from 'CommonTable' table to another table 'CommonTable_History' for history tracking purpose.
here is sample code
-
CREATE TRIGGER [dbo].[Trigger2] ON [dbo].[CommonTable]
FOR INSERT, UPDATE, DELETE
--based on logic read the value from DELETED or INSERTED table and store in other table.
SELECT #RowData = (SELECT * FROM DELETED AS [CommonTable] WHERE [RecId] = #RowRecId FOR XML AUTO, BINARY BASE64 , ELEMENTS)
--and then insert #RowData in 'CommonTable_History' table.
With the help of 'sp_settriggerorder', I have set the order of execution of these triggers, so first "Trigger1" get executed and then "Trigger2".
Second trigger "Trigger2" works well for insert/delete values. It works fine for new inserted value if new inserted values has not been changed by first trigger "Trigger1".
But if in some cases, inserted values has been changed in "Trigger1". say [StatusCode] = 'New Value' and old values was 'Old Value' then "Trigger2" still store the 'Old Value' instead of 'New Value'.
Why because "Trigger1" change the value but that value still has not been store in database and before that "Trigger2" get executed on Insert.
Now my requirement is, here I want to store "New Value".
So I thought, lets make "Trigger2" to use "AFTER" keywords. But "FOR" and "AFTER" behave same could not solve the problem.
Then I thought, lets make "Trigger2" to use "INSTEAD OF" keyword. But "INSTEAD OF" gives following error
"Cannot CREATE INSTEAD OF DELETE or INSTEAD OF UPDATE TRIGGER. This is because the table has a FOREIGN KEY with cascading DELETE or UPDATE."
I can not remove FOREIGN KEY with cascading DELETE or UPDATE for table 'CommonTable'.
Please let me know if you people have any other alternate solution.
-Vikram Gehlot
I think your second trigger needs to use the values from the actual table, not the inserted/deleted tables to populate the log table - inserted/deleted will always have the unaltered, original values, while your altered values will appear in the table. Make the second trigger an "After" trigger, so you will not have to use the sp_settriggerorder. Like this, for example:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trg_Trig1]
ON [dbo].[TestTable]
FOR INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
update TestTable
set [value] = 10
where [value] = 25
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trg_Trig2]
ON [dbo].[TestTable]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
insert into log_TestTable
(id, description, [value])
select tt.id, tt.description, tt.[value]
from inserted i
LEFT JOIN TestTable tt
ON tt.id = i.id
END
It may not be the cleanest solution but can you simply combine the two triggers into one? That way both pieces of SQL would know about each other's changes.
Your second trigger appears to me as if it would not work properly is mulitple records are inserted in a set-based operations unloess you use a loop which is poor choice in a trigger. Fix that first!
Instead of select * from deleted, why not join the deleted or inserted table to the original table and take the values from there (except for the id value which you get from deleted or inserted, that should give you the most current values of all fileds and if you add other trigger logic later wil not break.