How to set "SET XACT_ABORT ON " in a SQL Server transaction? - sql-server

I want to set SET XACT_ABORT ON in a SQL Server 2008R2 stored procedure with a transaction, so do it in a creation script:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET XACT_ABORT ON
GO
CREATE PROCEDURE MyProc
AS
BEGIN TRAN
...
IF ##ERROR <> 0
BEGIN
GOTO Done
END
...
IF ##ERROR <> 0
BEGIN
GOTO Done
END
COMMIT TRAN
Done:
IF ##ERROR <> 0
BEGIN
ROLLBACK TRAN
END
GO
After successful creation, I check the transaction by clicking "Modify" stored procedure option and in a generated ALTER PROCEDURE script I don't see SET XACT_ABORT ON line:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE MyProc
AS
BEGIN TRAN
...
Where am I wrong or what is the trick? How to correctly define SET XACT_ABORT ON?

You normally set xact_abort as part of the body of the stored procedure:
CREATE PROCEDURE MyProc
AS
SET XACT_ABORT ON
BEGIN TRAN
....
There are two "special" settings that are remembered from the session that created the procedure. Explanation from MSDN:
Stored procedures execute with the SET settings specified at execute
time except for SET ANSI_NULLS and SET QUOTED_IDENTIFIER. Stored
procedures specifying SET ANSI_NULLS or SET QUOTED_IDENTIFIER use the
setting specified at stored procedure creation time. If used inside a
stored procedure, any SET setting is ignored.
So when you create a stored procedure, SQL Server copies the QUOTED_IDENTIFIER option from the connection to the procedure definition. The goal is that someone else with a different QUOTED_IDENTIFIER setting still gets the behavior the author of the procedure intended.
The same is not true for XACT_ABORT.

You didn't mention whether or not you are using SQL Management Studio, but if you are and click "Modify" on an existing stored procedure (which I'm assuming is what you did) then MS just generates a boilerplate script based on the contents of the existing stored procedure.
You might consider defining your stored procedures in a separate script file that performs and ALTER PROCEDURE, plus whatever other options you want outside of the sproc (such as SET XACT_ABORT ON). That way you have more control and can just execute the script to update the sproc.

Related

SET QUOTED_IDENTIFIER ON within the stored procedure is not working

I am using FOR XML PATH in my stored procedure and so that I need to have QUOTED_IDENTIFIER set to ON. I have it as a first statement of my stored procedure.
SET QUOTED_IDENTIFIER ON;
This is working fine all the times except I restore my database first time.
Just after restoring the database, I checked sp_helptext <SPName> and the stored procedure seems fine.
However when I browse my stored procedure from the Object Explorer and click on "Modify", it shows this:
When I tried executing the stored procedure using EXEC <SP_Name> it throws an error
SELECT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
Can someone guide me why SQL Server is adding SET QUOTED_IDENTIFIER OFF on its own? How to get rid of it? or How can we overwrite it from within the stored procedure?
If I remove SET QUOTED_IDENTIFIER OFF from the top, re-execute/run it then it will work fine.
My problem is - We have automated process which runs migrations/create database on every build so I can not remove it manually all the times.
I also can not set it on the database level.
I checked the database setting for that and its set to false but that should not make any difference right? As I have it specifically SET To ON inside my stored procedure.
Thank you in advance.
The QUOTED_IDENTIFIER database setting is irrelevant. It's only a default, every client driver sets QUOTED_IDENTIFIER on connecting and overrides the database default.
The SET QUOTED_IDENTIFIER inside the stored procedure is irrelevant; remove it.
SET QUOTED_IDENTIFIER has no effect when it appears in the body of a
stored procedure as static Transact-SQL.
SET QUOTED_IDENTIFIER
The setting for QUOTED_IDENTIFIER which was in force for the batch creating the stored procedure will be stored in the catalog, and used by SSMS to script the stored procedure. It does not matter what the current session setting is, or the database default is, or what the SSMS query options are. If it was created with QUOTED_IDENTIFIER ON it will be scripted with QUOTED_IDENTIFIER ON. You can see the stored setting here in sys.sql_modules.
eg
select uses_quoted_identifier
from sys.sql_modules
where object_id = object_id('MyProc')
So,
However when I browse my stored procedure from the Object Explorer and click on "Modify", it [is scripted with QUOTED_IDENTIFER OFF].
If the setting in sys.sql_modules different than the setting in the generated script, that would be a bug in SSMS/SMO.
And this:
means that the stored procedure was created with QUOTED_IDENTIFIER OFF. As you see the session setting for the session invoking the stored procedure is irrelevant.
When a stored procedure is created, the SET QUOTED_IDENTIFIER and SET
ANSI_NULLS settings are captured and used for subsequent invocations
of that stored procedure.
SET QUOTED_IDENTIFIER
This stored procedure's setting also controls the QUOTED_IDENTIFIER setting for dynamic SQL inside the stored procedure. But inside dynamic SQL you can change the setting.
And so yes
My problem is - We have automated process which runs migrations/create database on every build
This process is broken, as it's creating your proc with QUOTED_IDENTIFIER OFF. If you can't fix it you can work around it by pushing your TSQL into a dynamic batch, and setting QUOTED_IDENTIFIER ON in the dynamic SQL. eg
set quoted_identifier off
go
create or alter procedure foo
as
begin
exec ('set quoted_identifier on; select * from "sys"."objects"')
end
go
exec foo --suceeds
Also you can make your stored procedure create script depend on QUOTED_IDENTIFIER ON so you can't possibly create it with QUOTED_IDENTIFIER OFF, eg
set quoted_identifier off
go
create or alter procedure foo
as
begin
select * from "sys"."objects"
end
fails with
Msg 102, Level 15, State 1, Procedure foo, Line 4 [Batch Start Line 2]
Incorrect syntax near 'sys'.
please test bellow statment:
USE MyDB
GO
SET QUOTED_IDENTIFIER on;
GO
EXISTS ....
BEGIN
EXEC ...
END

do i need to explicitely add SET ANSI_NULLS ON in EntityFramework migration

I am writing migration for stored procedure with EF 6
string sqlQuery = #"
/****** Object: StoredProcedure [dbo].[GetSalesByLocation] Script Date: 9/21/2016 8:32:36 AM ******/
SET ANSI_NULLS ON GO
SET QUOTED_IDENTIFIER ON GO
CREATE PROCEDURE [dbo].[GetSalesByLocation]
#MerchantID bigint,
#LocationID bigint,
#AccountAccessID bigint,
#KioskID bigint,
#StartDate DATETIME,
#EndDate DATETIME
AS
BEGIN
.....
END
GO";
Sql(sqlQuery);
when i try to run Update-database command i get below error
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
Incorrect syntax near 'GO'.
Also when i create SP without
SET ANSI_NULLS ON GO
SET QUOTED_IDENTIFIER ON GO
and open in SQL management studio i can see above statement getting added automatically.
So my question is if i am writing migration for stored procedure do i need to `set ansi null on explicitly?
SET ANSI_NULL and SET QUOTED_IDENTIFIER outside the procedure affects SQL Server behaviour only for the session (so it will not be written in the stored procedure and it will not affect the stored procedure behaviour when the stored procedure will be run).
Also, SET ANSI_NULL ON is the default (try SELECT GETANSINULL() on SQMS) so you could omit it. You can also omit SET QUOTED_IDENTIFIER ON (you are using [] to quote identifiers and not ").
If you need to run more statements on the same connection, in Management Studio you use GO, using ADO you can split them on different execution methods (so you will eventually see errors in the right place). Otherwise you can use ; (in this case probably you can't use ;).

SET XACT_ABORT OFF if transaction terminated by SET XACT_ABORT ON

I read that many tend to SET XACT_ABORT ON at the beginning of the procedure.
CREATE PROC myProc
AS
Begin
BEGIN TRAN
SET XACT_ABORT ON
[..code1 that might throw an error..]
[..code2..]
SET XACT_ABORT OFF [?]
COMMIT TRAN
END
SET XACT_ABORT OFF [?]
Because run-time errors will terminate the procedure, SET XACT_ABORT will be left as ON. I have some questions :
Where do you set it on ? Before the CREATE PROC definition, after BEGIN or after BEGIN TRAN ? SET is at connection level so I suppose all three would not make a difference ?
When do you turn it OFF if it is left ON when errors occur ?
Setting XACT_ABORT before the CREATE PROC definition doesn't have much sense. Unlike ANSI_NULLS or QUOTED_IDENTIFIER, this option is not stored as a property of the stored procedure. I would say that you should set XACT_ABORT immediately after CREATE PROC myProc AS or after the first BEGIN, where you would also place the SET NOCOUNT ON.
I would not bother setting XACT_ABORT off. If any code portion needs to have it off (for example, inside a TRY-CATCH block that ignores some errors), it should set it off inside that specific TRY block, and set it back on at the end of END CATCH.
For more info, see: http://www.sommarskog.se/error_handling/Part1.html

QUOTED IDENTIFIER error in SQL Server 2008 on SP execution

I have this error on executing stored procedure:
INSERT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
Procedure is created with QUOTED_IDENTIFIER flag set to ON
IF EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'sp_procedure') AND TYPE IN (N'P', N'PC'))
BEGIN
DROP PROCEDURE [dbo].[sp_procedure]
END
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_procedure]
(
#param_1 XML
,#param_2 INT
)
AS
BEGIN
-- CODE
END
SELECT statement from sys.sql_modules shows that uses_quoted_identifiers somehow is set to 0.
I have already tried to execute following code. It run in one batch.
SET QUOTED_IDENTIFIER ON;
EXEC sp_procedure #param_1 = N'<?xml version="1.0" encoding="utf-16"?>
xmlns:xsd="http://www.w3.org/2001/XMLSchema" />', #param_2= 51326
But it does not help.
Each session is created with QUOTED_IDENTIFIER set to 1:
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
Any ideas what can it be?
UPD
Turned out that after running this specific script, there also run lots of other files. And one of them just recreated stored procedure with QUOTED_IDENTIFIER set to OFF.
Thank you for your help
Pleasure make sure the table and view definitions are also created with quoted identifier on.

SQL Server Stored Procedure SET Options

What are the best SET Options to issue before stored procedure creation?
for example
SET QUOTED_IDENTIFIER OFF
SET ANSI_NULLS ON
CREATE PROCEDURE HelloWorld
AS
--also, should any be issued within the procedure body?
PRINT 'hello world!'
RETURN 0
GO
By best, I mean the most preferred settings.
For stored procedure creation specifically, then only two are important at parse time
SET ANSI_NULLS
SET QUOTED_IDENTIFIER
And they should be ON to work with newer SQL Server functionality that only works these being ON
Stored procedures execute with the SET settings specified at execute time except for SET ANSI_NULLS and SET QUOTED_IDENTIFIER. Stored procedures specifying SET ANSI_NULLS or SET QUOTED_IDENTIFIER use the setting specified at stored procedure creation time. If used inside a stored procedure, any SET setting is ignored.
Ref: http://msdn.microsoft.com/en-us/library/ms190356.aspx
To set both at the same time and others as well, use
SET ANSI_DEFAULTS ON
When enabled (ON), this option enables the following ISO settings:
SET ANSI_NULLS
SET CURSOR_CLOSE_ON_COMMIT
SET ANSI_NULL_DFLT_ON
SET IMPLICIT_TRANSACTIONS
SET ANSI_PADDING
SET QUOTED_IDENTIFIER
SET ANSI_WARNINGS
For completeness, set these other three at the connection level which allows things like INDEXED VIEWs to work correctly
SET ARITHABORT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET NUMERIC_ROUNDABORT OFF
Transaction Isolation Level (often Read committed - to ensure no dirty reads). And unless you're interested in the number of rows affected; set nocount on
I typically don't bother setting any before the proc. In the proc Itypically use SET NOCOUNT ON and if it is just a select SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

Resources