减少此 COUNT() 查询的持续时间

Mur*_*ilo 4 performance sql-server-2014 query-performance

我有一个包含 2,161,524 行的表。我认为计数查询花费的时间太长。

select count(mcon_codigo_pk) from tbMovimentoConta
-- count = 2,161,524
-- time = 9 seconds

select count(1) from tbMovimentoConta 
where con_codigo_fk = 1 
and mcon_data between '2015-01-05' and '2016-01-06'
-- count = 1,034,729 
-- time = 13 seconds
Run Code Online (Sandbox Code Playgroud)

细节:

  • 该列con_codigo_fkbigint并且具有外键索引(非聚集)
  • 该列mcon_datadatetime并且具有索引(非聚集)
  • 表有自增PK(聚集索引)
  • 还有更多三个具有索引的外来索引(所有索引都是由实体框架创建的)

索引创建脚本

CREATE NONCLUSTERED INDEX [ix_mcon_data] ON [dbo].[tbMovimentoConta]
(
    [mcon_data] ASC
)
WITH (
    PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON
);
GO
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我的电脑有 8GB RAM 和运行 Windows 10 的 Core i7。

我的查询真的需要很长时间还是平均预期时间?如果他们需要很长时间,我该怎么做才能让他们更快?

ype*_*eᵀᴹ 6

查询的覆盖索引有两种可能性:

  • 上的复合索引(con_codigo_fk, mcon_data)。这将涵盖所有类似的查询。如果您添加此索引,您(最有可能)可以安全地删除该索引,(con_codigo_fk)而将使用新的索引。其他索引(mcon_data)可以被不同的查询使用,所以我不会删除它。

    添加索引的代码:

    CREATE INDEX ix__con_codigo_fk__mcon_data       -- choose a name
      ON dbo.tbMovimentoConta
      (con_codigo_fk, mcon_data) ;
    
    Run Code Online (Sandbox Code Playgroud)
  • 上的过滤索引(mcon_data) WHERE (con_codigo_fk = 1)。这当然仅对具有特定值 ( 1) 的查询有用。
    此类索引的用例要少得多,因此它可能对您没有用处。

另一个问题是您使用BETWEEN的是日期时间类型。这会给您不准确的结果,因为它将包含具有确切日期时间的结果'2016-01-06' 00:00:00'

最好使用包含-不包含的范围:

select count(*) 
from tbMovimentoConta 
where con_codigo_fk = 1 
  and mcon_data >= '2015-01-05' 
  and mcon_data  < '2016-01-06' ;
Run Code Online (Sandbox Code Playgroud)

@Aaron Bertrand在博客中对此进行了更详细的解释:和魔鬼有什么BETWEEN共同点?


另一个问题 - 索引没有试图解决 - 是虽然带有WHERE条件的查询所需的时间可能是由于计划错误和缺乏适当的索引,第一个查询的时间,整个表计数( 9 秒)听起来有点太多了。(并且添加索引后查询的 1 秒也很高)。

我会在没有更多细节的情况下进行推测,因为解释可能来自许多不同的原因(一般/内存 SQL Server 设置、服务器上的高负载、广泛的聚集索引等),所以我建议您添加有关问题的更多详细信息或发布新问题(CREATE TABLE如果问题仅针对涉及此表的查询,则使用脚本)。

  • 添加了创建索引的语句。 (2认同)