Trigger that updates just the inserted row - sql-server

I'm trying to create a simple trigger using TSQL (or SQL Server 2008). The problem is: my current trigger is updating the entire table. This was fine for a while, but now the table has more than 20k rows. So I want a trigger that only updates the rows that are being inserted.
Here's my current simple trigger:
CREATE TRIGGER trig_MyPplUpdate
ON [Persons]
FOR INSERT
AS
Begin
Update Persons
set MyFile = NULL
where Len(MyFile) < 60
End
I think I'll have to use either the "inserted" table or the row_number function ordered by the primary key. Any ideas?

If it is necessary to use a trigger here at all I would use an INSTEAD OF trigger to adjust the values pre-insert and avoid the need to JOIN back onto the base table and Update them afterwards.
CREATE TRIGGER trig_MyPplUpdate
ON [Persons]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO Persons
SELECT foo,
bar,
CASE
WHEN Len(MyFile) >= 60 THEN MyFile
END
FROM Inserted
END

You need to join the Inserted pseudo table in your UPDATE statement. Always be aware that SQL Server fires the trigger once per statement and that statement can very well modify/insert multiple rows at once - so your Inserted table quite likely will contain more than one row - just need to keep that in mind when you write your trigger.
Try something like this:
CREATE TRIGGER trig_MyPplUpdate
ON [Persons]
FOR INSERT
AS
UPDATE dbo.Persons
SET MyFile = NULL
WHERE Len(MyFile) < 60
AND PersonID IN (SELECT DISTINCT PersonID FROM Inserted)
or use whatever unique column (your primary key) you have to get exactly those rows that have been inserted.

Related

T-SQL : create trigger to copy new columns from one table to another and increment no

I'm trying to create a trigger to copy newly added rows from one table on a different database but on the same server to another table and increment a column which only exists on the table where the new rows are being added. I'm not sure if the code is accurate and would like any feedback on how it can be improved.
CREATE TRIGGER AddReTncyTransStatement
ON [DBAdmin].[dbo].[ReTncyTransStatement]
AFTER UPDATE, INSERT
AS
BEGIN
INSERT INTO [DBAdmin].[dbo].[ReTncyTransStatement]
([ORG-CODE], [TNCY-SYS-REF], [TRANS-NO], [PROGRESS-RECID])
SELECT
[ORG-CODE],
[TNCY-SYS-REF],
[TRANS-NO],
[ANALYSIS-CODE] ``,
(SELECT MAX([PROGRESS-RECID])
FROM [DBAdmin].[dbo].[ReTncyTransStatement]) + 1 AS RECID
FROM
[SQLViewsPro2EOD].[dbo].[RE-TNCY-TRANS]
END;
I imagine that you have a table called [RE-TNCY-TRANS] which is in [SQLViewsPro2EOD] database and the other table which is [ReTncyTransStatement] that resides inside the [DBAdmin] database.
I also imagine that you want to insert a record into [ReTncyTransStatement] each time a record inserted into [RE-TNCY-TRANS]. So to achieve this you need to rewrite your trigger as below:
CREATE TRIGGER AddReTncyTransStatement
ON [SQLViewsPro2EOD].[dbo].[RE-TNCY-TRANS]
AFTER UPDATE, INSERT
AS
BEGIN
INSERT INTO [DBAdmin].[dbo].[ReTncyTransStatement]
(
[ORG-CODE],
[TNCY-SYS-REF],
[TRANS-NO],
[PROGRESS-RECID]
)
SELECT [ORG-CODE],
[TNCY-SYS-REF],
[TRANS-NO],
ISNULL((
SELECT MAX([PROGRESS-RECID]) FROM [DBAdmin].[dbo].[ReTncyTransStatement]
),0) + 1 AS RECID
FROM Inserted;
END;
Update
Why I used the ISNULL function?
Because at the first time, there is no record in [DBAdmin].[dbo].[ReTncyTransStatement] table, so the MAX([PROGRESS-RECID]) will be NULL. I used ISNULL to handle this situation.
Why I used the inserted?
According to the Microsoft docs:
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table. The
rows in the inserted table are copies of the new rows in the trigger
table.
Read more here:https://learn.microsoft.com/en-us/sql/relational-databases/triggers/use-the-inserted-and-deleted-tables?view=sql-server-2017

SQL Server trigger (before insert) to validate data?

We have an application that we use in the warehouse to track which pickers are picking which orders. We have an instance every so often where the the order number that is inserted into the table is not the actual order number. While we are going to put in some validation on the application, I was wondering how we could put a trigger in place on the Table to prevent writing the record when the order number is wrong.
For example:
Before insert, when len(ordernum) <> '6' then delete record.
else insert into Table1
?
INSTEAD OF triggers cause their source DML operation to skip and they just execute the code provided inside them. Actual insert, delete or update operation do not occur at all. However they have their associated inserted and deleted tables simulating the DML operation. Inserted and deleted tables are widely used in operations inside triggers
--Instead of Trigger
CREATE TRIGGER BeforeDelete_Trigger ON [dbo].[Order1]
INSTEAD OF INSERT AS
BEGIN
IF (SELECT * FROM Order1 WHERE len(ordernum) <> '6')
THEN
INSERT INTO Order1;
ELSE
INSERT INTO Order2 (col 1, col 2) VALUES (inserted.col1, inserted.col2);
END;
GO
you can create an instead of trigger:
--Create an INSTEAD OF INSERT trigger on the table:
CREATE TRIGGER InsteadTrigger on dbo.table1
INSTEAD OF INSERT
AS
BEGIN
if len(inserted.ordernum)= 6
insert into table1 (ordernum,col1, col2) values (inserted.ordernum,inserted.col1, inserted.col2)
--rows that dont have a ordernum length of 6 are not inserted
end

inserted table role in instead of insert trigger

I have a view based upon 2 tables, I have written an instead of insert trigger to insert into that view:
Create trigger tr_vWEmployeeDetails_InsteadOfInsert
on vWEmployeeDetails
Instead Of Insert
as
Begin
Declare #DeptId int
Select #DeptId = DeptId
from tblDepartment
join inserted on inserted.DeptName = tblDepartment.DeptName
if(#DeptId is null)
Begin
Raiserror('Invalid Department Name. Statement terminated', 16, 1)
return
End
Insert into tblEmployee(Id, Name, Gender, DepartmentId)
Select Id, Name, Gender, #DeptId
from inserted
End
The above code works fine, but I want to know that the insert statement to the view doesn't work, instead the trigger works, then from where do we get values in the 'inserted' magic table.
Please explain.
From the relevant MSDN page:
DML trigger statements use two special tables: the deleted table and the inserted tables. SQL Server automatically creates and manages these tables. You can use these temporary, memory-resident tables to test the effects of certain data modifications and to set conditions for DML trigger actions. You cannot directly modify the data in the tables or perform data definition language (DDL) operations on the tables, such as CREATE INDEX.
The insert on the view is blocked by the use of the instead of trigger - That's literally the meaning of "instead of".
Please note that your trigger code will fail if an insert statement to the view will try to insert multiple records. The reason for this is that in SQL Server, triggers are raised per statement, and not per row. The following code that is specified in the beginning of the trigger
Declare #DeptId int
Select #DeptId = DeptId
from tblDepartment
join inserted
on inserted.DeptName = tblDepartment.DeptName
Will fail once the inserted table will have multiple rows that matches the on clause if the join.
Triggers in SQL Server must always be written to handle multiple rows in the inserted / deleted table.

Mssql Instead of Update triggers null for values if no rows in view - Maybe Understanding Required

This is a logic question about Triggers I think.
We have a instead of update trigger on the view.
At present we have a webapp that runs a command like:
Update view set this=x, that = y where z='2323';
Sometimes the 2323 doesn't match a row in the view but it still runs the instead of update.
Alas the set values from "inserted" table are all NULL instead of x or y.
We have a little logic in the trigger that will also setup (insert) a new entry if it is not found. It also inserts another row in another table (Audit).
How do I solve this issue where - NO records returned in the view = No values passed into the instead of trigger?
If it build like that by design, then what are my options, I guess to do a select in the webapp, and if a row exists call update else insert trigger, but this seems overkill.
Any clues?
Martin
While the question is a bit vague, I can suggest to architect it other way around - create instead of insert trigger, and write your logic inside. This way you'll always have a record in the inserted table.
So if there's no record - you'll insert your data into underlying tables, otherwise - run an update.
create table test (name nvarchar(128) primary key, value int);
create trigger utr_test on test
instead of insert
as
begin
set nocount on
update test set
value = i.value
from test as t
inner join inserted as i on
i.name = t.name
insert into test (
name,
value
)
select
i.name,
i.value
from inserted as i
where
not exists (
select * from test as t where t.name = i.name
)
end
Then you can try this:
insert into test values ('Test', 1);
select * from test;
insert into test values('Test', 2);
select * from test;

Finding out which rows are inserted , updated or deleted using triggers

I have a table named indication in Database and it has three columns Name, Age, and Enable.
I want to create a trigger that inserts an alarm into my alarm table whenever the Age is under 18 and Enable is true, I want to check the record on the indication table at the exact moment that it has been inserted, that way I can check whether it should be inserted in alarm or not.
I found COLUMNS_UPDATED (Transact-SQL) on MSDN and it works for updated columns, is there the same thing for ROWS_UPDATED?
You can always set your trigger to respond to only an INSERT action, with
CREATE TRIGGER TR_Whatever_I ON dbo.YourTable FOR INSERT
AS
... (body of trigger)
Be aware FOR INSERT is the same as AFTER INSERT. You also have the option of INSTEAD OF, but with that you have to perform the data modification yourself. There is no BEFORE trigger in SQL Server.
In some cases it is very convenient to handle more than one action at once because the script for the different actions is similar--why write three triggers when you can write just one? So in the case where your trigger looks more like this:
CREATE TRIGGER TR_Whatever_IUD ON dbo.YourTable FOR INSERT, UPDATE, DELETE
AS
... (body of trigger)
Then you don't automatically know it was an insert in the body. In this case, you can detect whether it's an insert similar to this:
IF EXISTS (SELECT * FROM Inserted)
AND NOT EXISTS (SELECT * FROM Deleted) BEGIN
--It's an INSERT.
END
Or, if you want to determine which of the three DML operations it is:
DECLARE #DataOperation char(1);
SET #DataOperation =
CASE
WHEN NOT EXISTS (SELECT * FROM Inserted) THEN 'D'
WHEN NOT EXISTS (SELECT * FROM Deleted) THEN 'I'
ELSE 'U'
END
;
Triggers still run if a DML operation affects no rows (for example, UPDATE dbo.YourTable SET Column = '' WHERE 1 = 0). In this case, you can't tell whether it was an update, delete, or insert--but since no modification occurred, it doesn't matter.
A Special Note
It's worth mentioning that in SQL Server, triggers fire once per operation, NOT once per row. This means that the Inserted and Deleted meta-tables will have as many rows in them during trigger execution as there are rows affected by the operation. Be careful and don't write triggers that assume there will only be one row.
Firstly I think you have to increase your knowledge on the way triggers work, and what the different type of triggers are.
You can create a trigger like this
CREATE TRIGGER trg_Indication_Ins
ON Indication
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
Insert Alarms (column1, column2) Select value1, value2 from inserted where Age < 18 and Enable = 1
END
This should basically do what you are looking for, and from what I understand from your quesion.
UPDATE:
Basically triggers can fire on INSERT, UPDATE or DELETE or any combination of the three, You can also set it to fire 'FOR/AFTER' the event (of which both actually means AFTER), or INSTEAD OF the event. A trigger will always have "internal" or meta-tables on the event.
These tables are inserted and deleted
The inserted table is basically all the new records that is applied to the table, and the deleted table have all the records that will be removed. In the case of the UPDATE event, the inserted table will have all the new values and deleted will have all the old values.
The inserted table will be empty on a DELETE trigger, and the deleted table will be empty on an INSERT trigger
Triggers can affect performance drastically if not used properly, so use it wisely.

Resources