2012-11-16 10 views
11

Sto cercando di alcuni expresion come questo (utilizzando SQL Server 2008)restituire le righe tra una gamma specifica, con una sola istruzione SELECT

SELECT TOP 10 columName FROM tableName 

Ma invece di che ho bisogno dei valori compresi tra 10 e 20. E Mi chiedo se ci sia un modo di farlo usando una sola istruzione SELECT.

Per esempio, questo è inutile:

SELECT columName FROM 
(SELECT ROW_NUMBER() OVER(ORDER BY someId) AS RowNum, * FROM tableName) AS alias 
WHERE RowNum BETWEEN 10 AND 20 

Perché le staffe di selezione interna è già tornando tutti i risultati, e sto cercando di evitare che, a causa di prestazioni.

risposta

0

Utilizzare SQL Server 2012 per recuperare/saltare!

SELECT SalesOrderID, SalesOrderDetailID, ProductID, OrderQty, UnitPrice, LineTotal 
FROM AdventureWorks2012.Sales.SalesOrderDetail 
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; 

Non c'è niente di meglio di quello che stai descrivendo per le versioni precedenti di SQL Server. Forse usare CTE, ma improbabile che faccia la differenza.

WITH NumberedMyTable AS 
(
    SELECT 
     Id, 
     Value, 
     ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber 
    FROM 
     MyTable 
) 
SELECT 
    Id, 
    Value 
FROM 
    NumberedMyTable 
WHERE 
    RowNumber BETWEEN @From AND @To 

oppure, è possibile rimuovere le prime 10 righe e quindi ottenere le 10 righe successive, ma io raddoppiare chiunque vorrebbe farlo.

+1

L'aggiornamento a SQL Server 2012 di solito non è un'opzione "schiocca le dita". E direi che * ci sono * migliori risposte nelle versioni precedenti di SQL Server. Ad esempio, perché non recuperare solo i valori * chiave * quando si applicano i numeri di riga, quindi unire quell'output (che sarà * molto * skinner) con la tabella? –

+0

@ AaronBertrand, hai ragione.Sono completamente d'accordo sul fatto che l'aggiornamento non è facile, ma nel caso in cui OP stia usando questa affermazione molto e si preoccupa davvero delle prestazioni, questo è qualcosa che lui/lei può prendere in considerazione. – RAS

7

C'è un trucco con row_number che non comporta l'ordinamento di tutte le righe.

Prova questo:

SELECT columName 
FROM (SELECT ROW_NUMBER() OVER(ORDER BY (select NULL as noorder)) AS RowNum, * 
     FROM tableName 
    ) as alias 
WHERE RowNum BETWEEN 10 AND 20 

Non è possibile utilizzare una costante nella order by. Tuttavia, puoi usare un'espressione che valuta una costante. SQL Server riconosce questo e restituisce semplicemente le righe rilevate, elencate correttamente.

+0

Non sono sicuro di come possa essere d'aiuto. Non sarà il caso che se ROW_NUMBER() viene applicato senza l'ordinamento esplicito, il paging sarà inutile (presentato in ordine arbitrario)? –

+0

Ho trovato che usando questo costrutto, i numeri di riga sono assegnati mentre le righe sono generate. O, più specificamente, non sembra esserci alcun ordinamento intermedio della tabella (almeno in termini di prestazioni). Di solito uso questo per assegnare gli ID numerici alle righe quando si usa 'SELECT INTO'. E, sì, i numeri delle righe sono arbitrari, ma le domande nella domanda non avevano clausole 'order by'. –

+0

Ma ancora, l'osservazione <> garantisce. Sono certo che l'ordine di presentazione è importante, ma di solito è lasciato fuori da queste domande per brevità, non perché vogliano incoraggiare risposte che potrebbero potenzialmente fornire risultati arbitrari. –

3

Perché pensi che SQL Server valuterà l'intera query interna? Supponendo che la colonna di ordinamento sia indicizzata, leggerà solo i primi 20 valori. Se sei davvero nervoso puoi fare questo:

Select 
    Id 
From (
    Select Top 20 -- note top 20 
    Row_Number() Over(Order By Id) As RowNum, 
    Id 
    From 
    dbo.Test 
    Order By 
    Id 
) As alias 
Where 
    RowNum Between 10 And 20 
Order By 
    Id 

ma sono abbastanza sicuro che il piano di query è lo stesso in entrambi i casi.

(Realmente) Risolto come nel commento di Aaron.

http://sqlfiddle.com/#!3/db162/6

+0

Tecnicamente vorresti inserire un ORDINARE. La selezione interna non è * garantita * per restituire le righe nello stesso ordine in cui sono applicate da ROW_NUMBER(), anche se questo è ciò che potresti osservare nella maggior parte dei casi. Nota anche che la query interna diventa sempre più costosa mentre ti sposti nella 2a, 3a, ... 500a pagina. Idealmente una soluzione fornirebbe prestazioni lineari. –

+0

Avresti bisogno di qualcosa come skip list per farlo in modo stateless. Non penso che SQL Server abbia una struttura di indice che fornisce prestazioni costanti per passare all'ennesima posizione in un indice. Potresti fare meglio se il set sottostante non fosse cambiato, potresti quindi precalutare gli offset della pagina e memorizzarli in una tabella separata. Grazie per la correzione che ordina la query esterna. – Laurence

+0

La query interna è in realtà quella che ha bisogno di un ordine (l'esterno fa per scopi di presentazione, ma l'interno ne ha bisogno per garantire la selezione della riga corretta). Inoltre non stavo suggerendo nulla riguardo la fornitura di apolidi ... gli utenti si aspettano che i dati sottostanti possano cambiare durante lo scorrimento. Ma cercare l'ennesima pagina * può * essere relativamente lineare in SQL Server, ma non se lo si fa con 'SELEZIONA TOP 10 FROM (SELEZIONA IN ALTO (pagina * pagesize) DA grande tavolo)' –

0

Un'altra opzione

SELECT TOP(11) columName 
FROM dbo.tableName 
ORDER BY 
CASE WHEN ROW_NUMBER() OVER (ORDER BY someId) BETWEEN 10 AND 20 
    THEN ROW_NUMBER() OVER (ORDER BY someId) ELSE NULL END DESC 
0

Si potrebbe creare una tabella temporanea che viene ordinato nel modo desiderato come:

SELEZIONE ROW_NUMBER() OVER (ORDER BY QualcheID) AS ROWNUM , * FROM tableName in ## tempTable ...

In questo modo si dispone di un elenco ordinato di riga S. e può solo interrogare per numero di riga le volte successive invece di eseguire la query interna più volte.

Problemi correlati