Simple IF EXISTS in TRIGGER fails - sql

I've created a simple TRIGGER:
ALTER TRIGGER [dbo].[SprawdzZajetosc] ON [dbo].[Wypozyczenia]
FOR insert, update
AS
BEGIN
IF EXISTS (SELECT * FROM Wypozyczenia)
BEGIN
RAISERROR('Wybrany pojazd został już wypożyczony w wybranym przedziale czasu.', 16, 1)
ROLLBACK TRANSACTION
END
END
I can't understand why 'if' is returning me TRUE even if table 'Wypozyczenia' is empty? It doesn't matter what 'Wypozyczenia' contains - it always returns me TRUE.
I tried with count(*) it always returns me a value > 0.
Why is that?

I am not 100% sure of this, but it sounds logical to me - The trigger is an insert/update trigger. As soon as something is being inserted, the trigger is triggered and the condition is TRUE. Since there is a ROLLBACK TRANSACTION fired, the inserted row is then rolled back and hence you get an empty table. What are you actually trying to achieve here ?

Apart from the reasons why you're doing this, the cause for IF EXISTS() to be always TRUE in your case is very simple it's because you're using an AFTER or FOR trigger.
CREATE TRIGGER
AFTER specifies that the DML trigger is fired only when all operations
specified in the triggering SQL statement have executed successfully.
All referential cascade actions and constraint checks also must
succeed before this trigger fires.
Meaning the row(s) you're trying to insert are already in the table. It's just a transaction has not been committed yet.
Here is SQLFiddle demo
Your IF EXISTS() check might've worked only in INSTEAD OF INSERT trigger but then you should've take into consideration that triggers in SQL Server are statement based. Meaning it fires once per statement and you can insert more than one row in one statement.
Here is SQLFiddle demo
As far as FOR UPDATE clause goes in your trigger definition it doesn't make any sense at all. If you're updating something it should be in the table. Thus table is not empty.

Related

Regarding trigger sequence

Have two triggers on a table. One trigger is executed when there is a insert or update for each row in the table. Second trigger is executed when there is a update for each row in the table. Which trigger gets executed first in ORACLE 10G when there is a update statement on a row in the table. Is there any order of execution for triggers in oracle? If so how can i set it?
The order in which the triggers will fire is arbitrary and not something that you can control in 10g. I believe, technically, it goes in the order that the triggers happened to be created but that's certainly not something that you'd want to count on.
In 11g, you can control the firing order of triggers. However you are almost always better off replacing the two triggers with one trigger that calls two stored procedures. So rather than
CREATE TRIGGER trg_1
BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
<<do thing 1>>
END;
CREATE TRIGGER trg_2
BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
<<do thing 2>>
END;
you would be much better served with something like
CREATE PROCEDURE p1( <<arguments>> )
AS
BEGIN
<<do thing 1>>
END;
CREATE PROCEDURE p2( <<arguments>> )
AS
BEGIN
<<do thing 2>>
END;
CREATE TRIGGER trg
BEFORE UPDATE ON t
FOR EACH ROW
BEGIN
p1( <<list of arguments>> );
p2( <<list of arguments>> );
END;
For versions before 11g, no, the order is unspecified. From 10g Release 2 docs:
For enabled triggers, Oracle automatically performs the following actions:
Oracle runs triggers of each type in a planned firing sequence when more than one trigger is fired by a single SQL statement. First, statement level triggers are fired, and then row level triggers are fired.
Oracle performs integrity constraint checking at a set point in time with respect to the different types of triggers and guarantees that triggers cannot compromise integrity constraints.
Oracle provides read-consistent views for queries and constraints.
Oracle manages the dependencies among triggers and schema objects referenced in the code of the trigger action
Oracle uses two-phase commit if a trigger updates remote tables in a distributed database.
Oracle fires multiple triggers in an unspecified, random order, if more than one trigger of the same type exists for a given statement; that is, triggers of the same type for the same statement are not guaranteed to fire in any specific order.
No order to trigger firing can be relied upon in 10g beyond the normal before statement, before row, after row, after statement order. In 11g a new FOLLOWS clause was added to the CREATE TRIGGER statement.
In Oracle 10g we do not control the triggers that are created on same timing. It is executed randomly. So we cannot say which trigger is fired first. To overcome this problem, Oracle 11g introduced FOLLOWS CLAUSE. Using this we can control the execution order.

Does ##IDENTITY in an SP using triggers result in a race condition?

If I were to use ##IDENTITY in a SQL sp which also activated a trigger, would it result in a race condition?
Say I perform an INSERT INTO TABLE_A which triggers an INSERT INTO TABLE_B then, in the current scope I use ##IDENTITY
Would I always get the resulting identity from the trigger (TABLE_B) or would it depend on which thread finished first?
Note: I know about SCOPE_IDENTITY, this question is hypothetical....
I believe you'll always get the identity from table B, because your transaction in Table A isn't considered complete and committed (and your T-SQL isn't allowed to advance) until the insert and trigger are both complete. Even if you're using an AFTER trigger, you're still waiting until the trigger code is done before you get to the next line and ask for the ##identity.

After Delete Trigger Fires Only After Delete?

I thought "after delete" meant that the trigger is not fired until after the delete has already taken place, but here is my situation...
I made 3, nearly identical SQL CLR after delete triggers in C#, which worked beautifully for about a month. Suddenly, one of the three stopped working while an automated delete tool was run on it.
By stopped working, I mean, records could not be deleted from the table via client software. Disabling the trigger caused deletes to be allowed, but re-enabling it interfered with the ability to delete.
So my question is 'how can this be the case?' Is it possible the tool used on it futzed up the memory? It seems like even if the trigger threw an exception, if it is AFTER delete, shouldn't the records be gone?
All the trigger looks like is this:
ALTER TRIGGER [sysdba].[AccountTrigger] ON [sysdba].[ACCOUNT] AFTER DELETE AS
EXTERNAL NAME [SQL_IO].[SQL_IO.WriteFunctions].[AccountTrigger]
GO
The CLR trigger does one select and one insert into another database. I don't yet know if there are any errors from SQL Server Mgmt Studio, but will update the question after I find out.
UPDATE:
Well after re-executing the same trigger code above, everything works again, so I may never know what if any error SSMS would give.
Also, there is no call to rollback anywhere in the trigger's code.
after means it just fires after the event, it can still be rolled back
example
create table test(id int)
go
create trigger trDelete on test after delete
as
print 'i fired '
rollback
do an insert
insert test values (1)
now delete the data
delete test
Here is the output from the trigger
i fired
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.
now check the table, and verify that nothing was deleted
select * from test
The CLR trigger does one select and
one insert into another database. I
don't yet know if there are any errors
from SQL Server Mgmt Studio, but will
update the question after I find out.
Suddenly, one of the three stopped
working while an automated delete tool
was run on it.
triggers fire per batch/statement not per row, is it possible that your trigger wasn't coded for multi-row operations and the automated tool deleted more than 1 row in the batch? Take a look at Best Practice: Coding SQL Server triggers for multi-row operations
Here is an example that will make the trigger fail without doing an explicit rollback
alter trigger trDelete on test after delete
as
print 'i fired '
declare #id int
select #id = (select id from deleted)
GO
insert some rows
insert test values (1)
insert test values (2)
insert test values (3)
run this
delete test
i fired
Msg 512, Level 16, State 1, Procedure trDelete, Line 6
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
check the table
select * from test
nothing was deleted
An error in the AFTER DELETE trigger will roll-back the transaction. It is after they are deleted but before the change is committed. Is there any particular reason you are using a CLR trigger for this? It seems like something that a pure SQL trigger ought to be able to do in a possibly more lightweight manner.
Well you shouldn't be doing a select in trigger (who will see the results) and if all you are doing is an insert it shouldn't be a CLR trigger either. CLR is not generally a good thing to have in a trigger, far better to use t-SQL code in a trigger unless you need to do something that t-sql can't handle which is probably a bad idea in a trigger anyway.
Have you reverted to the last version you have in source control? Perhaps that would clear the problem if it has gotten corrupted.

Update trigger behavior in SQL 2005

I used an Update statement inside a procedure to update a table which has an update trigger. Does the update statement complete after the trigger completes or what?
the trigger runs as part of the UPDATE statement (after the data in the table has been updated); the proc resumes after this. There are also "instead of" triggers that replace the UPDATE statement.
See here for more.
There are two types of triggers in SQL Servers. INSTEAD OF triggers, and AFTER triggers. By default, a trigger is an AFTER trigger, meaning this is what happens. Consider TableA, with an UPDATE AFTER TRIGGER which updates TableB.
Issue statement: UPDATE TableA set XXX = 5;
TableA gets updated
The trigger fires, and TableB gets updated.
triggers are attached to the statement(s) that trigger them and are implicitly the part of transaction that fired them.
For ex :
if triggers are fired because of update , then it helps to understand that database would implicitly insert a begin tran and end tran surrounding that update.

Are Sql Triggers synchronous or asynchronous?

I have a table that has an insert trigger on it. If I insert in 6000 records into this table in one insert statement from a stored procedure, will the stored procedure return before the insert trigger completes?
Just to make sure that I'm thinking correctly, the trigger should only be called (i know 'called' isn't the right word) once because there was only 1 insert statement, right?
My main question is: will the sproc finish even if the trigger hasn't completed?
Your insert trigger will run once for the entire insert statement. This is why it is important to use the inserted temporary table to see what has actually been inserted, and not just select the most recent single record, or something like that.
I just tested an insert and update trigger and indeed, they are considered part of the insert by sql server. the process will not finish until the trigger finishes.
Triggers are part of the transaction that called them.
One important thing about triggers that you must be aware of is that the trigger fires once for each transaction (at least in SQL server, you should check other dbs, but even if it will process row by row, that is usually a poor idea), so if you insert 6000 records the trigger fires once not 6000 times. Many people are not aware of this and write triggers as if they will process multiple record inserts one record at a time. This is not true and your trigger must account for handing the multiple record insert.
The trigger call is not asynchronous. Each call to your insert procedure will result in the trigger being fired, and the procedure will not return until the trigger finishes.
Take a look at the query plan to see how it works. You'll see that the statements in the trigger will be called for each call to the procedure.
The thing is, every time the TRIGGER criteria is met, the TRIGGER fires. It fires once in batch processing or Transaction. See my lesson 101 on TRIGGER
You should use cursor in insert statement to process trigger row.
Because Triggers in SQL Server fire once per statement, not once per row.