Multi Row Update Trigger - sql

I've got this trigger:
CREATE TRIGGER tgr_passenger_flight
ON Flight
AFTER UPDATE
AS
BEGIN
IF ##ROWCOUNT= 0 RETURN
SET NOCOUNT ON
BEGIN TRY
IF EXISTS
(SELECT 1
FROM inserted I inner join PassengerForFlight PV ON I.flightnumber= PV.flightnumber)
BEGIN
;THROW 50000, 'Cannot update, Passenger is linked to flight',1
END
END TRY
BEGIN CATCH
;THROW
END CATCH
END
It works perfectly for one row update. But when i updated multi rows it failed.
What am I doing wrong? Do I have to change the join with PassengerForFlight? or must I also work with Deleted pseudo table because its a after update trigger?
Edit:
When i run this update-statement:
UPDATE Flight
SET gatecode = 'B'
WHERE flightcode = 'KL'
I've get the following message :
Thats correct, but this update statement touch two different flightnumbers. One thats not exist in PassengerForFlight and one that exists in PassengerForFlight.
What i want is:
1 row(s) affected and then the error message

There's nothing wrong with your JOIN. The problem is that you used IF EXISTS(). What you coded is: "if there is ANY row in the INSERTED table that exists in the PassengerForFlight table, then throw an error.
Since you're throwing an error, the trigger will rollback any transaction that it is participating in.
One way to do what you want is change this to an INSTEAD OF trigger, and to PRINT a message instead of throwing an error.
Your current code's philosophy is "let the UPDATE happen, and if certain condition exists, throw an error and roll it back." This can only happen in a way that affects the entire UPDATE (all rows).
I suggest a philosophy, of "Instead of the requested UPDATE, only UPDATE those rows that meet a certain condition. If any rows in the requested UPDATE didn't meet that condition, then give the user a message."

Related

If my for update trigger raise error, my update statement should fail

To ensure version control, I created a For Update trigger on my table. I have two tables. Account table, step one Second, the Account history table, which is utilized in the trigger, has a column called Version. If any of my columns are modified, I have Version+1 written in the column, and the old record from the Account table will be inserted in the Account history in the trigger. Additionally, I have a trigger new condition written. The newer version ought to be grated. version, If I run an update query on my main (Account) table to perform negative testing while keeping the older version, I get a trigger-defined error, but my update statement still updates the Account table, even though it shouldn't. I need to add transaction(BEGIN TRY BEGIN CATCH TRAN) on my update?, If my trigger fails my update statement should fail
ALTER TRIGGER tr_AccountHistory
ON account
FOR UPDATE
AS
BEGIN
SELECT old.column
FROM deleted
SELECT new.Version
FROM inserted
SELECT old.Version FROM deleted
IF #Old_Version >= #New_Version
BEGIN
RAISERROR ('Improper version information provided',16,1);
END
ELSE
BEGIN
INSERT INTO AccountHistory
(
insert column
)
VALUES
(
old.column
);
END
END
UPDATE account
SET id= 123456,
Version = 1
WHERE id =1
Instead of using RAISERROR, you should use THROW. This will respect XACT_ABORT and automatically rollback the transaction.
You also have other fatal flaws in your trigger:
It expects there to be exactly one row modified. It may be multiple or zero rows.
You have not declared any variables and are instead selecting back out to the client.
Either way, you should just join the inserted and deleted tables by primary key.
CREATE OR ALTER TRIGGER tr_AccountHistory
ON account
FOR UPDATE
AS
SET NOCOUNT ON;
IF NOT EXISTS (SELECT 1 FROM inserted) -- early bailout
RETURN;
IF EXISTS (SELECT 1
FROM inserted i
JOIN deleted d ON d.YourPrimaryKey = i.YourPrimaryKey
WHERE d.Version >= i.Version
)
THROW 50001, 'Improper version information provided', 1;
INSERT INTO AccountHistory
(
insert column
)
SELECT
columsHere
FROM deleted;

How to lock the transaction untill single query completes its execution for not getting deadlock error

I have the following code in which I have doubt.
Update Statement on Table 1
Update Statement on Table 2
Select Statement which include both the Table 1
Now above code will return to the application. means it is get all function for the application.
I am getting deadlock error in the application frequently.
I have hundred of users which is fetching the same table at a time.
So I have to make sure that untill the completion of update statement select statement will not fire OR how to lock the update statement.
One more doubt that if suppose I am updating the one row & another user has tried to select that table then will he get the deadlock.
(User was trying to select another row which was not in the update statement.)
what will happen for this scenario.
Please help me.
Thanks in advance
You should use transaction,
BEGIN TRANSACTION [Tran1]
BEGIN TRY
Update Statement on Table 1
Update Statement on Table 2
Select Statement which include both the Table 1
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
END CATCH
GO
If you want nobody to update/delete the row, I would go with the UPDLOCK on the SELECT statement. This is an indication that you will update the same row shortly, e.g.
select #Bar = Bar from oFoo WITH (UPDLOCK) where Foo = #Foo;

##ROWCOUNT check not detecting zero row count

I have a SQL Server Stored Procedure (using SQL Server 2008 R2) where it performs several different table updates. When rows have been updated I want to record information in an Audit table.
Here is my pseudo code:
UPDATE tblName SET flag = 'Y' WHERE flag = 'N'
IF ##ROWCOUNT > 0
BEGIN
INSERT INTO auditTable...etc
END
Unfortunately, even when zero rows are updated it still records the action in the audit table.
Note: There are no related triggers on the table being updated.
Any ideas why this could be happening?
Any statement that is executed in T-SQL will set the ##rowcount, even the if statement, so the general rule is to capture the value in the statement following the statement you're interested in.
So after
update table set ....
you want
Select #mycount = ##Rowcount
Then you use this value to do your flow control or messages.
As the docs state, even a simple variable assignment will set the ##rowcount to 1.
This is why it's important in this case that if you want people to diagnose the problem then you need to provide the actual code, not pseudo code.

SQL Server Trigger not triggered after insert and update

I want to copy the contents of a column into another column in the same table. Therefore, I created this trigger:
ALTER TRIGGER [dbo].[kennscht_copy_to_prodverpt]
ON [dbo].[Stammdaten]
AFTER INSERT
AS
UPDATE Stammdaten
SET PRODVERPT = (SELECT KENNSCHT FROM INSERTED)
WHERE SNR = (SELECT SNR FROM INSERTED);
But when I use an UPDATE on the table to update KENNSCHT to a different value, PRODVERPT is not updated as well. Now, you could argue that is because the trigger is on AFTER INSERT and not AFTER UPDATE, but when I change it so it's triggered by UPDATE and INSERT, whenever I update any row, I get an error message from SQL Server
Cannot update row because it would make the row not unique or update multiple rows (2 rows) [sic]
What is going on here? Either the trigger doesn't do anything, or it's messing up the whole table, making it un-updateable.
Update: I also tried the following trigger:
UPDATE s
SET s.PRODVERPT = i.KENNSCHT
FROM Stammdaten s
INNER JOIN INSERTED i ON i.SNR = s.SNR;
But it has exactly the same behaviour. If I use only AFTER INSERT, nothing changes and if I use AFTER INSERT, UPDATE I get the error message above.
Update 2: There are no multiple rows in the table, I already checked that because I thought it might be connected to the issue.
If you run this trigger as an AFTER UPDATE trigger, it runs recursively, since it always issues another UPDATE statement against the same table, which leads to another execution of the trigger.
To work around this, you either need to make the update trigger an INSTEAD OF UPDATE trigger or test if the KENNSCHT column was modified at all. For the latter you can use the UPDATE() function like this:
ALTER TRIGGER [dbo].[kennscht_copy_to_prodverpt_after_update]
ON [dbo].[Stammdaten]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
IF (UPDATE(KENNSCHT))
BEGIN
UPDATE s
SET s.PRODVERPT = i.KENNSCHT
FROM Stammdaten s
INNER JOIN INSERTED i ON i.SNR = s.SNR
END
END

Insert trigger preventing duplicates

I have a table with a AutoIdentity column as its PK and a nvarchar column called "IdentificationCode". All I want is when inserting a new row, it will search the table for any preexisting IdentificationCode, and if any found roll back the transaction.
I have written the folowing trigger:
ALTER trigger [dbo].[Disallow_Duplicate_Ids]
on [dbo].[tbl1]
for insert
as
if ((select COUNT(*) from dbo.tbl1 e , inserted i where e.IdentificationNo = i.IdentificationNo ) > 0)
begin
RAISERROR('Multiple Ids detected',16,1)
ROLLBACK TRANSACTION
end
But when inserting new rows, it always triggers the rollback even if there is no such IdentificationCode.
Can any one help me please?
thanks
As #Qpirate mentions, you should probably put some sort of UNIQUE constraint on the column. This is probably 'stronger' than using a trigger, as there's ways to disable those.
Also, the implicit-join syntax (comma-separated FROM clause) is considered an SQL anti-pattern - if possible, please always explicitly declare your joins.
I suspect that your error is because your trigger seems to be an AFTER trigger, and you check to see if there are any (non-zero) rows in the table; in other words, the trigger is (possibly) 'failing' the INSERT because it was INSERTed. Changing it to a BEFORE (or INSTEAD OF) trigger, or changing the count to >= 2 may solve the problem.
Without seeing your insert statement, it's impossible to know for sure, but (especially if you're using a SP), you may be able to check for existence in the INSERT statement itself, and throw an error (or do something else) if the row isn't inserted.
For example, the following:
INSERT INTO tbl1 (identificationCode, *otherColumns*)
VALUES (#identificationCode, *otherColumns)
WHERE NOT EXISTS (SELECT '1'
FROM tbl1
WHERE identificationCode = #identificationCode)
Will return a code indicating 'row not found' (inserted, etc; on pretty much every system this is SQLCODE = 100) if identificationCode is already present.
Use EXISTS to check if the IdentificationCode already exist.
If EXISTS (Select * from tbl1 where IdentificationCode = #IdentificationCode )
BEGIN
//do something
END
Else
BEGIN
//do something
END