SQL服务器参数化查询不使用非聚簇过滤

SQL Server parameterized query does not use Non Clustered Filtered

我在 Students table 上定义了一个包含和过滤的非聚簇索引。 SQL服务器版本是2017.

学生table定义:

CREATE TABLE [dbo].[Students]
(
    [Id] [INT] IDENTITY(1,1) NOT NULL,
    [Name] [NVARCHAR](50) NOT NULL,
    [CreatedOn] [DATETIME2](7) NOT NULL,
    [Active] [BIT] NOT NULL,
    [Deleted] [BIT] NOT NULL,

    CONSTRAINT [PK_Students] 
        PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]

具有包含和过滤器的非聚集索引:

CREATE NONCLUSTERED INDEX [NonClusteredIndex-20200508-225254] 
ON [dbo].[Students] ([CreatedOn] ASC)
INCLUDE([Name]) 
WHERE ([Active] = (1) AND [Deleted] = (0))
ON [PRIMARY]
GO

此查询使用 NonClusteredIndex-20200508-225254

SELECT Name, CreatedOn FROM dbo.Students
WHERE Active = 1
  AND Deleted = 0
ORDER BY CreatedOn

实际执行计划

但是当我使用如下参数化查询时,它没有使用NonClusteredIndex-20200508-225254。为什么会这样?我哪里错了?

DECLARE @Active BIT = 1
DECLARE @Deleted BIT = 0

SELECT Name, CreatedOn 
FROM dbo.Students
WHERE Active = @Active
  AND Deleted = @Deleted
ORDER BY CreatedOn

实际执行计划

这完全在意料之中。

当您编译带有参数或变量的计划时,它需要生成一个适用于它们可能具有的任何可能值的计划。

您可以将 OPTION (RECOMPILE) 添加到语句中,以便考虑这些的运行时值(基本上它们被替换为具有运行时值的文字)但这将意味着每次执行都要重新编译。

您最好有两个单独的查询,一个用于过滤索引处理的情况,一个用于其他情况。

您可能一直希望 SQL 服务器会执行类似下面的操作,并在聚集索引扫描 + 排序与过滤索引扫描和不排序之间动态切换(过滤器最多只有启动谓词)执行一个分支)

但是要获得此计划需要更改过滤索引以将 Name 移动到如下关键列中...

CREATE NONCLUSTERED INDEX [NonClusteredIndex-20200508-225254] 
ON [dbo].[Students] ([CreatedOn] ASC, [Name] asc)
WHERE ([Active] = (1) AND [Deleted] = (0))

...并将查询重写为

DECLARE @Active BIT = 1
DECLARE @Deleted BIT = 0

SELECT NAME,
       CreatedOn
FROM   dbo.Students WITH (INDEX =[NonClusteredIndex-20200508-225254])
WHERE  Active = 1
       AND Deleted = 0
       AND 1 = 1 /*Prevent auto parameterisation*/
       AND ( @Active = 1 AND @Deleted = 0 )
UNION ALL
SELECT NAME,
       CreatedOn
FROM   dbo.Students
WHERE  Active = @Active
       AND Deleted = @Deleted
       AND NOT ( @Active = 1
                 AND @Deleted = 0 )
ORDER  BY CreatedOn
OPTION (MERGE UNION)