为什么索引视图的性能会比等效表差得多?

Pie*_*ens 0 sql-server materialized-view sql-server-2014

我有这张桌子

create table SkaData.FiscalPeriod(
     PeriodNo               int     not null constraint FiscalPeriod_FK_BasePeriod    references SkaData.Period(Number)
    ,YearEndMonthNo         int     not null constraint Fiscalperiod_CK_YearEnd       check (YearEndMonthNo between 1 and 12)
    ,ContributionType       char(4) not null constraint FiscalPeriod_CK_ContribType   check(ContributionType in ('Base','Cr','Dr'))

    ,ContributionSign       int     not null constraint FiscalPeriod_CK_ContribSign   check(ContributionSign in (+1,-1))
    ,ContributionPeriodNo   int     not null constraint FiscalPeriod_FK_ContribPeriod references SkaData.Period(Number)

    ,constraint FiscalPeriod_PK unique clustered (PeriodNo,YearEndMonthNo,ContributionType)
);
Run Code Online (Sandbox Code Playgroud)

目前填充为

    insert Skadata.FiscalPeriod(
        PeriodNo,YearEndMonthNo,ContributionType,ContributionperiodNo,ContributionSign)
        select
             data.PeriodNo
            ,YearEndMonthNo
            ,ContributionType
            ,ContributionPeriodNo   = pvt.PeriodNo
            ,ContributionSign       = pvt.Sign
        from (
            select 
                 YearEndMonthNo = N
                ,PeriodNo       = Period.Number
                ,CreditPeriodNo = Period.Number - MonthNo
                ,DebitPeriodNo  = Period.Number - MonthNo + N - 12
            from SkaData.Period
            join (select * from 
                (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12) )Number(N)
            )Number(N)  on Number.N >= MonthNo + 12 - Period.Number
        ) data
        cross apply (values
             (PeriodNo,         +1, 0,  'Base')
            ,(CreditPeriodNo,   +1, 12, 'Cr')
            ,(DebitPeriodNo,    -1, 12, 'Dr')
        ) pvt(PeriodNo, Sign, TestPeriod,ContributionType)
        where YearEndMonthNo   <> pvt.TestPeriod
Run Code Online (Sandbox Code Playgroud)

它允许从日历 YTD 值轻松转换为非日历 YTD 值。

但是,该表是派生表,这意味着必须在实例化新年份时对其进行维护。我想用索引视图替换它以消除这种维护的需要。

我已经证明了上面的 select 与下面的等效,使用 NUMBERS 表,它是精确和确定的,不使用子查询或 APPLY 运算符,允许它成为索引视图的定义:

create view SkaData.FiscalPeriod with schemabinding as
    select
         PeriodNo               = cast(Period.Number as int)
        ,YearEndMonthNo         = cast(months.N/3 + 1 as int)
        ,ContributionType       = cast(case months.N%3 when 0 then 'Base'
                                                       when 1 then 'Cr'
                                                       when 2 then 'Dr'
                                  end as char(4))
        ,ContributionPeriodNo   = cast(case months.N%3 when 0 then Period.Number
                                                       when 1 then Period.Number - MonthNo
                                                       when 2 then Period.Number - MonthNo + (months.N/3) + 1 - 12
                                  end as int)
        ,ContributionSign       = cast(case months.N%3 when 0 then +1
                                                       when 1 then +1
                                                       when 2 then -1
                                  end as int)
    from SkaData.Period
    join SkaData.Number months   on (months.N/3) + 1 >= MonthNo + 12 - Period.Number
                                and months.N between 0 and 33
go

create unique clustered index FiscalPeriod_PK on SkaData.FiscalPeriod(PeriodNo,YearEndMonthNo,ContributionType);
go
Run Code Online (Sandbox Code Playgroud)

请注意,视图上的单个聚集索引与原始表上的(一个且唯一的)聚集索引相同。

但是,针对索引视图运行的几个查询比针对原始表运行得更慢(平均约 3*,范围高达约 6*,更慢)。

有谁知道为什么会发生这种情况?引擎中的一个可能的错误是不相同地对待两个相同的聚集索引吗?我的测试数据目前仅涵盖两个时期,相隔一年。

我最初认为这可能是由于视图的列可以为空,但是使用isnull来合并它们只会使查询变得如此缓慢,我什至无法衡量性能。

我在 SQL Server 2014 上:

Microsoft SQL Server 2014 (SP2-GDR) (KB4019093) - 12.0.5207.0 (Intel X86) 
    Jul  3 2017 02:37:05 
    Copyright (c) Microsoft Corporation
    Developer Edition on Windows NT 6.1 <X64> (Build 7601: ) (WOW64)
Run Code Online (Sandbox Code Playgroud)

Ste*_*ble 6

索引视图通常不能用作表的替代品。原因是WITH (NOEXPAND)提示经常需要。通常,视图会被扩展(尽管有索引)并使用基础定义。保罗·怀特写了关于前面

更改查询来自: SELECT * FROM SkaData.FiscalPeriod

到: SELECT * FROM SkaData.FiscalPeriod WITH(NOEXPAND)