我有这张桌子
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)
);
目前填充为
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
这允许从日历 YTD 值轻松转换为非日历 YTD 值。
然而,该表是一个派生表,这意味着它必须在实例化新年份时进行维护。我想用索引视图替换它以消除这种维护的需要。
我已经使用 NUMBERS 表证明了上面的 select 与下面的 select 的等价性,它是精确和确定的,不使用子查询或 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
请注意,视图上的单个聚集索引与原始表上的(唯一的)聚集索引相同。
但是,针对索引视图运行的几个查询比针对原始表运行得更慢(平均大约 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)
索引视图通常不能用作表的直接替代品。原因是
WITH (NOEXPAND)
经常需要提示。通常,视图会被扩展(尽管有索引)并使用底层定义。Paul White之前已经写过关于它的文章。更改查询来自:
SELECT * FROM SkaData.FiscalPeriod
至:
SELECT * FROM SkaData.FiscalPeriod WITH(NOEXPAND)