我有一个表[Documents]
,其中包含以下列:
Name (string)
Status (string)
DateCreated [datetime]
此表有大约100万条记录。这三列中的所有三列都有一个索引(每个列都有一个索引)。
当我运行此查询时:
select top 50 *
from [Documents]
where (Name = 'None' OR Name is null OR Name = '')
and Status = 'New';
执行速度非常快(300毫秒)
如果我使用ORDER BY
子句运行相同的查询,那么它真的很慢(3000毫秒)
select top 50 *
from [Documents]
where (Name = 'None' OR Name is null OR Name = '')
and Status = 'New'
order by DateCreated;
我理解它在另一个索引(DateCreated
)中搜索,但是它真的应该慢得多吗?如果是这样,为什么?我能做些什么来加速这个查询(复合索引)?
由于
顺便说一句:包括DateCreated
在内的所有索引的碎片都非常低,实际上我进行了重组并且没有改变任何东西。
答案 0 :(得分:6)
至于查询速度较慢的原因,查询需要“按顺序”返回行,因此需要进行排序,或者需要使用索引。
将索引与CreatedDate的前导列一起使用,SQL Server可以避免排序。但SQL Server还必须访问基础表中的页面,以评估是否要返回该行,查看Status和Name列中的值。
如果优化器选择不使用CreatedDate作为前导列的索引,则需要首先找到满足谓词的所有行,然后执行排序操作以按顺序获取这些行。然后它可以从排序集返回前五十行。 (SQL Server不一定需要对整个集合进行排序,但它需要遍历整个集合,并进行足够的排序以保证它需要返回“前五十”。
注意:我怀疑您已经知道这一点,但澄清一下:SQL Server在ORDER BY
之前尊重TOP 50
。如果您希望任何50行满足谓词,但不一定是具有最低值DateCreated的50行,您可以重构/重写查询,获取(最多)50行,然后执行那些排序。
提高效果的几点想法
添加综合索引(如其他答案所示)可能会有所改进,例如:
ON Documents (Status, DateCreated, Name)
SQL Server可能能够使用该索引来满足Status上的等式谓词,并且还可以在没有排序操作的情况下以DateCreated顺序返回行。 SQL服务器也可以从索引中满足Name上的谓词,限制基础表中页面的查找次数,它需要为返回的行执行,以获取行的“所有”列
对于SQL Server 2008或更高版本,我会考虑过滤索引...取决于Status ='New'的基数(即,如果满足谓词Status='New'
的行是一个相对较小的子集该表。
CREATE NONCLUSTERED INDEX Documents_FIX
ON Documents (Status, DateCreated, Name)
WHERE Status = 'New'
我还会修改查询以指定ORDER BY Status, DateCreated, Name
这样order by子句与索引匹配,它并没有真正改变返回行的顺序。
作为一个更复杂的替代方案,我会考虑添加一个持久计算列并在该
上添加一个过滤索引 ALTER TABLE Documents
ADD new_none_date_created AS
CASE
WHEN Status = 'New' AND COALESCE(Name,'') IN ('','None') THEN DateCreated
ELSE NULL
END
PERSISTED
;
CREATE NONCLUSTERED INDEX Documents_FIXP
ON Documents (new_none_date_created)
WHERE new_none_date_created IS NOT NULL
;
然后可以重写查询:
SELECT TOP 50 *
FROM Documents
WHERE new_none_date_created IS NOT NULL
ORDER BY new_none_date_created
;
答案 1 :(得分:1)
如果DateCreated
字段表示对表的插入时间,则可以创建整数id字段并按该整数字段排序。
答案 2 :(得分:0)
您需要2列索引:(名称,DateCreated)。索引中字段的顺序很重要。因此,将Name的索引替换为两列的新索引(Name,DateCreated)。