2016-06-08 61 views
3

我有一个统计表(仅附加),其活动记录是最高的WordsReadMinutesReadUserId,BookId,UserGroupIdDate写这个查询更有效的方法吗?

现在我想查找自给定日期以来总共MinutesReadPagesRead

该查询工作 ...但它是在一个大的日期范围可怕的慢:

SELECT 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 

执行计划是这样的:

execution plan

没有扫描键查询或任何其他低垂果实。

任何其他的想法,我可以得到更好的性能出这个查询?

+0

请编辑您的问题以包含相关表格的DDL,一些示例数据作为DML和期望的结果。 –

回答

1

我通过调整索引来优化它。我的线索是,72%的查询成本是在那种子条款中。索引IX_ReadingStatDaily_User正在索引字段UserId,BookId,UserGroupIdDate,而INCLUDEWordsReadPagesRead(和一些其他字段)。我将WordsReadPagesRead转移到索引本身中,突然查询占用了原始时间的三分之一。

+0

如果您现在重新尝试Felix的已删除答案,会发生什么情况? (当我读到你的问题时,我也会建议) –

+0

@Damien_The_Unbeliever我在调整索引后尝试了它们。我的速度还是更快。 –

+1

您可能没有按照rownumber版本的最佳顺序添加两列,以避免排序。 (日期ASC,UserId ASC,BookId ASC,UserGroupId ASC,WordsRead DESC,PagesRead DESC)包括(MinutesRead)' –

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 
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) 
东西
+0

Okeeeey ...我本能地回想起了转换数字字符串和后面。难道不是,效率极低? –

+0

@ShaulBehr通过删除自己重新回到桌子上的计划的分支,可能具有更多的补偿。你在测试时发现了什么? –

相关问题