Trigger works based on type of insert SQL Server - sql

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

Related

sql trigger after insert,update same table

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

Create a trigger that updates a column in a table when a different column in the same table is updated - SQL

How to create a trigger that updates a column in a table when a different column in the same table is updated.
So far I have done the following which works when any new data is created. Its able to copy data from "Purchase Requisition" to "PO_Number" however when data has been modified in "Purchase Requisition" , no changes is made to "PO_Number" and the value becomes NULL. Any kind help will be seriously appreciated.
ALTER TRIGGER [dbo].[PO_Number_Trigger]
ON [dbo].[TheCat2]
AFTER INSERT
AS BEGIN
UPDATE dbo.TheCat2 SET PO_Number=(select Purchase_Requisition from inserted) where DocNo= (Select DocNo from inserted);
END
You need to add 'UPDATE' as well as insert to the trigger, otherwise it will only execute on new data, not updated data. Also added 'top 1' to the select statements from the inserted table to allow this to be 'safe' on batch updates, however it will only update 1 record.
ALTER TRIGGER [dbo].[PO_Number_Trigger]
ON [dbo].[TheCat2]
AFTER INSERT, UPDATE
AS BEGIN
UPDATE dbo.TheCat2 SET PO_Number=(select top 1 Purchase_Requisition from inserted) where DocNo= (Select top 1 DocNo from inserted);
END
This might do what you want:
Your trigger is altering all rows in TheCat2. Presumably, you only want to alter the new ones:
ALTER TRIGGER [dbo].[PO_Number_Trigger]
ON [dbo].[TheCat2] AFTER INSERT
AS
BEGIN
UPDATE tc
SET PO_Number = Purchase_Requisition
FROM dbo.TheCat2 tc JOIN
inserted i
on tc.DocNo = i.DocNo ;
END;
However, perhaps a computed column is sufficient for your purposes:
alter table add PO_Number as Purchase_Requisition;

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

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.

SQL Server 2005 triggers

I created a trigger on a table for updates. If any update happens, I want to store the old value in separate table. I am trying to get the old value from "inserted" table, but this table is not populated with old values after update.
Here is the sample code:
CREATE TRIGGER [dbo].[Logs_Update]
ON [dbo].[Logs] AFTER UPDATE
AS
DECLARE #url varchar(50)
BEGIN
SELECT #url = i.url
FROM INSERTED i
INSERT INTO dbo.Triggers_tbl
(ID, URL)
VALUES
(1000, #url)
END
I get #url as null from the inserted table.
Please let me know what is wrong with the trigger
The DELETED table contains the "old" values and the "INSERTED" table contains the "new" values.
To add to this, the INSERTED and DELETED tables may contain multiple rows if your update affects multiple rows and therefore you should probably run your insert with an INSERT SELECT rather than a variable.
INSERT INTO dbo.Triggers_tbl(URL) SELECT d.url FROM DELETED d
The "old" values (after an UPDATE) are available in the Deleted pseudo-table:
CREATE TRIGGER [dbo].[Logs_Update]
ON [dbo].[Logs]
AFTER UPDATE
AS
DECLARE #url varchar(50)
SELECT #url = d.url from deleted d
INSERT INTO dbo.Triggers_tbl(ID,URL) VALUES(1000,#url)
As HLGEM correctly commented on, the OP's code is assuming the trigger will be called for each row separately - which is not correct.
In that light, the trigger code really ought to deal with a set of rows being updated, and thus it should be something like:
CREATE TRIGGER [dbo].[Logs_Update]
ON [dbo].[Logs]
AFTER UPDATE
AS
INSERT INTO dbo.Triggers_tbl(ID,URL)
SELECT 1000, d.url
FROM Deleted d
or something like that.
The records that were updated are in the DELETED virtual table, not INSERTED. Change "from inserted" to "from deleted" and that should work.