添加ORDER BY语句时,SQL查询很慢

时间:2016-04-11 21:56:43

标签: sql-server performance

我有一个表[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在内的所有索引的碎片都非常低,实际上我进行了重组并且没有改变任何东西。

3 个答案:

答案 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)。