LIMIT 10..20 w SQL Server

161

Próbuję zrobić coś takiego:

SELECT * FROM table LIMIT 10,20

lub

SELECT * FROM table LIMIT 10 OFFSET 10

ale używając SQL Server

Jedyne rozwiązanie, które znalazłem, wygląda na przesadę:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

Ja również znaleźć :

SELECT TOP 10 * FROM stuff; 

... ale to nie jest to, co chcę robić, ponieważ nie mogę określić limitu początkowego.

Czy mogę to zrobić w inny sposób?

Ciekawe, czy istnieje powód, dla którego SQL Server nie obsługuje tej LIMITfunkcji lub czegoś podobnego? Nie chcę być podły, ale to naprawdę brzmi jak coś, czego potrzebuje DBMS ... Jeśli tak, to przepraszam za taką ignorancję! Pracuję z MySQL i SQL + od 5 lat, więc ...

marcgg
źródło
1
Korzystanie z CTE ROW_NUMBER()i ograniczanie TOPszerokości zakresu i WHEREwarunku ograniczenia zakresu jest najlepsze, jakie udało mi się osiągnąć. Zauważyłem również znacznie lepszą wydajność, jeśli TOPklauzula używa literału zamiast zmiennej
Jodrell.
Problem z każdym rozwiązaniem obejmującym ROW_NUMBER () polega na tym, że jeśli nie wiesz z góry, jakie kolumny będziesz mieć, i masz łączenia, a połączone tabele mają tę samą nazwę kolumny, otrzymasz komunikat „The column „xxx” zostało określone wiele razy ”. Nie jest to tak rzadkie, jak mogłoby się początkowo wydawać. Używam Dapper, a wszystkie moje tabele mają kolumnę Id. Dapper dzieli i mapuje na tym, więc nie chcę zmieniać ich nazwy, ale nie mogę użyć aliasu SELECT * FROM ([oryginalne zapytanie]). Nie znalazłem jeszcze rozwiązania!
Steve Owen

Odpowiedzi:

104

LIMITKlauzula nie jest częścią standardowego SQL. Jest obsługiwany jako rozszerzenie dostawcy do SQL przez MySQL, PostgreSQL i SQLite.

Bazy danych innych marek mogą mieć podobne funkcje (np. TOPW Microsoft SQL Server), ale nie zawsze działają one identycznie.

Trudno jest TOPnaśladować LIMITklauzulę w programie Microsoft SQL Server . Są przypadki, w których to po prostu nie działa.

Rozwiązanie, które pokazałeś, ROW_NUMBER()jest dostępne w Microsoft SQL Server 2005 i nowszych. To najlepsze rozwiązanie (na razie), które działa wyłącznie jako część zapytania.

Innym rozwiązaniem jest użycie TOPdo pobrania pierwszy licznik + przesunięć wierszy, a następnie użyj funkcji API szukać po pierwszym przesuniętych rzędach.

Zobacz też:

Bill Karwin
źródło
135

W przypadku programu SQL Server 2012 + możesz użyć .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 
Martin Smith
źródło
10
SQl Server 2012 wymaga określenia ORDER BY, gdy używasz OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY, podczas gdy MySql i SQLite nie wymagają ORDER BY, gdy używasz LIMIT 5,5
Tomas Kubes
4
@ qub1n - MySQL nie gwarantuje jednak, jakie wiersze odzyskasz w takim przypadku.
Martin Smith
3
Czy musisz użyć offset, czy możesz zostawić tę linię (zakładając, że nie chcesz przesunięcia)?
Cullub
Twoje przykładowe zapytanie działa dobrze, ale jeśli zmienię nazwę tabeli i uporządkuję według koloru, jak poniżej WYBIERZ * FROM DimProduct ZAMÓWIENIE PRZEZ ProductKey PRZESUNIĘCIE 5 RZĘDÓW POBIERZ NASTĘPNE 5 Parse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
RZĘDÓW
36

jak znalazłeś, jest to preferowana metoda serwera sql:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10
KM.
źródło
Dlaczego apo wewnętrznym wyborze? Zakładam, że dajesz wewnętrznemu aliasowi select, ale wtedy wydaje się, że nigdy go nie używasz ... Czy powinieneś to zrobić a.rowzamiast tylko row?
Lucas
3
@Lucas, musisz umieścić alias po ( )tabeli pochodnej, ale pozwoli to odejść, jeśli zapomnisz użyć go do odniesienia się do kolumn. Naprawiłem to jednak ...
KM.
dzięki, odkryłem to na własnej skórze (próbowałem pominąć alias).
Lucas,
1
Wziął +1: Jednak @MartinSmith „s odpowiedź jest głosowało więcej, po porównaniu z realizacji planu na takie podejście, to okazało się, że to rozwiązanie działa znacznie szybciej.
Surowy
10

Jeśli używasz SQL Server 2012+, zagłosuj na odpowiedź Martina Smitha i użyj rozszerzeń OFFSETi FETCH NEXTdo ORDER BY,

Jeśli masz pecha i utknąłeś z wcześniejszą wersją, możesz zrobić coś takiego,

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

Uważam, że jest funkcjonalnym odpowiednikiem

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

i najlepszy sposób, jaki znam, robiąc to w TSQL, przed MS SQL 2012.


Jeśli istnieje bardzo wiele wierszy, lepszą wydajność można uzyskać, używając tabeli tymczasowej zamiast CTE.

Jodrell
źródło
Głos za wskazaniem odpowiedzi Martina Smitha (i zamieszczeniem do niej linków), jednocześnie zapewniając rozwiązanie sprzed 2012 roku. Również dla rady przy stole tymczasowym, ponieważ masz rację :)
fujiiface
7

Niestety, ROW_NUMBER()to najlepsze, co możesz zrobić. Właściwie jest to bardziej poprawne, ponieważ wyniki a limitlub topklauzuli tak naprawdę nie mają znaczenia bez poszanowania określonej kolejności. Ale nadal jest to trudne.

Aktualizacja: Sql Server 2012 dodaje funkcję limitpodobną do funkcji poprzez słowa kluczowe OFFSET i FETCH . Jest to podejście ansi-standardowe, w przeciwieństwie do LIMITniestandardowego rozszerzenia MySql.

Joel Coehoorn
źródło
@Joel: Czy możesz wyjaśnić, dlaczego ROW_NUMBER () nie może numerować wierszy w taki sposób, w jaki wychodzą z ORDER BY? Zawsze się zastanawiałem, dlaczego napis „OVER (ORDER BY name)” jest obowiązkowy, ale myślę, że jest ku temu dobry powód. Albo przynajmniej powód.
Tomalak
3
ponieważ nie ma czegoś takiego jak zamówienie bez klauzuli order by. Otrzymujesz dowolną kolejność, w jakiej rekordy były dostępne dla serwera, i która może się zmienić z żądania zapytania na żądanie zapytania.
Joel Coehoorn
1
@marcgg: Nigdy nie przeczytałem żadnych wskazówek, że Microsoft planuje wdrożyć LIMIT. Nawet jeśli mają taki plan, dostawcy zamkniętych źródeł zwykle nie ogłaszają funkcji z wyprzedzeniem. Z pewnością byłaby to pomocna funkcja, ale nie wiemy, ile pracy wymagałoby zaimplementowanie, biorąc pod uwagę ich kod.
Bill Karwin
3
Jeśli nie chcesz powtarzać się w klauzuli ORDER BY, użyj aliasu ROW_NUMBER () zamiast oryginalnego zestawu kolumn.
Peter Radocchia
2
@Tomalak: Jeśli chodzi o SQL Server, kolejność używana do obliczenia ROW_NUMBER () jest całkowicie niezwiązana z kolejnością zestawu wyników. Dlatego musisz je określić osobno.
LukeH
6

Co powiesz na to?

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

Daje ci ostatnie 10 rzędów z pierwszych 20 rzędów. Wadą jest to, że kolejność jest odwrotna, ale przynajmniej jest łatwa do zapamiętania.

David Patrick
źródło
6
A jeśli w tabeli jest tylko 14 wierszy? Otrzymasz wiersze od 14 do 5, co nie jest tym samym, co wiersze zwrócone przez LIMIT 10 PRZESUNIĘCIE 10 (powinny to być wiersze od 14 do 11).
Bill Karwin
2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Powinien dać rekordy 11-20. Prawdopodobnie niezbyt wydajne przy zwiększaniu, aby uzyskać kolejne strony, i nie wiem, jak może na to wpłynąć zamawianie. Może trzeba będzie to określić w obu instrukcjach WHERE.

Andy
źródło
1

Dobrym sposobem jest stworzenie procedury:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

tak jak limit 0,2 /////////////// Wykonaj paginację 0,4

Wahaj Latif
źródło
1

Tylko dla rozwiązania rekordowego, które działa w większości silników baz danych, choć może nie być najbardziej wydajne:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

Uwaga: ostatnia strona nadal zawierałaby wiersze ReturnCount bez względu na to, czym jest SkipCount. Ale w wielu przypadkach może to być dobra rzecz.

YB
źródło
1

Odpowiednikiem LIMIT jest SET ROWCOUNT, ale jeśli chcesz mieć ogólną paginację, lepiej napisać takie zapytanie:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit
Satish Kumar sonker
źródło
0
select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

Drukuje wiersze od 10 do 15.

sjith
źródło
0

Jak dotąd ten format działa dla mnie (chociaż nie jest to najlepsza wydajność):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

Uwaga z boku, paginowanie danych dynamicznych może prowadzić do dziwnych / nieoczekiwanych wyników.

Charlie Affumigato
źródło
0

Z dokumentacji online MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ), oto ich przykład, który przetestowałem i działa, w celu pobrania określonego zestawu wierszy. ROW_NUMBER wymaga OVER, ale możesz zamówić, co chcesz:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;
Shannon WM
źródło
0

Użyj całego serwera SQL:; z tbl as (SELECT ROW_NUMBER () over (order by (select 1)) as RowIndex, * from table) wybierz top 10 * z tabeli, gdzie RowIndex> = 10

Phạm Tấn Lợi
źródło
-3
 SELECT * FROM users WHERE Id Between 15 and 25

wydrukuje od 15 do 25 jako podobny limit w MYSQl

SR1
źródło
2
Co się stanie, jeśli użytkownik usunie rekord między 15 a 25?
Gökçer Gökdal