2016-06-08 102 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通過刪除自己重新回到桌子上的計劃的分支,可能具有更多的補償。你在測試時發現了什麼? –

相關問題