Przesunięcie wierszy w SQL Server

133

Czy istnieje sposób w SQL Server, aby uzyskać wyniki zaczynając od podanego przesunięcia? Na przykład w innym typie bazy danych SQL można:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

aby uzyskać wyniki 51-75. Wydaje się, że ta konstrukcja nie istnieje w programie SQL Server.

Jak mogę to zrobić bez ładowania wszystkich wierszy, na których mi nie zależy? Dzięki!

Alex
źródło
Możesz użyć przesunięcia i pobrać następną instrukcję. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Odpowiedzi:

154

Unikałbym używania SELECT *. Określ kolumny, które faktycznie chcesz, nawet jeśli mogą to być wszystkie.

SQL Server 2005+

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Efektywne stronicowanie dużych zestawów wyników w SQL Server 2000

Bardziej wydajna metoda przeglądania dużych zestawów wyników

Brian Kim
źródło
6
Dlaczego sugerujesz unikanie SELECT, nawet jeśli wybierasz wszystkie kolumny?
Adam Ness
12
Jestem pewien, że użył „*”, ponieważ wpisanie go było prostsze i trafił lepiej niż „col1, col2, ... colN”
gillonba
9
Jeśli chodzi o to, dlaczego go nie używać, SELECT *oznacza to, że jeśli struktura tabeli ulegnie zmianie, zapytanie nadal działa, ale daje inne wyniki. Jeśli dodana jest kolumna, może się to przydać (chociaż nadal musisz gdzieś używać jej nazwy); jeśli kolumna zostanie usunięta lub zmieniona zostanie jej nazwa, lepiej, aby kod SQL ulegał awarii w widoczny sposób, niż kod zachowywał się dziwnie, ponieważ zmienna nie została zainicjowana.
IMSoP
5
wybrać wszystkie dane stołu i wyciąć? jeśli masz 5000000000 wierszy? wybrać 5000000000 wierszy i wyciąć dla każdego zapytania? nie jest wydajne dla procesora i pamięci serwera.
e-info128
3
Należy pamiętać, że 2012+ został zaimplementowany znacznie lepiej. Zobacz odpowiedź + Martin Smith
meridius
100

Jeśli będziesz przetwarzać wszystkie strony w kolejności, po prostu zapamiętaj ostatnią wartość klucza widoczną na poprzedniej stronie i użycie TOP (25) ... WHERE Key > @last_key ORDER BY Keymoże być najlepszą metodą, jeśli istnieją odpowiednie indeksy, aby umożliwić wydajne wyszukiwanie - lub kursor API, jeśli nie są .

Aby wybrać dowolną stronę, najlepszym rozwiązaniem dla SQL Server 2005-2008 R2 jest prawdopodobnie ROW_NUMBERiBETWEEN

W przypadku SQL Server 2012+ możesz w tym celu użyć rozszerzonej klauzuli ORDER BY .

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Chociaż dopiero się okaże, jak dobrze będzie działać ta opcja .

Martin Smith
źródło
2
Jest teraz dostępny w SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen
13
Najwyższy czas, aby dodali to do tSQL
JohnFx
3
Tylko dla Sql Server 2012 :(
e-info128
22

To jest w jedną stronę (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

a to jest inny sposób (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize
leoinfo
źródło
Dla wyjaśnienia pierwszego z nich ... (@pageSize) jest tutaj symbolem zastępczym dla rzeczywistej wartości. Będziesz musiał zrobić konkretnie „TOP 25”; SQL Server 2000 nie obsługuje zmiennych w klauzuli TOP. To sprawia, że ​​jest to ból związany z dynamicznym SQL.
Cowan
5
To rozwiązanie dla SQL2000 nie działa dla ostatniej strony w zestawie wynikowym, chyba że całkowita liczba wierszy jest wielokrotnością rozmiaru strony.
Bill Karwin
10

Możesz użyć ROW_NUMBER()funkcji, aby uzyskać to, czego chcesz:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
Matthias Meid
źródło
7

Jest OFFSET .. FETCHw SQL Server 2012, ale musisz określić ORDER BYkolumnę.

Jeśli naprawdę nie masz żadnej jawnej kolumny, którą mógłbyś przekazać jako ORDER BYkolumnę (jak sugerowali inni), możesz użyć tej sztuczki:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... lub

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Używamy go w jOOQ, gdy użytkownicy nie określają wyraźnie zamówienia. Spowoduje to wygenerowanie całkiem przypadkowej kolejności bez dodatkowych kosztów.

Lukas Eder
źródło
6

W przypadku tabel z większą liczbą i dużymi kolumnami danych preferuję:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Ma znacznie lepszą wydajność w tabelach z dużymi danymi, takimi jak BLOB, ponieważ funkcja ROW_NUMBER musi przeglądać tylko jedną kolumnę, a ze wszystkimi kolumnami zwracane są tylko pasujące wiersze.

Arthur van Dijk
źródło
5

Zobacz mój wybór dla paginatora

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

To rozwiązuje paginację;)

PerfectLion
źródło
3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable
Jithin Shaji
źródło
Wydajność nie wydaje się optymalna, ponieważ zapytanie jest wtedy niepotrzebnie wykonywane dwukrotnie. Zwłaszcza, gdy użytkownik przechodzi do wyższych stron, zapytanie o odrzucenie wierszy, tj. Część poniżej EXCEPT, będzie trwało coraz dłużej.
vanval,
2

W zależności od wersji nie możesz tego zrobić bezpośrednio, ale możesz zrobić coś hackerskiego

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

gdzie „pole” jest kluczem.

Niepodcięte
źródło
4
To rozwiązanie dla SQL2000 nie działa dla ostatniej strony w zestawie wynikowym, chyba że całkowita liczba wierszy jest wielokrotnością rozmiaru strony.
Bill Karwin
2

Poniżej zostanie wyświetlonych 25 rekordów, z wyłączeniem pierwszych 50 rekordów w programie SQL Server 2012.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

możesz zastąpić ID jako swoje wymaganie

Shb
źródło
Pls również, dodaj to jest możliwe w SQL SERVER 2012
Usman Younas
2

Należy zachować ostrożność podczas używania tego ROW_NUMBER() OVER (ORDER BY)stwierdzenia, ponieważ wydajność jest dość niska. To samo dotyczy używania wyrażeń wspólnych tabel z ROW_NUMBER()tym jest jeszcze gorzej. Używam następującego fragmentu kodu, który okazał się nieco szybszy niż użycie zmiennej tabeli z tożsamością w celu podania numeru strony.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet
Patrik Melander
źródło
To zwróci 11 wierszy, a nie 10.
Aaron Bertrand
1

Używam tej techniki do paginacji. Nie pobieram wszystkich wierszy. Na przykład, jeśli moja strona musi wyświetlić 100 pierwszych wierszy, pobieram tylko 100 z klauzulą ​​where. Wynik SQL powinien mieć unikalny klucz.

Tabela zawiera następujące elementy:

ID, KeyId, Rank

Ta sama ranga zostanie przypisana do więcej niż jednego KeyId.

SQL jest select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Po raz pierwszy zdaję 0 dla obu. Drugi przejazd 1 i 14. Trzeci przejazd 2 i 6 ....

Wartość dziesiątego rekordu Rank & Id jest przekazywana do następnego

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Będzie to miało najmniejszy nacisk na system

Ravi Ramaswamy
źródło
1

W SqlServer2005 możesz wykonać następujące czynności:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 
Aheho
źródło
Nie powinno być @Offset + @Limit - 1? Jeśli @Limit wynosi 10, zwróci to 11 wierszy.
Aaron Bertrand
1

Najlepszym sposobem, aby to zrobić bez marnowania czasu na zamawianie rekordów, jest:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

zajmuje to mniej niż jedną sekundę!
najlepsze rozwiązanie dla dużych stołów.

Pishgaman.org
źródło
0

Szukałem tej odpowiedzi już od jakiegoś czasu (dla zapytań ogólnych) i znalazłem inny sposób zrobienia tego na SQL Server 2000+ przy użyciu ROWCOUNT i kursorów, bez TOP lub jakiejkolwiek tabeli tymczasowej.

Za pomocą SET ROWCOUNT [OFFSET+LIMIT]możesz ograniczyć wyniki, a kursorami przejdź bezpośrednio do żądanego wiersza, a następnie zapętlaj do końca.

Twoje zapytanie wyglądałoby więc tak:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0
Capilé
źródło
Nie chciałbym zobaczyć tego, jak to się robi, gdy dojdziesz do końca stołu ...
Aaron Bertrand
0

W przypadku programu SQL Server 2012 (11.x) i nowszych oraz Azure SQL Database możesz również mieć „fetch_row_count_expression”, a także klauzulę ORDER BY.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15

Uwaga OFFSET Określa liczbę wierszy do pominięcia, zanim zacznie zwracać wiersze z wyrażenia zapytania. NIE jest to numer wiersza początkowego. Tak więc, aby uwzględnić pierwszy rekord, musi wynosić 0.

Tejasvi Hegde
źródło