更有效的编写此查询的方法?

时间:2016-06-08 06:53:45

标签: sql-server sql-server-2014

我有一个统计表(仅附加),其活动记录是WordsRead MinutesRead UserId BookIdUserGroupIdDate {}}和MinutesRead

现在我想找到自给定日期以来的总PagesReadSELECT Minutes = SUM(r.MinutesRead), Pages = SUM(r.PagesRead) FROM (SELECT DISTINCT r.Date, r.UserId, r.BookId, r.UserGroupId FROM dbo.ReadingStatDaily r WHERE r.Date >= @p0) r0 CROSS APPLY (SELECT TOP 1 r.MinutesRead, r.PagesRead FROM dbo.ReadingStatDaily r WHERE r0.Date = r.Date AND r0.UserId = r.UserId AND r0.UserGroupId = r.UserGroupId AND r0.BookId = r.BookId AND r.Date >= @p0 ORDER BY r.WordsRead DESC, r.PagesRead DESC) r

此查询正常工作 ...但在较大的日期范围内,它的速度非常慢:

FIRAuth.auth()?.createUserWithEmail

执行计划如下:

execution plan

没有扫描,关键查找或其他任何悬而未决的成果。

如何从这个查询中获得更好的性能?

3 个答案:

答案 0 :(得分:1)

我通过调整索引来优化它。我的线索是72%的查询成本是在那个排序子条款中。索引IX_ReadingStatDaily_User正在UserId和{{BookId上对UserGroupIdDateINCLUDEWordsRead以及PagesRead建立索引1}}(以及其他一些领域)。我将WordsReadPagesRead移到索引本身,突然查询占用原始时间的三分之一。

答案 1 :(得分:1)

另一种可以重写的方法是

DECLARE @p0 DATE = <what_ever>;

WITH r0
     AS (SELECT Date,
                UserId,
                BookId,
                UserGroupId,
                MAX(RIGHT(CONCAT('000000000', WordsRead), 10) + 
                    RIGHT(CONCAT('000000000', PagesRead), 10) + 
                    RIGHT(CONCAT('000000000', MinutesRead), 10) COLLATE Latin1_General_BIN2) AS highest_words_pages_minutes
         FROM   dbo.ReadingStatDaily
         WHERE  Date >= @p0
         GROUP  BY Date,
                   UserId,
                   BookId,
                   UserGroupId)
SELECT Minutes = SUM(0 + RIGHT(highest_words_pages_minutes, 10)),
       Pages = SUM(0 + SUBSTRING(highest_words_pages_minutes, 11, 10))
FROM   r0 

哪个应该提供更简单的计划,例如

enter image description here

假设您的索引类似于

CREATE INDEX IX_ReadingStatDaily_User
  ON dbo.ReadingStatDaily(Date ASC, 
                          UserId ASC, 
                          BookId ASC, 
                          UserGroupId ASC)
  INCLUDE (WordsRead, PagesRead, MinutesRead) 

答案 2 :(得分:0)

这样的事情会起作用吗?

select Minutes = SUM(r.MinutesRead), Pages = SUM(r.PagesRead) from (
    SELECT
      Date,
      UserId,
      BookId,
      UserGroupId,
      MinutesRead, 
      PagesRead,
      row_number() over (PARTITION BY Date, r.UserId, r.BookId, r.UserGroupId 
                         ORDER BY r.WordsRead DESC, r.PagesRead DESC) as RN
    FROM 
      dbo.ReadingStatDaily
    WHERE
      Date >= @p0
) X where RN = 1