SQL Server - does trigger affects ##Rowcount? - sql

I have a query which do UPSERT or update if exists and insert if not:
update MyTable
set [Name]=#NewValue
where ID=#ID
If ##RowCount = 0
insert into MyTable([Name])
values(#Name)
Now, I wonder if the ##RowCount will be affected by a query executed in a trigger? Let us say in my trigger I have:
insert into MyLogs(Description)
values("Some description...")
If the update is successful in my first query, the trigger will run the insert to MyLogs which will have affected rows.

##ROWCOUNT is tied to the scope of your current execution and is therefore unaffected by a trigger, which would run in a different scope.

Related

Stored Procedure

I am using SQl-server 2012 and this code is inside in a Stored Procedure.
These are my SQL queries.I want to execute "Insert" query when "Update" query not executed.
If Update query executed,then insert query should not be executed.
Update tblStock Set Balance= Balance + #ReduceRawQty
Where LocCode=#LocCode
AND ItemCode=#rawitemcode
and CostPrice=#rawcostprice
Insert Into tblStock(LocCode, ItemCode, CostPrice, Balance,Transfer,PCode)
Values(#LocCode,#rawitemcode,#RawCostPrice,#RawQty,0,#PCode)
Is there any method to do this ,please help me
You can check ##ROWCOUNT to see how many rows were affected by the UPDATE statement:
UPDATE ...
IF ##ROWCOUNT = 0
INSERT ...
Alternatively you can use EXISTS to check for a row first and decide which operation to perform:
IF EXISTS (SELECT 1 FROM tblStock WHERE LocCode=#LocCode AND ItemCode=#rawitemcode and CostPrice=#rawcostprice)
UPDATE ...
ELSE
INSERT ...

Issue with SQL Server trigger event firing

I have a trigger on a table that is something like this:
ALTER TRIGGER [shoot_sms]
ON [dbo].[MyTable]
AFTER INSERT
AS
begin
declare #number bigint
declare #body varchar(50)
declare #flag int
select #number=number,#body=body,#flag=flag from inserted
if(#flag=0)
begin
insert into temptable (number,body,status)
select #number,#body,'P'
end
end
Now I am making two entries in mytable as below:
insert into mytable(number, body, flag)
values(3018440225, 'This is test', 0)
insert into mytable(number, body, flag)
values(3018440225, 'This is test', 0)
I execute these queries at a time, but for both of the queries the trigger fires only once and performs the task for the first query only.
How can I make it work for both insert statements?
Just an idea but put a GO statement between those two insert statements and that might cause the trigger to fire twice.
You should probably rewrite your trigger to handle multiple row inserts I think.
Here is your query converted. You should get two rows now.
ALTER TRIGGER [shoot_sms]
ON [dbo].[MyTable]
AFTER INSERT
AS
begin
insert into temptable (number,body,status)
select number,body,'P'
from inserted
where flag = 0
end
Also notice your trigger is much simpler now.
Since those two statements are in one SQL batch, the trigger will (by design) only fire once.
Triggers don't fire once per row - they fire once per statement! So if you have an INSERT or UPDATE statement that affects more than one row, your trigger will have more than one row in the Inserted (and possibly Deleted) pseudo tables.
The way you wrote this trigger is really not taking into account that Inserted could contain multiple rows - what row do you select from the Inserted table if you're inserting 20 rows at once?
select #number = number, #body = body, #flag = flag from inserted
You need to change your trigger to take that into account!

Purpose of checking ##rowcount = 0 in a trigger?

I've read a book For eg., Pro SQL Server 2008 Relation Database Design And Implementation Lois Davidson where I've found suggestion to check the ##rowcount inside the trigger: if it is = 0 then return:
if ##rowcount = 0 return
I'm wondering if the no row is modified how come trigger is fired?
The trigger fires for the statement being run. It will fire even if the table is empty, or if the statement affected no rows:
create table tr (i int);
go
create trigger g on tr after update
as
print 'foo'
go
update tr set i = 2
#Muflix Update:
create table tr (i int);
go
create trigger g on tr after insert
as
print 'foo'
go
insert into tr select * from tr;
go
As you see the trigger fires even if no rows were inserted.
Becuase Trigger won't be able to know how many rows are affected by the triggering event that is why you have to check inside.
Trigger is fired because triggering event has occurred. Trigger does not
check how many rows are affected. Therefore you've to check ##rowcount
inside trigger body. To fire a trigger, triggering event is important and
not the number of rows affected.
Also be aware that the behaviour of ##rowcount in a trigger when fired by a MERGE statement in SQL Server 2008 is not what is expected and is different.

Mixing update/insert records in a single invocation of "Instead of insert, update" - Is that possible?

I would like to know whether an "instead of insert, update" trigger can be called to both "UPDATE" A records in the table while "INSERTING" B records in a single invocation?
Actually, I'm just curious to know whether SQL Server could call the mentioned trigger for updating and inserting records, at once? or it just calls the trigger once for updating some records, and once for inserting some others?
Consider this, for example:
ALTER TRIGGER [dbo].[MyTable_DoWhatever]
ON [dbo].[MyTable]
INSTEAD OF INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Updating BIT = 0;
IF EXISTS(SELECT * FROM INSERTED i INNER JOIN DELETED d ON i.Id = d.Id) SET #Updating = 1
END
Does the above #Updating flag valid to determine whether the trigger is called for "INSTEAD OF INSERTED" or "INSTEAD OF UPDATE"?
Thanks.

How can I do a BEFORE UPDATED trigger with sql server?

I'm using Sqlserver express and I can't do before updated trigger. There's a other way to do that?
MSSQL does not support BEFORE triggers. The closest you have is INSTEAD OF triggers but their behavior is different to that of BEFORE triggers in MySQL.
You can learn more about them here, and note that INSTEAD OF triggers "Specifies that the trigger is executed instead of the triggering SQL statement, thus overriding the actions of the triggering statements." Thus, actions on the update may not take place if the trigger is not properly written/handled. Cascading actions are also affected.
You may instead want to use a different approach to what you are trying to achieve.
It is true that there aren't "before triggers" in MSSQL. However, you could still track the changes that were made on the table, by using the "inserted" and "deleted" tables together. When an update causes the trigger to fire, the "inserted" table stores the new values and the "deleted" table stores the old values. Once having this info, you could relatively easy simulate the "before trigger" behaviour.
Can't be sure if this applied to SQL Server Express, but you can still access the "before" data even if your trigger is happening AFTER the update. You need to read the data from either the deleted or inserted table that is created on the fly when the table is changed. This is essentially what #Stamen says, but I still needed to explore further to understand that (helpful!) answer.
The deleted table stores copies of the affected rows during DELETE and
UPDATE statements. During the execution of a DELETE or UPDATE
statement, rows are deleted from the trigger table and transferred to
the deleted table...
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...
https://msdn.microsoft.com/en-us/library/ms191300.aspx
So you can create your trigger to read data from one of those tables, e.g.
CREATE TRIGGER <TriggerName> ON <TableName>
AFTER UPDATE
AS
BEGIN
INSERT INTO <HistoryTable> ( <columns...>, DateChanged )
SELECT <columns...>, getdate()
FROM deleted;
END;
My example is based on the one here:
http://www.seemoredata.com/en/showthread.php?134-Example-of-BEFORE-UPDATE-trigger-in-Sql-Server-good-for-Type-2-dimension-table-updates
sql-server triggers
T-SQL supports only AFTER and INSTEAD OF triggers, it does not feature a BEFORE trigger, as found in some other RDBMSs.
I believe you will want to use an INSTEAD OF trigger.
All "normal" triggers in SQL Server are "AFTER ..." triggers. There are no "BEFORE ..." triggers.
To do something before an update, check out INSTEAD OF UPDATE Triggers.
To do a BEFORE UPDATE in SQL Server I use a trick. I do a false update of the record (UPDATE Table SET Field = Field), in such way I get the previous image of the record.
Remember that when you use an instead trigger, it will not commit the insert unless you specifically tell it to in the trigger. Instead of really means do this instead of what you normally do, so none of the normal insert actions would happen.
Full example:
CREATE TRIGGER [dbo].[trig_020_Original_010_010_Gamechanger]
ON [dbo].[T_Original]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Old_Gamechanger int;
DECLARE #New_Gamechanger int;
-- Insert statements for trigger here
SELECT #Old_Gamechanger = Gamechanger from DELETED;
SELECT #New_Gamechanger = Gamechanger from INSERTED;
IF #Old_Gamechanger != #New_Gamechanger
BEGIN
INSERT INTO [dbo].T_History(ChangeDate, Reason, Callcenter_ID, Old_Gamechanger, New_Gamechanger)
SELECT GETDATE(), 'Time for a change', Callcenter_ID, #Old_Gamechanger, #New_Gamechanger
FROM deleted
;
END
END
The updated or deleted values are stored in DELETED. we can get it by the below method in trigger
Full example,
CREATE TRIGGER PRODUCT_UPDATE ON PRODUCTS
FOR UPDATE
AS
BEGIN
DECLARE #PRODUCT_NAME_OLD VARCHAR(100)
DECLARE #PRODUCT_NAME_NEW VARCHAR(100)
SELECT #PRODUCT_NAME_OLD = product_name from DELETED
SELECT #PRODUCT_NAME_NEW = product_name from INSERTED
END