Jak napisać LINQ .Skip (1000) .Take (100) w czystym SQL?

93

Jaki jest odpowiednik SQL .Skip()metody w LINQ?

Na przykład: Chciałbym wybrać wiersze 1000-1100 z określonej tabeli bazy danych.

Czy jest to możliwe tylko w przypadku SQL? A może muszę zaznaczyć całą tabelę, a następnie znaleźć wiersze w pamięci? Najlepiej byłoby tego uniknąć, jeśli to możliwe, ponieważ stół może być dość duży.

Promień
źródło

Odpowiedzi:

78

W SQL Server 2005 i nowszych możesz użyć funkcji ROW_NUMBER . na przykład.

USE AdventureWorks;
GO
WITH OrderedOrders AS
(
    SELECT SalesOrderID, OrderDate,
    ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
    FROM Sales.SalesOrderHeader 
) 
SELECT * 
FROM OrderedOrders 
WHERE RowNumber BETWEEN 51 AND 60; --BETWEEN is inclusive
Dan Diplo
źródło
Zobacz link w mojej odpowiedzi, aby uzyskać więcej szczegółów. stackoverflow.com/questions/1744802/…
Mike Atlas
MIĘDZY 51 a 60 - to obejmuje.
Drew Miller,
1
Ale to najpierw wybierze wszystko, a następnie z tego wyboru zajmie tylko 10, prawda? A może pierwsze zapytanie / widok będzie miało już tylko 10?
Tadej
139

SQL Server 2012 i nowsze wersje dodały następującą składnię:

SELECT *
FROM Sales.SalesOrderHeader 
ORDER BY OrderDate
OFFSET (@Skip) ROWS FETCH NEXT (@Take) ROWS ONLY
John Gietzen
źródło
11
Zauważ, że musisz użyć polecenia ORDER BY ___, aby użyć polecenia OFFSET ... nie, że powinieneś kiedykolwiek próbować paginować bez zamówienia.
James Haug,
Zwróć również uwagę, że 'nowa' składnia ma dziwny spadek wydajności liniowy z @skip! Podejście numer_wiersza NIE ma tego (testowane tylko w kolejności indeksowanej). Jednak dla lo @Skip mniej niż 20, nowa składnia jest szybsza niż podejście row_number.
Eske Rahn
22

LINQ to SQL robi to za pomocą funkcji okienkowej ROW_NUMBER:

  SELECT a,b,c FROM 
   (SELECT a,b,c, ROW_NUMBER() OVER (ORDER BY ...) as row_number
    FROM Table) t0
   WHERE to.row_number BETWEEN 1000 and 1100;

To działa, ale potrzeba wytworzenia row_number z ORDER BY może skutkować posortowaniem zapytania po stronie serwera i spowodować problemy z wydajnością. Nawet jeśli indeks może spełnić wymaganie ORDER BY, zapytanie nadal musi policzyć 1000 wierszy, zanim zacznie zwracać wyniki. Zbyt często programiści o tym zapominają i po prostu wrzucają kontrolę paginacji do tabeli 5 milionów wierszy i zastanawiają się, dlaczego pierwsza strona jest zwracana o wiele szybciej niż ostatnia ...

Niemniej jednak, użycie ROW_NUMBER () jest prawdopodobnie najlepszą równowagą między łatwością użycia a dobrą wydajnością, pod warunkiem, że unikasz sortowania (warunek ORDER BY można spełnić za pomocą indeksu).

Remus Rusanu
źródło
1
Dzięki za dodatkowe informacje o wydajności, będę musiał uważać i przetestować.
Ray
Przetestowane i dla mojej półmilionowej tabeli wierszy ta ostatnia strona jest około 7 razy wolniejsza niż pierwsza. Nie idealne, ale dla mnie do przyjęcia.
Ray
6

Spróbuj tego:

select * from [Table-Name] order by [Column-Name] 
offset [Skip-Count] rows
FETCH NEXT [Take-Count] rows only

Przykład:

select * from Personals order by Id
offset 10 rows            --------->Skip 10
FETCH NEXT 15 rows only   --------->Take 15
Fereydoon Barikzehy
źródło
4

Zrób to:

Uruchom .Skip (1000). Weź (100) w kontekście danych LINQ to SQL i spójrz na dane wyjściowe SQL. Wygeneruje dla Ciebie instrukcję SQL, która zrobi to, co opisujesz.

Nie będzie tak elegancka, ale spełnia swoje zadanie.

Józefa
źródło
2
Nie to, o co pytano.
RayLoveless