选择几乎所有具有许多“分组依据”列的行时提高查询性能

Zai*_*rja 6 performance sql-server index-tuning sql-server-2012 query-performance

我有一个包含 20 列和大约 600,000 条记录的表。最大行大小仅为 100 字节左右。该表每隔几天重新填充一次,但记录数保持大致相同。

现在只有一个聚集索引:主键的 int 标识列。

我有几个依赖于这个表的查询和视图,通常需要 5-10 秒来执行。当我简单地选择所有记录 ( select * from myTable) 时,检索所有结果大约需要 4 秒钟。

我一直无法找到在 SQL Server 中选择 500,000 条记录的相关基准。这个时间是典型的吗?

这是我在表上执行的典型查询:

select  CO.Company
    ,CO.Location
    ,CO.Account
    ,CO.SalesRoute
    ,CO.Employee
    ,CO.ProductType
    ,CO.Item
    ,CO.LoadJDate
    ,CO.CommissionRate
    ,SUM(CO.[Extended Sales Price]) AS Sales_Dollars
    ,SUM(CO.[Delivered Qty]) AS Quantity
from    dbo.Commissions_Output CO
where   CO.[Extended Sales Price] <> 0
group by    CO.Company
        ,CO.Location
        ,CO.Account
        ,CO.SalesRoute
        ,CO.Employee
        ,CO.ProductType
        ,CO.Item
        ,CO.LoadJDate
        ,CO.CommissionRate
Run Code Online (Sandbox Code Playgroud)

当我在表上至少有一个非聚集索引时,我得到以下结果:

扫描计数 18,逻辑读取 18372;CPU 时间 = 24818 毫秒,已用时间 = 8614 毫秒。

我尝试了各种索引和组合(过滤器列上的索引,包括分组列;所有过滤器/分组列上的索引并包括聚合列;等等)。它们都提供相同的性能,并且几乎总是使用相同的执行计划。

当我删除除聚集索引 (PK) 之外的所有内容时,性能通常最多可提高 3-4 秒。当扫描计数减半时,逻辑读取减少。

关于数据的一些注意事项:分组前的select和where子句的结果大约是50万行(几乎是整个表)。通过分组只合并了大约 10,000 行,在分组后仍然留下大约 500,000 条记录。

没有非聚集索引的执行计划显示,成本最高的操作是哈希匹配 (49%) 和 where 子句的聚集索引扫描 (35%)。MSSMS 建议我为[Extended Sales Price]. 具有至少一个非聚集索引的执行计划显示成本最高的操作是排序(在 group-by 列上)。

鉴于此查询返回几乎所有记录并且 group-by 几乎没有减少行数,这是查询可以达到的速度吗?它看起来很慢,我阅读了关于人们在 1000 毫秒内返回数十万行的文章和问题。我错过了什么,或者这是一个相当典型的速度?规范化这个表目前不是一个选项,我不确定这会有多大帮助。

最后一个注意事项:我有几个视图和其他涉及加入该表的查询(有一些规范化)。起初我认为这些视图和查询由于连接不良等原因而变慢,但看起来真正的罪魁祸首是这张表和对它的初始查询。大多数查询和视图都适用于表中的几乎所有数据。当我选择单列或一小部分行时,执行时间很好,但这种情况很少见。

更新:这里是所有的执行时间、计划和 IO 统计信息。我没有将每个查询运行数百次,但执行时间似乎没有超过 1000 毫秒的“热”与“冷”的差异。

无非聚集索引,无 MAXDOP 设置: nonc_nomaxdop

表'Commissions_Output'。扫描计数 9,逻辑读 11263,物理读 0,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。

CPU 时间 = 6690 毫秒,已用时间 = 4605 毫秒。(最大 CPU 时间 = 7516 毫秒,最小运行时间 = 3754 毫秒。)

使用非聚集索引,无 MAXDOP 设置: nc_nomaxdop

表'Commissions_Output'。扫描计数 16,逻辑读取 6227

CPU 时间 = 6591 毫秒,已用时间 = 3717 毫秒。

没有非聚集索引,MAXDOP 1 nonc_maxdop

表'Commissions_Output'。扫描计数 1,逻辑读取 10278

CPU 时间 = 2656 毫秒,已用时间 = 4991 毫秒。

使用非聚集索引,MAXDOP 1 nc_maxdop

表'Commissions_Output'。扫描计数 1,逻辑读取 10278

CPU 时间 = 2656 毫秒,已用时间 = 4991 毫秒。

使用的非聚集索引:

create nonclustered index IX_NC_Comm_Output on dbo.Commissions_Output([Extended Sales Price])
include (company, location, account, salesroute, employee, producttype, item, loadjdate, commissionrate, [delivered qty])
Run Code Online (Sandbox Code Playgroud)

bar*_*ver 5

我想从不同的角度来解决这个问题。

我同意@ypercube 的观点,即您始终可以建立索引以简化查询。那说:

  • 您提到该表包含的数据量相对较少
  • 表每隔几天才重建一次
  • 您显示对文本列的聚合是典型查询中最昂贵的部分,即使在创建覆盖索引后您也会遇到

为什么不更进一步并预先创建聚合,以便查询不需要多次工作?似乎是索引视图的理想情况,您可以在早期实现聚合查询输出,或者在将数据加载到Commissions_Output. 无论哪种方式,您都只会牺牲很少的磁盘空间来提高性能。

索引视图确实有很多关于您想要使用它们的环境的限制,但在某些情况下自动使用而不是原始表有很大的好处。


ype*_*eᵀᴹ 3

您测试过的非聚集索引并不是最适合此查询的索引。它可以用于WHERE子句和执行索引扫描而不是全表扫描,但不能用于GROUP BY.

最好的索引必须是部分索引(以过滤子句中不需要的行WHERE),然后使用 中使用的所有列GROUP BY,然后INCLUDE使用 中的所有其他列SELECT

CREATE INDEX special_ix 
  ON dbo.Commissions_Output
    ( company, location, account, 
      salesroute, employee, producttype, 
      item, loadjdate, commissionrate ) 
INCLUDE 
  ( [Extended Sales Price], [Delivered Qty] ) 
WHERE 
  ( [Extended Sales Price] <> 0 ) ;
Run Code Online (Sandbox Code Playgroud)