2010-02-06 2 views
8

Ho scritto il seguente codice, funziona bene, ma ci vogliono 3 secondi per completare se la tabella contiene un milione di record. C'è un modo per ottimizzare il seguente codice.T-sql: come eseguire il paging ottimizzato?

DBCC DROPCLEANBUFFERS; 
DBCC FREEPROCCACHE; 

DECLARE @Page_Size int; 
DECLARE @Page_Number int; 
DECLARE @Lower_Bound int; 
DECLARE @Upper_Bound int; 

SET @Page_Size = 30; 
SET @Page_Number = 30000; 
SET @Lower_Bound = (@Page_Number - 1) * @Page_Size; 
--SET @Upper_Bound = @Page_Number * @Page_Size; 


WITH Customers AS--(Row_Numbr, Record_Id, First_Name, 
     Middle_Name, Last_Name, Email, Telephone) AS 
(

    SELECT ROW_NUMBER() 
     OVER 
     (ORDER BY Account.Customer.Record_Id) AS Row_Numbr, * 
    FROM Account.Customer 
) 

SELECT top(@Page_Size) * 
FROM Customers 
WHERE Row_Numbr > @Lower_Bound-- 
    AND Row_Numbr <= @Upper_Bound -- This is suppose to be faster 
--SELECT * FROM Customers 
--WHERE Row_Numbr > @Lower_Bound 
-- AND Row_Numbr <= @Upper_Bound 
+0

cosa dice il piano di esecuzione? – super9

+0

Quali indici hai? Puoi pubblicare lo script della tabella di creazione? –

+0

Correlati: http://stackoverflow.com/questions/1897436/row-number-over-not-fast-enough-with-large-result-set-any-good-solution –

risposta

14

Innanzitutto, perché DBCC DROPCLEANBUFFERS;? Questo è un reset a freddo del pool di buffer. A meno che non si voglia misurare e ottimizzare le prestazioni dell'IO dei dischi rigidi, a nessuno importa delle prestazioni di una cache fredda. Questo è non come funzionerà il tuo sistema. La memorizzazione nella cache delle pagine nel pool buffer è l'aspetto della prestazione più critico nei database e lo si elimina. È come presentarsi in una Ferrari senza motore e chiedere perché è così lento. Per le misurazioni delle prestazioni dovresti fare esattamente il di fronte allo: esegui la query 4-5 volte per scaldare la cache, quindi misurare.

In secondo luogo, qual è la struttura del tavolo? La tabella è Account.Customer ordinata per cluster di tabelle per Record_id? Se no, non otterrai mai le prestazioni che desideri, indipendentemente da come esprimi il tuo T-SQL.

E, ultimo ma non meno importante, quale sistema avete? Ha abbastanza RAM per memorizzare in cache l'intero database in memoria? Se no, acquista più RAM. Esistono altri processi che competono per la memoria, come IIS/Asp? Se sì, buttali sul loro server, dovresti n ever ever run the database on the same host as the web server se le prestazioni sono importanti.

Per una paginazione veloce alternativa considerare soluzioni ideate keyset:

/* moving up */ 
SELECT top(@Page_Size) * 
FROM Account.Customer 
WHERE Record_Id > @lastPageRecordId 
ORDER BY Record_Id; 

/* moving down */ 
SELECT top(@Page_Size) * 
FROM Account.Customer 
WHERE Record_Id < @firstPageRecordId 
ORDER BY Record_Id DESC; 

Un mazzo di chiavi guidato soluzione può chiedere direttamente alla ultima posizione e poi spaziare scansiona la pagina successiva/precedente, utilizzando la posizione chiave di indice cluster. La logica di paging (stato) deve ricordare l'ultimo e il primo tasto sulla pagina visualizzata per continuare da lì, invece di ricordare il numero di pagina.

Le soluzioni basate su Rowcount (così come LIMIT in MySQL) sono meno efficienti di quelle basate su keyset perché devono sempre contare i record per posizionarsi, invece di cercare direttamente la posizione come possono farlo i keysets.

+0

Ma Record_Id può essere 1, 3, 7, 10, 11, 12, 13, 17. Le pagine non saranno uguali, anche l'ultima pagina probabilmente non coprirà l'intera tabella. In questo caso, come ottenere l'accuratezza @lastPageRecordId? Proprio ora ho capito che vuoi migliorare le prestazioni usando l'indice Record_Id, quindi vuoi che io modifichi la clausola WITH e usi Record_Id invece di Row_Number, non sono sicuro di come modificare la clausola WITH ancora !! Ti sto seguendo? – Costa

+1

Dire di voler visualizzare le pagine di 3. La prima volta che si dice TOP (3) ... ORDER BY RecordId e si ottiene 1,3,7. Per la prossima pagina chiedi il TOP (3) ..DOVE RecordId> 7 ... ORDER BY ... e ottieni 10,11,12. Per andare giù chiedi TOP (3) ... DOVE RecordId <10 ... ORDINA PER ... DESC e ottieni 7,3,1. Per andare avanti chiedi TOP (3) ... DOVE RecordId> 12 ... ORDINA DA ... e ottieni 13,17. Si utilizza il primo e l'ultimo RecordId sulla pagina come * chiavi *, non come gradi. –

+0

@Remus Ciao, quindi la soluzione del keyset è più veloce del cercapersone? Ive ha provato a creare una procedura memorizzata memorizzata basata sul codice ur sopra ma non ottengo risultati restituiti. Qualche idea? Crea Proc PagingSample \t ((at) int PAGE_SIZE, \t (at) firstPageRecordId int) AS iniziare \t selezionare Home ((at) PAGE_SIZE) * \t DA dbo.data \t WHERE (a) (at) IDENTITY <(at) firstPageRecordId \t ORDINA DA DateTime DESC; fine –

2

Io uso questa stored procedure:

CREATE PROCEDURE sp_PagedItems 
    (
    @Page int, 
    @RecsPerPage int 
    ) 
AS 

-- We don't want to return the # of rows inserted 
-- into our temporary table, so turn NOCOUNT ON 
SET NOCOUNT ON 


--Create a temporary table 
CREATE TABLE #TempItems 
(
    ID int IDENTITY, 
    Name varchar(50), 
    Price currency 
) 


-- Insert the rows from tblItems into the temp. table 
INSERT INTO #TempItems (Name, Price) 
SELECT Name,Price FROM tblItem ORDER BY Price 

-- Find out the first and last record we want 
DECLARE @FirstRec int, @LastRec int 
SELECT @FirstRec = (@Page - 1) * @RecsPerPage 
SELECT @LastRec = (@Page * @RecsPerPage + 1) 

-- Now, return the set of paged records, plus, an indiciation of we 
-- have more records or not! 
SELECT *, 
     MoreRecords = 
    (
    SELECT COUNT(*) 
    FROM #TempItems TI 
    WHERE TI.ID >= @LastRec 
    ) 
FROM #TempItems 
WHERE ID > @FirstRec AND ID < @LastRec 


-- Turn NOCOUNT back OFF 
SET NOCOUNT OFF 
1

Se qualcuno sta usando SQL Server 2012 - una nuova funzionalità è stata aggiunta nella clausola ORDER BY, per interrogare l'ottimizzazione di un insieme di dati, rendendo il lavoro più facile con paginazione dei dati per chiunque scriva anche in T-SQL per l'intero piano di esecuzione in SQL Server. Riferimento here.