Interesting issue with Filtered indexes.
Recently, an ISV I work with ran into an interesting problem with Filtered Indexes.
The application does all SQL INSERT, DELETE, and UPDATE operations using individual stored procedures. To improve performance, they decided to create a Filtered Index (new in SQL 2008) to restrict the data such that it does not contain NULL values, in this case drastically reducing the number of rows in the index. See documentation here: https://msdn.microsoft.com/en-us/library/ms175049.aspx
This all worked fine, until months later, when the ‘UPDATE’ stored procedure was updated as part of a routine application upgrade. The stored proc was replaced with a newer version using a TSQL script. After adding the ‘new’ stored proc the application was tested and the following was observed;
· the SELECT statements accessing the table continued to use the filtered index
· the ‘INSERT’, and ‘DELETE’ stored procs continued to work
· However, the modified ‘UPDATE’ stored proc returned the following error:
InnerException: System.Data.SqlClient.SqlException: UPDATE failed because the following SET options have incorrect settings: 'ANSI_NULLS, 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.
Note that the error message is very explicit and actually points us to the source of the problem. Can you guess what it is?
You are correct! The TSQL script used SET options that were set differently for the new ‘UPDATE’ stored proc than they were originally, and they don’t conform to the rules required to utilize Filtered Indexes. In this case, the SET ANSI_NULLS and QUOTED_IDENTIFIER were set to an invalid setting.
The solution to the problem was to recreate the stored proc using the ‘correct’ settings required to use Filtered indexes, documented here: https://msdn.microsoft.com/en-us/library/ms188783.aspx
The following simplified TSQL example shows the problem and the solution.
NOTE: the fact that Stored Procedures were used is important, because they ‘inherit’ the SET statement values they were created with, and NOT the values they are executed with.
USE USE master
GO
CREATE DATABASE FI_Test
GO
USE FI_Test
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [FactSalesQuota](
[SalesQuotaKey] [int] IDENTITY(1,1) NOT NULL,
[EmployeeKey] [int] NOT NULL,
[DateKey] [int] NOT NULL,
[CalendarYear] [smallint] NOT NULL,
[CalendarQuarter] [tinyint] NOT NULL,
[SalesAmountQuota] [money] NOT NULL,
CONSTRAINT [PK_FactSalesQuota_SalesQuotaKey] PRIMARY KEY CLUSTERED
([SalesQuotaKey] ASC))
GO
CREATE NONCLUSTERED INDEX FI_FactSalesQuota
ON FactSalesQuota(Employeekey,CalendarQuarter)
WHERE CalendarYear = 2009 -- <---- This makes it a Filtered index
GO
INSERT FactSalesQuota values(53,20090101,2009,4, 37000.00)
GO
SELECT * FROM FactSalesQuota
GO
--- this was the script to update the sp
use FI_Test
GO
sp_rename UPDATE_FactSalesQuota , UPDATE_FactSalesQuota_V1
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF -- <------ HERE is what caused the error
--SET QUOTED_IDENTIFIER ON -- <------ it will work if this is set correctly
GO
CREATE PROCEDURE UPDATE_FactSalesQuota
@EmployeeKey int,
@CalendarYear smallint,
@CalendarQuarter tinyint,
@SalesAmountQuota money
AS
UPDATE FactSalesQuota
SET SalesAmountQuota = @SalesAmountQuota,
DateKey = cast((CONVERT (char(8) ,getdate(), 112)) as int)
WHERE EmployeeKey = @EmployeeKey and
CalendarYear = @CalendarYear and
CalendarQuarter = @CalendarQuarter
GO
--this fails
EXECUTE UPDATE_FactSalesQuota 53,2009,4,52000.00
GO
SELECT * FROM FactSalesQuota
GO
-- Now go back fix the script, and retry the sript and now it will work