I have an INSERT trigger on table. When a row is inserted in table, the INSERT trigger is fired.
The trigger's behaviour is such that it will insert another row into the same table.
What would be the result of the INSERT statement?
Does this INSERT result in an infinite loop, or just the expected 2 inserts?
This is a setting in SQL- see the CREATE TRIGGER msdn page, specifically the section on Recursive Triggers. The setting you need to look into is RECURSIVE_TRIGGERS, if this is false, a trigger on Table1 will not trigger another insert into Table1. If you do allow recursive triggers, the limit is 32 levels deep.
Related
I have a problem described as follows: I have a table with one instead of insert trigger:
create table TMessage (ID int identity(1,1), dscp varchar(50))
GO
Alter trigger tr_tmessage on tmessage
instead of insert
as
--Set NoCount On
insert into tmessage
select dscp from inserted
GO
Alter proc P1
As
--Set NoCount On
insert into tmessage
(dscp)
values('some data')
Select SCOPE_IDENTITY()
GO
When I execute P1 it returns Null for SCOPE_IDENTITY() instead of the identity of the table. I even tried Output clause in the insert statement in the proc. but again the output table Identity field that gets filled from inserted in the Output clause is 0 in this case.
Any help would be appreciated.
Well, you've got yourself quite a pickle there.
From the one hand, you need the instead of insert trigger on your table, but from the other hand, you want to get the identity that this trigger generates back to the stored procedure that activated it.
Since there is no way to send parameters to and from triggers, you will have to do one of 3 things:
Find some way to eliminate the need for that instead of trigger.
This is my best recommendation.
Break your stored procedure to 2 parts: One part will do everything until the insert into statement (including it), thus activating the instead of insert trigger, and the other part that will do all operations needed after the trigger. this way you can use the scope_identity() inside the instead of insert trigger and send it's return value to the second stored procedure as a parameter.
Note: this design means you have to insert records one by one. should you try to insert more then one record, scope_identity() will only return the identity of the last row inserted by the trigger.
Find some way of passing data between the stored procedure and the instead of trigger. Since triggers can't except or return parameters, you will have to use either a temporary table or a regular table. This suggested solution is only suggested as a last resort, since it will complicate your code and probably cause some performance issues as well. Also, you will have to find a way to hold execution of the stored procedure until the instead of trigger will finish it's work. I can give you some pointers on how to share data between the procedure and the trigger, but I really suggest not to choose this solution.
SCOPE_IDENTITY() return the value from current scope and that is stored procedure outside from stored procedure it will be null.use ##identity to get last inserted identity.
Am facing a problem with trigger.
I created a trigger for a table like this
ALTER TRIGGER [dbo].[manageAttributes]
ON [dbo].[tr_levels]
AFTER insert
AS
BEGIN
set nocount on
declare #levelid int
select #levelid=levelid from inserted
insert into testtable(testid) values(#levelid)
-- Insert statements for trigger here
END
But when I insert rows into table tr_levels like this
insert int tr_levels (column1,colum2) values(1,2)
trigger triggered perfectly
But when I tried to insert into table as a bulk like this
insert int tr_levels (column1,colum2) values(1,2),(3,4),(5,6)..
Trigger doesnt fires for all the rows. It fires only one time for the first row. Is that bug with SQL or is there a solution to trigger the trigger for all rows insertion in a bulk insert query
No, it does fire for all rows - once - but you're ignoring the other rows by acting as if inserted only contains one. select #scalar_variable=column from inserted will arbitrarily retrieve a value from one of the rows and ignore the others. Write a set-based insert using inserted in a FROM clause
You need to treat inserted as a table that can contain 0, 1 or multiple rows. So, something like:
ALTER TRIGGER [dbo].[manageAttributes]
ON [dbo].[tr_levels]
AFTER insert
AS
BEGIN
set nocount on
insert into testtable(testid)
select levelid from inserted
END
You have the same issue many people have that: you think the trigger is fired per row. It is not - it is per operation. And inserted is a table. You take one (random) value and ignore the rest. Fix that and it will work.
Triggers fire once per statement in the base table. So if you insert 5 rows in one statement, the trigger fires once and inserted has the 5 rows.
i am working on sql server, where i want to insert the record in a particular table say (a), this table contains two column [id (Identity Field) and name(nvarchar(max)] now after the records is inserted in table (a), a trigger should fire and insert the identity field value in table b.... i am using after insert trigger for this purpose but i am not getting how i would be getting the identity field value in trigger... which should be inserted in table b.
This is what i am using
create trigger tri_inserts on (a)
after insert
as
begin
insert into b (id, name) values (?,?)
end
Please reply as soon as possible..
Thanks and Regards
Abbas Electricwala
create trigger tri_inserts on a
after insert
as
set nocount on
insert into b (id, name)
SELECT id, name FROM INSERTED
GO
#gbn has the best solution, but I want you to understand why the SELECT clause is better than using a VALUES clause in a trigger. Triggers fire for each batch of records inserted/updated/deleted. So the inserted pseudotable or the deleted pseudotable may have one record or they may have a million. The trigger has to be able able to handle either case. If you use a values clause, you only get the action happening for one of the records out the the million. This casues data integrity issues. If you decide to loop through the records in a cursor and use the VALUES clause, your performance will be horrible when you get a large number of records. When I came to this job, we had one such trigger, it took 45 minutes to insert a 40,000 record insert. Removing the cursor and using a set-based solution based on athe SELECT clause (Although a much more complex one than the example)reduced the time for the same insert to around 40 seconds.
Say I have an UPDATE trigger on tableA that inserts a new record into tableB.
CREATE TRIGGER insertIntoTableB
ON tableA
FOR UPDATE
AS
INSERT INTO tableB (...) VALUES (...)
GO
I then run these statements sequentially. Will the second UPDATE statement (UPDATE tableB) work OK? (i.e. wait for the trigger on table A to fully execute)
UPDATE tableA
SET ...
WHERE key = 'some key'
UPDATE tableB
SET ...
WHERE key = 'newly inserted key from trigger'
The behavior is subject to the nested triggers server configuration, see Using Nested Triggers:
Both DML and DDL triggers are nested
when a trigger performs an action that
initiates another trigger. These
actions can initiate other triggers,
and so on. DML and DDL triggers can be
nested up to 32 levels. You can
control whether AFTER triggers can be
nested through the nested triggers
server configuration option. INSTEAD
OF triggers (only DML triggers can be
INSTEAD OF triggers) can be nested
regardless of this setting.
When a trigger on table A fires and inside the trigger table B is updated, the trigger on table B runs immediately. The Table A trigger did not finish, it is blocked in waiting for the UPDATE statement to finish, which in turn waits for the Table B trigger to finish. However, the updates to table A have already occurred (assuming a normal AFTER trigger) and querying the Table A from the table B's trigger will see the updates.
If the updates are sequentially coded into the UPDATE trigger of A then yes.
I have created an INSTEAD OF INSERT trigger on a view in my database. I want to know which columns are included in the column list of the INSERT statement on the view.
If you read the MSDN documentation for triggers the UPDATE() and COLUMNS_UPDATED() functions should satisfy this requirement. However, during my testing I found that regardless of what columns are in the INSERT column list the UPDATE() and COLUMNS_UPDATED() functions always return all columns from the view.
CREATE VIEW dbo.MyView (BatchId, [Status], OrderNumber, WhenClosed) AS
SELECT bth.BatchId, bth.[Status], bth.OrderNumber,
Private.ufxAdjustDateTime(bth.WhenClosed, bth.WhenClosedUtcOffset)
FROM Private.Batch AS bth
GO
CREATE TRIGGER dbo.[MyView-Insert] ON dbo.MyView INSTEAD OF INSERT AS
BEGIN
SET NOCOUNT ON
DECLARE #batchIdIsSet BIT
SELECT #batchIdIsSet = 0
IF UPDATE(BatchId)
SELECT #batchIdIsSet = 1
INSERT INTO Private.Batch
(BatchId, [Status], OrderNumber)
SELECT CASE #batchIdSet
WHEN 1 THEN ins.BatchId
ELSE NEWID()
END, ins.[Status], ins.OrderNumber
FROM inserted AS ins
END
The reason I want to do this is that I need to modify an existing table and I have loads of legacy code that relies on it. So what I've done is created a new table, changed the old table to a view and created triggers, on the view, to allow INSERT, UPDATE and DELETE statements.
Now, the old table had defaults for certain columns, if the insert into the view uses the default I want to use a default in my insert into the new table. To do this I have to be able to figure out which columns had values [explicitly] supplied for the INSERT.
Checking to see if the column has NULL is not enough because the INSERT statement can explicitly set the field value to NULL and this is perfectly acceptable.
Hmmm, I hope this is clear.
Kep.
On an INSERT statement, every column is affected. It either gets NULL, or the value you're specifying.
Checking for NULL would be the best option, but as you can't do that, I'm thinking you might be a bit stuck. Can you work out scenarios which might need to handle NULL explicitly?
For INSERT, everything is a change because it's a new row.
If you had an AFTER trigger, you could test to see if the inserted value is the default value. If the default is NULL (eg nullable and no default), then how can you distinguish if NULL is explicitly inserted in any trigger?
In a BEFORE trigger, I don't know if you can trap the default. Of course, if the default is NEWID() this still won't help you.
On the face of it, this can't be done in a trigger.