I am investigating the effect of various SET options on the creation of indexed views on SQL Server 2014 CU1. The following code:
- ensures that the SET options are as they should be
- creates a table
- creates a view based on the table
- creates an index on the view
- inserts a record in the view (which will fail if the SET options are incorrect)
It works as expected. However, if I uncomment the section where I deliberately set each SET option to the wrong value, the CREATE INDEX statement (which occurs earlier in the script) fails saying that QUOTED_IDENTIFIER has the wrong value.
I have tested with SSMS 2012 and 2014. How can this be?
USE tempdb;
-- ensure correct SET options for indexed view creation
-- http://msdn.microsoft.com/en-us/library/ms191432.aspx
SET ANSI_NULLS ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET ARITHABORT ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET NUMERIC_ROUNDABORT OFF;
SET QUOTED_IDENTIFIER ON;
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = 'v1'
AND TABLE_SCHEMA = 'dbo')
DROP VIEW dbo.v1;
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 't1'
AND TABLE_SCHEMA = 'dbo')
DROP TABLE t1;
CREATE TABLE dbo.t1(c1 int);
GO
CREATE VIEW dbo.v1 WITH SCHEMABINDING AS SELECT c1 FROM dbo.t1;
GO
CREATE UNIQUE CLUSTERED INDEX i1 ON dbo.v1(c1);
---- make SET options the opposite of what is required
--SET ANSI_NULLS OFF;
--SET ANSI_PADDING OFF;
--SET ANSI_WARNINGS OFF;
--SET ARITHABORT OFF;
--SET CONCAT_NULL_YIELDS_NULL OFF;
--SET NUMERIC_ROUNDABORT ON;
--SET QUOTED_IDENTIFIER OFF;
INSERT INTO dbo.v1(c1) VALUES(1);
DROP VIEW dbo.v1;
DROP TABLE dbo.t1;
I can work around it with dynamic SQL and get the expected error message:
Msg 1934, Level 16, State 1, Line 32
INSERT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'…
-- make SET options the opposite of what is required
EXECUTE ('SET ANSI_NULLS OFF;
SET ANSI_PADDING OFF;
SET ANSI_WARNINGS OFF;
SET ARITHABORT OFF;
SET CONCAT_NULL_YIELDS_NULL OFF;
SET NUMERIC_ROUNDABORT ON;
SET QUOTED_IDENTIFIER OFF;
INSERT INTO dbo.v1(c1) VALUES(1);');
but why should I have to?
Best Answer
Among the SET statements in your script, SET QUOTED_IDENTIFIER is special in that it is processed at parse time rather than at execute time.
From SET Statements (Transact-SQL) (emphasis mine):
Confirmed in SET QUOTED_IDENTIFIER (Transact-SQL):
So, in your case QUOTED_IDENTIFIER gets reset at parse time and is, therefore, OFF by the time CREATE INDEX is executed. The engine complains about it as expected.
To resolve this, you don't have to resort to dynamic SQL, it is enough to put a
GO
line just after the CREATE INDEX statement to make it a different batch from the subsequent SETs, which would cause it to be parsed separately from them:Note, however, that SET Statements (Transact-SQL) also includes the following (emphasis mine):
Which means that the uncommented SET statements would still break your script, only not at CREATE INDEX but at INSERT.