2016-06-08 11 views
3

Ich habe eine Statistik-Tabelle (anhängen nur), deren aktive Datensatz ist derjenige mit der höchsten WordsRead und MinutesRead pro UserId, BookId, UserGroupId und Date.Effizientere Art, diese Abfrage zu schreiben?

Jetzt möchte ich die Summe MinutesRead und PagesRead seit einem bestimmten Datum finden.

Diese Abfrage funktioniert ... aber es ist schrecklich langsam über einen großen Zeitraum:

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 

Ausführungsplan sieht wie folgt aus:

execution plan

keine Scans, Schlüsselsuche oder jede andere niedrig hängenden Frucht.

Irgendwelche anderen Ideen, wie ich bessere Leistung aus dieser Abfrage erhalten könnte?

+0

Bitte bearbeiten Sie Ihre Frage, um die DDL der relevanten Tabelle, einige Beispieldaten als DML und das gewünschte Ergebnis einzubeziehen. –

Antwort

1

Ich habe es optimiert, indem ich einen Index optimiert habe. Mein Anhaltspunkt war, dass 72% der Abfragekosten in dieser Art Unterklausel waren. Der Index IX_ReadingStatDaily_User war die Indizierung der Felder UserId, BookId, UserGroupId und Date, mit INCLUDE auf WordsRead und PagesRead (und einige andere Felder). Ich habe und PagesRead in den Index selbst verschoben, und plötzlich dauert die Abfrage ein Drittel der ursprünglichen Zeit.

+0

Was passiert, wenn Sie Felix 'gelöschte Antwort jetzt erneut versuchen? (Was, wenn ich deine Frage gelesen habe, war, was ich auch vorgeschlagen hätte) –

+0

@Damien_The_Unbeliever Ich versuchte sie beide nach dem Optimieren des Index. Meine ist noch schneller. –

+1

Sie haben die beiden Spalten möglicherweise nicht in der optimalen Reihenfolge für die Versionsnummer hinzugefügt, um eine Sortierung zu vermeiden. Msgstr "CREATE INDEX IX_ReadingStatDaily_User ON dbo.ReadingStatDaily (Datum ASC, UserId ASC, BookId ASC, UserGroupId ASC, WordsRead DESC, PagesRead DESC) include (MinutesRead)' –

0

Würde so etwas funktionieren?

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

Ein anderer Weg, dies umschreiben könnte, ist

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 

die einen einfacheren Plan wie

enter image description here

Angenommen, Ihre Index ähnlich

ist etwas geben sollte
CREATE INDEX IX_ReadingStatDaily_User 
    ON dbo.ReadingStatDaily(Date ASC, 
          UserId ASC, 
          BookId ASC, 
          UserGroupId ASC) 
    INCLUDE (WordsRead, PagesRead, MinutesRead) 
+0

Okeeeey ... Ich wende mich instinktiv auf die Konvertierung von Zahlen zu Zeichenfolgen und zurück. Wird das nicht sehr ineffizient sein? –

+0

@ShaulBehr Potenziell mehr als kompensiert, indem dieser Zweig des Plans entfernt wird, der das Selbst wieder auf den Tisch bringt. Was findest du beim Testen? –

Verwandte Themen