2016-07-31 13 views
1

Dies ist, was ich versuche zu erreichen. Ich habe einige Steuerelemente, die Paging verwenden, und ich möchte Abruf und Offset in dem Plan verwenden. Also ich bin vertraut mit der Verwendung einer CTE, um den Abfrageplan zu verkürzen, aber ich muss eine WHERE-Klausel nicht nur eine select * liefern. Unten ist ein vollständiger ausführbarer Test, der meiner Meinung nach nicht so effizient ist, wie er sein könnte. Wenn jemand auf eine effizientere Art und Weise der Rückgabe meines Datensatzes unter Beibehaltung der ursprünglichen vollständigen Zählung hinweisen könnte, wäre ich dankbar!Der effektivste Abfrageplan unter Verwendung von CTE und Offset/Fetch Next, während immer noch eine where-Klausel bereitgestellt wird

if exists(select 1 from sys.tables where name = 'TestTableSize') 
begin 
drop table TestTableSize; 
end 

CREATE TABLE dbo.TestTableSize 
(
    MyKeyField VARCHAR(10) NOT NULL, 
    MyDate1 DATETIME NOT NULL, 
    MyDate2 DATETIME NOT NULL, 
    MyDate3 DATETIME NOT NULL, 
    MyDate4 DATETIME NOT NULL, 
    MyDate5 DATETIME NOT NULL 
); 
go 

DECLARE @RowCount INT 
DECLARE @RowString VARCHAR(10) 
DECLARE @Random INT 
DECLARE @Upper INT 
DECLARE @Lower INT 
DECLARE @InsertDate DATETIME 

SET @Lower = -730 
SET @Upper = -1 
SET @RowCount = 0 

WHILE @RowCount < 100000 
BEGIN 
    SET @RowString = CAST(@RowCount AS VARCHAR(10)) 
    SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0) 
    SET @InsertDate = DATEADD(dd, @Random, GETDATE()) 

    INSERT INTO TestTableSize 
     (MyKeyField 
     ,MyDate1 
     ,MyDate2 
     ,MyDate3 
     ,MyDate4 
     ,MyDate5) 
    VALUES 
     (REPLICATE('0', 10 - DATALENGTH(@RowString)) + @RowString 
     , @InsertDate 
     ,DATEADD(dd, 1, @InsertDate) 
     ,DATEADD(dd, 2, @InsertDate) 
     ,DATEADD(dd, 3, @InsertDate) 
     ,DATEADD(dd, 4, @InsertDate)) 

    SET @RowCount = @RowCount + 1 
END 


DECLARE 
    @PageSize INT = 10, 
    @PageNum INT = 1; 

WITH cte AS(
    select * from TestTableSize 
    where MyDate1 between '1/1/2015' and '2/1/2015' 
), cteCount AS (
    SELECT COUNT(1) AS MaxRows FROM cte 
) 
SELECT * 
FROM cte, cteCount 
ORDER BY cte.MyKeyField 
    OFFSET (@PageNum-1)*@PageSize ROWS 
    FETCH NEXT @PageSize ROWS ONLY 
+1

Wirklich gut präsentierte Frage. Ich habe es ausgeführt und es dauerte nur 17 Sekunden. Das einzige, was ich vorschlagen kann, ist einen gruppierten Index auf MyDate1 zu setzen. – Missy

+0

Also die obige Abfrage gibt Ihr gewünschtes Ergebnis wie es ist, oder gibt es zusätzliche 'WHERE' Kriterien, die Sie hinzufügen wollten? –

Antwort

1

Ich gehe davon aus Sie suchen nicht die Leistung Ihrer Testtabelle bauen zu verbessern, so dass die einzige Sache, die ich für die Bereinigung zu sehen ist, dass Ihr zweite cte nicht benötigt wird, einfach COUNT(*) OVER() in der Spitze kann cte:

DECLARE 
    @PageSize INT = 10, 
    @PageNum INT = 1; 

WITH cte AS(
    select *,COUNT(*) OVER() AS CT from TestTableSize 
    where MyDate1 between '1/1/2015' and '2/1/2015' 
) 
SELECT * 
FROM cte 
ORDER BY cte.MyKeyField 
    OFFSET (@PageNum-1)*@PageSize ROWS 
    FETCH NEXT @PageSize ROWS ONLY 

Darüber hinaus werden einige Indizierung helfen, MyDate1 und MyKeyField sicherlich indiziert werden sollen.

+0

Hart - von dem, was ich sammle Graf (*) Over() ist sehr teuer, deshalb verwende ich die 2. cte. http://StackOverflow.com/a/20130331/5304320 – JL1

+0

Sorry das Problem mit der obigen Abfrage ist, dass das erste CTE mit einer Where-Klausel zusätzliche Plan Schritte verursacht, wenn nur eine Auswahl * nicht – JL1

+0

Das ist interessant, ich bin skeptisch, dass es schlechter abschneidet als die 2 'Cte'-Methode, aber ich muss das testen. Es macht Sinn, dass das Filtern Kosten hinzufügt. Ich glaube nicht, dass Sie die Abfrage signifikant umstrukturieren können. Ich denke, Indexierung ist die beste Möglichkeit, die Leistung zu verbessern. –