我在表上有一个持久的计算列,该表只是由串联的列组成,例如
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
在这Comp
不是唯一的,并且D是的每个组合的有效起始日期A, B, C
,因此我使用以下查询来获取每个的结束日期A, B, C
(基本上是Comp的相同值的下一个开始日期):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
然后,我在计算列中添加了一个索引来协助此查询(以及其他查询):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
查询计划使我感到惊讶。我本以为,因为我有一个where子句指出了这一点,D IS NOT NULL
并且我正在按进行排序Comp
,并且没有引用索引之外的任何列,所以可以使用计算列上的索引来扫描t1和t2,但是我看到了聚集索引扫描。
因此,我强制使用此索引来查看它是否产生了更好的计划:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
哪个给了这个计划
这表明正在使用密钥查找,其详细信息是:
现在,根据SQL-Server文档:
如果该列在CREATE TABLE或ALTER TABLE语句中标记为PERSISTED,则可以在使用确定性表达式但不精确的表达式定义的计算列上创建索引。这意味着数据库引擎将计算出的值存储在表中,并在更新计算出的列所依赖的任何其他列时更新它们。当数据库引擎在列上创建索引以及在查询中引用该索引时,数据库引擎将使用这些持久值。当数据库引擎无法准确证明返回计算列表达式的函数(特别是在.NET Framework中创建的CLR函数)是否既确定又精确时,使用此选项可以在计算列上创建索引。
因此,如果像文档所说的那样“数据库引擎将计算出的值存储在表中”,并且该值也存储在我的索引中,为什么在不引用A,B和C时需要进行键查找来获取A,B和C根本没有查询?我认为它们被用来计算Comp,但是为什么呢?另外,为什么查询可以在上使用索引t2
,但不能在上使用索引t1
?
注意:我已经标记了SQL Server 2008,因为这是我的主要问题所在的版本,但我在2012年也得到了相同的行为。