Triggers and disabling "row(s) affected" message - sql-server

I have a trigger type INSTEAD OF Insert, Update.
It looks something like below:
IF EXISTS(some select staement)
updade
set
where
ELSE IF (some other select staement)
insert
values
ELSE--(3)
RAISERROR ('msg',16,1)
RETURN; SET NOCOUNT ON;
The issue is that in 3rd - "else" option I would like to show only error message without any "row(s) affected" message.
SET NOCOUNT ON dosen't work for me. I've already tried different configurations, put this with and without return. I was putted it everywhere in my statement.
It doesn't work anywhere. I use SQL Server 2005 Can anybody help me please?
Rows affected massage should appear always. The only exception is else statement.

Use SET NOCOUNT ON; before a query and then use GO
SET NOCOUNT ON;
GO

In a simplified example:
create table T (
ID int not null
)
go
create trigger TT
on T
instead of insert
as
RAISERROR('No',16,1)
go
insert into T (ID)
select 1
We get the output:
Msg 50000, Level 16, State 1, Procedure TT, Line 5
No
(1 row(s) affected)
The only way to suppress that "1 row affected" message is to rollback the transaction (by including ROLLBACK after the error message). And that will generate this instead:
Msg 50000, Level 16, State 1, Procedure TT, Line 5
No
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.
There's no way to suppress these messages further. The "1 row affected" message is being generated in the outer scope (the scope in which the INSERT statement is being run) rather than within the scope of the trigger, and is being generated because the trigger is running to completion - so far as the outer statement is concerned, it's been succesful.
The usual advice about NOCOUNT is to use it to suppress additional rowcounts being returned from within a trigger. If you're using it for that, it should be the first statement within the body of the trigger. IN your sample, you have it as the last statement, where it will have no effect anyway.

SeeSnapshot
create TRIGGER triggerdeletefordesignation
ON designation
instead of INSERT,DELETE,update
AS
BEGIN
print '"operation on this table is not allowed"'
rollback
END
GO
insert into designation values(6,'lead');
exec getDesg
and output:
"operation on this table is not allowed"
Msg 3609, Level 16, State 1, Line 12
The transaction ended in the trigger. The batch has been aborted.
and record not inserted into table

Related

"Instead of delete trigger" triggers two times

I am using MS SQL Server 2016 where I have implemented a instead of delete trigger. It looks like this:
ALTER TRIGGER MyTrigger ON MyTable INSTEAD OF DELETE AS
BEGIN
IF --some condition
BEGIN
RAISERROR ('Error msg', 16, 1)
ROLLBACK TRAN
RETURN
END
DELETE MyTable FROM MyTable JOIN deleted ON MyTable.id = deleted.id
END
If I execute a DELETE statement on the table 'MyTable' and the condition in the if is not fulfilled the DELETE statement is executed after the if-block. This is absolutely correct. But in the console of SSMS it is written twice that the DELETE statement was executed. So the following is written in the console:
(1 rows affected)
(1 rows affected)
I do not understand why. Why does SSMS indicate twice that a row is affected? I use SSMS version 15.0.18338.0.
This is because there were 2 sets of data effect, the set outside the TRIGGER, and then again inside it, because the initial dataset doesn't perform the DML operation itself. If you don't want to see the latter count, turn NOCOUNT to ON. This, of course, means that if fewer rows are effected in your TRIGGER, you won't know about it in the output from SSMS (but it's just informational anyway).
It is also heavily advised that you don't use ROLLBACK inside a TRIGGER, handle transactions outside the TRIGGER, not inside. RAISERROR isn't recommend either and you should be using THROW for new development work (that's been recommended since 2012!). This results in a TRIGGER like below:
CREATE OR ALTER TRIGGER MyTrigger ON dbo.MyTable INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM deleted WHERE SomeVal = 'Nonsense')
THROW 95302, N'Error Msg', 16; --Use an error number appropriate for you
ELSE
DELETE MT FROM dbo.MyTable MT JOIN deleted ON MT.id = deleted.id;
END;
GO

message from trigger is not displayed

I have created trigger for checking value length. I want display my own message than length is more than specified. Trigger was created without error. But If I try create record with value more than specified I get only system error. How make so I see also my message? Thanks
CREATE TRIGGER check_region_name_length
ON Region
INSTEAD OF INSERT
AS
BEGIN
IF EXISTS
(
SELECT inserted.RegionName FROM inserted
WHERE LEN(inserted.RegionName)>10
)
BEGIN
RAISERROR('RegionName value is longer than specified', 10, 1);
ROLLBACK TRANSACTION;
RETURN;
END;
END
GO
Basic data integrity checks are performed well before a trigger fires, and if they fail, the trigger is never invoked at all.
As such, if you're trying to replace or augment an existing warning (i.e. if the column's length is defined as (10)), you're on a hiding to nothing trying to set a new error message from a trigger.
E.g.:
create table T (Val1 char(1) not null)
go
create trigger T_T on T instead of insert
as
insert into T (Val1)
select SUBSTRING(Val1,1,1) from inserted
go
insert into T (Val1) values ('ab')
Produces:
Msg 8152, Level 16, State 14, Line 1
String or binary data would be truncated.
The statement has been terminated.
Even though, had the trigger been allowed to fire, it would have performed a successful insert.

How to make SET XACT_ABORT ON rollback the transaction?

Based on the Books Online documentation of SET XACT_ABORT ON, i get the impression that if a T-SQL statement raises a run-time error, the entire transaction is terminated and rolled back:
Remarks
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back.
Testing this in SQL Server 2008 R2:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
PRINT 'TranCount befor an error = '+CAST(##Trancount AS varchar(50))
DROP TABLE QuertyAsdf
PRINT 'TranCount after an error = '+CAST(##Trancount AS varchar(50))
Gives the output:
TranCount befor an error = 1
Msg 3701, Level 11, State 5, Line 6
Cannot drop the table 'QwertyAsdf', because it does not exist or you do not have permission.
TranCount after an error = 1
i was also under the impression that SET XACT_ABORT ON terminates the batch if there's an error:
SET XACT_ABORT ON instructs SQL Server to rollback the entire transaction and abort the batch when a run-time error occurs.
That sounds handy. How can i make it do that too?
The SQL Server only rollback transactions when Severity level greater or equals 16.
See example:
Msg 544, Level 16, State 1, Line 1
Cannot insert explicit value for identity column in table 'ORC_ORCAMENTO' whenIDENTITY_INSERT is set to OFF.
Test on SQL Server 2008 R2
SET XACT_ABORT ON;
BEGIN TRANSACTION;
PRINT 'TranCount befor an error = '+CAST(##Trancount AS varchar(50))
insert into ORC_ORCAMENTO (ORCID, ORCNOME, ORCATIVO) VALUES (1, 'TESTE_ALEXP', 0);
PRINT 'TranCount after an error = '+CAST(##Trancount AS varchar(50))
Returns
TranCount befor an error = 1
Msg 544, Level 16, State 1, Line 5
Cannot insert explicit value for identity column in table 'ORC_ORCAMENTO' when IDENTITY_INSERT is set to OFF.
TranCount after an error = 0
See Microsoft Error Message Levels on
https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors
When you use xact abort on, in the try catch statement, you can manually raise an error to make the transaction roll back.
set xact_abort on;
begin try
...dml statements here....
if conditions here...
raiseerror(....);
end try
begin catch
end catch

When an error stops execution in SQL Server?

If I exec this batch:
begin transaction
PRINT 'start'
PRINT 1/0
PRINT 'continue'
drop table dbo.tblPrueba
select * from dbo.tblPrueba
PRINT 'finish'
rollback transaction
The ouput is this:
start
Msg 8134, Level 16, State 1, Line 3
Divide by zero error encountered.
continue
Msg 208, Level 16, State 1, Line 6
Invalid object name 'dbo.tblPrueba'.
I am forcing two errors:
- the first one: PRINT 1/0 (that generates this error:
Msg 8134, Level 16, State 1, Line 3
Divide by zero error encountered.
) And continue executing the batch
- the second one:
drop table dbo.tblPrueba
select * from dbo.tblPrueba
That generates this error:
Msg 208, Level 16, State 1, Line 6
Invalid object name 'dbo.tblPrueba'.
And stops execution of the batch
What is the different between them? Where can I learn those that stop execution and those that doesn´t?
Thanks a lot!!
Since the first error is a divide by zero error, this behavior depends on your ARITHABORT, ARITHIGNORE and ANSI_WARNINGS settings.
From the article:
These three SET commands give you very fine-grained control for a very
small set of errors. When a division by zero or an overflow occurs,
there are no less four choices.
No action at all, result is NULL – when ARITHIGNORE is ON.
Warning message, result is NULL – when all are OFF.
Statement-termination – when ANSI_WARNINGS is ON.
Batch-abortion – when ARITHABORT is ON and ANSI_WARNINGS is OFF.
As far as which errors stop execution and which ones don't, please refer to the same article.
The easiest way to ensure all errors are handled correctly is to use TRY/CATCH
Without this, different errors can be statement, scope or batch aborting depending on settings such as ARITHxx, ANSI_WARNINGS and XACT_ABORT. This is demonstrated and discussed at "Error Handling in SQL 2000"
You can see the different (no SET options changed) with this
CREATE TABLE dbo.tblPrueba (gbn int);
GO
BEGIN TRY
begin transaction
PRINT 'start'
PRINT 1/0
PRINT 'continue'
drop table dbo.tblPrueba
select * from dbo.tblPrueba
PRINT 'finish'
rollback transaction
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE();
IF XACT_STATE() <> 0 rollback transaction
END CATCH
If I run this twice, I get this because the DROP is never executed
Msg 2714, Level 16, State 6, Line 1
There is already an object named 'tblPrueba' in the database.
Where can I learn those that stop execution
You can use exception handling
Begin try
begin transaction
PRINT 'start'
PRINT 1/0
PRINT 'continue'
create table #t
(
id int
)
drop table #t
select * from #t
PRINT 'finish'
rollback transaction
End Try
Begin Catch
if( XACT_STATE() == 1)
Rollback Tran
End Catch
You can use Set XACT_ABORT ON like below.
Set XACT_ABORT ON
begin transaction
PRINT 'start'
PRINT 1/0
PRINT 'continue'
create table #t
(
id int
)
drop table #t
select * from #t
PRINT 'finish'
rollback transaction

SQL Server: How to abort a series of batches in Query Analyzer?

i have a series of T-SQL statements separated by the special Query Analyzer batch separator keyword:
GO
If one batch fails, i need Query Analyzer to not try subsequent batches - i want it to stop processing the series of batches.
For example:
PRINT 'This runs'
go
SELECT 0/0, 'This causes an error'
go
PRINT 'This should not run'
go
Output:
This runs
Server: Msg 8134, Level 16, State 1, Line 2
Divide by zero error encountered.
This should not run
Possible?
Update
An example of this in real use might be:
sp_rename 'Shelby', 'Kirsten'
go
DROP VIEW PeekAView
go
CREATE VIEW PeekAViewAS
SELECT * FROM Kirsten
go
Here is how I'd do it:
PRINT 'This runs'
go
SELECT 0/0, 'This causes an error'
go
if (##error <> 0)
Begin
set nocount on
set noexec on
End
GO
PRINT 'This should not run'
go
set noexec off
set nocount off
GO
The "noexec" mode puts SSMS is a state where it just compiles the T-SQL and doesn't actually execute it. It is similar to accidentally pressing the Parse toolbar button (Ctrl+F5) instead of Execute (F5).
Don't forget to turn noexec back off at the end of your script. Otherwise users are going to get confused by permanent "Command(s) completed successfully." messages.
I use the check against ##error in the subsequent batch instead of using TRY CATCH blocks. Using ##error in the next batch will catch compile errors, like "table doesn't exist".
In addition to the noexec mode, I also toggle the nocount mode. With noexec mode on and nocount off, your queries will still report a message "(0 rows(s) affected)". The message always reports zero rows, because you're in noexec mode. However, turning nocount on suppresses these messages.
Also note that if running SQL Server 2005 the command you are skipping might still give error messages if it references a table that doesn't exist and the command if the first command in the batch. Forcing the command to be the second command in the batch with a bogus Print statement can suppress this. See MS Bug #569263 for more details.
You can activate the "Query, SQLCMD Mode" menu option and place the following at the beginning of the script:
:on error exit
This will stop execution when an error occurs, even if there are subsequent batches.
Just make sure that you don't accidentally run the script without SQLCMD mode on because you will get the typical behavior where errors are ignored.
When I need to do this, I issue a RAISERROR of severity 20. This, or higher, will kill the current connection, and prevent subsequent "GO batches" from executing. Yes, it can be awkward, but it does the job.
Create a temporary table; and update it after each step (if successful); and then check the success of the previous step by validating against the table.
create table #ScriptChecker (SuccessfullStep int)
-- Do Step One
Insert into #ScriptChecker
Select 1
-- Step 2
If exists (select * from #ScriptChecker where SuccessfullStep = 1)
-- Do Step 2 ...
based on #u07ch idea, but only insert on failure...
create table #test (failure int)
if not exists (select * from #test)
BEGIN
print 'one' --sql here
END
go
if not exists (select * from #test)
BEGIN
print 'two'--sql here
END
go
if not exists (select * from #test)
BEGIN
print 'three' ---SQL SERVER 2000 version
--error--
SELECT 0/0, 'This causes an error'
IF ##ERROR!=0
BEGIN
insert into #test values (1)
PRINT 'ERROR'
END
end
go
if not exists (select * from #test)
BEGIN
print 'three' ---SQL SERVER 2005/2008 version
BEGIN TRY
--error--
SELECT 0/0, 'This causes an error'
END TRY
BEGIN CATCH
insert into #test values (1)
PRINT 'ERROR'
END CATCH
END
go
if not exists (select * from #test)
BEGIN
--sql here
print 'four'
END
go
output 2000:
one
two
three
----------- --------------------
Msg 8134, Level 16, State 1, Line 7
Divide by zero error encountered.
(1 row(s) affected)
ERROR
output 2005/2008:
one
two
three
----------- --------------------
(0 row(s) affected)
(1 row(s) affected)
ERROR
Erland Sommarskog in the microsoft.public.sqlserver.programming group had a very good idea:
In a change script such as the one you
posted, you need to be defensive, and
start each batch with IF ##trancount >
0.
Using
IF ##trancount > 0
is much cleaner.

Resources