Offset Baris di SQL Server

133

Apakah ada cara di SQL Server untuk mendapatkan hasil mulai dari offset yang diberikan? Sebagai contoh, dalam tipe lain dari database SQL, itu mungkin dilakukan:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

untuk mendapatkan hasil 51-75. Konstruk ini tampaknya tidak ada di SQL Server.

Bagaimana saya bisa menyelesaikan ini tanpa memuat semua baris yang tidak saya pedulikan? Terima kasih!

Alex
sumber
Anda dapat menggunakan offset dan mengambil pernyataan berikutnya. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Jawaban:

152

Saya akan menghindari penggunaan SELECT *. Tentukan kolom yang sebenarnya Anda inginkan walaupun mungkin semuanya.

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

Paging Efisien Melalui Set Hasil Besar di SQL Server 2000

Metode yang Lebih Efisien untuk Paging Melalui Set Hasil Besar

Brian Kim
sumber
6
Mengapa Anda menyarankan menghindari SELECT bahkan jika Anda memilih semua kolom?
Adam Ness
12
Saya yakin dia menggunakan "*" karena lebih mudah untuk mengetik dan mendapatkan titik lebih baik daripada "col1, col2, ... colN"
gillonba
9
Adapun mengapa tidak menggunakannya, SELECT *berarti bahwa jika struktur tabel berubah, kueri Anda masih berjalan, tetapi memberikan hasil yang berbeda. Jika kolom ditambahkan, ini mungkin berguna (meskipun Anda masih harus menggunakannya dengan nama di suatu tempat); jika sebuah kolom dihapus atau diganti namanya, lebih baik bagi SQL Anda untuk memecah tampak daripada kode lebih lanjut berperilaku aneh karena variabel tidak diinisialisasi.
IMSoP
5
pilih semua data tabel dan potong? jika memiliki baris 5000000000? pilih 5000000000 baris dan potong untuk setiap permintaan? itu tidak efisien untuk cpu dan memori server.
e-info128
3
Harap dicatat bahwa 2012+ telah mengimplementasikannya dengan lebih baik. Lihat jawaban oleh + Martin Smith
meridius
100

Jika Anda akan memproses semua halaman secara berurutan, maka cukup dengan mengingat nilai kunci terakhir yang terlihat pada halaman sebelumnya dan menggunakan TOP (25) ... WHERE Key > @last_key ORDER BY Keydapat menjadi metode dengan kinerja terbaik jika ada indeks yang sesuai untuk memungkinkan ini dicari secara efisien - atau kursor API jika tidak .

Untuk memilih halaman arbiter solusi terbaik untuk SQL Server 2005 - 2008 R2 mungkin ROW_NUMBERdanBETWEEN

Untuk SQL Server 2012+ Anda dapat menggunakan klausa ORDER BY yang ditingkatkan untuk kebutuhan ini.

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

Meskipun masih harus dilihat seberapa baik kinerja opsi ini .

Martin Smith
sumber
2
Sekarang tersedia di SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen
13
Sudah waktunya mereka menambahkan ini ke tSQL
JohnFx
3
Hanya untuk Sql Server 2012 :(
e-info128
22

Ini adalah salah satu cara (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

dan ini adalah cara lain (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
sumber
Hanya untuk mengklarifikasi yang pertama ... (@pageSize) adalah pengganti di sini untuk nilai aktual. Anda harus melakukan 'TOP 25' secara khusus; SQL Server 2000 tidak mendukung variabel dalam klausa TOP. Ini membuatnya sakit melibatkan SQL dinamis.
Cowan
5
Solusi itu untuk SQL2000 tidak bekerja untuk halaman terakhir di set hasil, kecuali jumlah total baris kebetulan merupakan kelipatan dari ukuran halaman.
Bill Karwin
10

Anda dapat menggunakan ROW_NUMBER()fungsi untuk mendapatkan apa yang Anda inginkan:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
Matthias Meid
sumber
7

Ada OFFSET .. FETCHdi SQL Server 2012, tetapi Anda harus menentukan ORDER BYkolom.

Jika Anda benar-benar tidak memiliki kolom eksplisit yang dapat Anda lewati sebagai ORDER BYkolom (seperti yang disarankan orang lain), maka Anda dapat menggunakan trik ini:

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

... atau

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

Kami menggunakannya di jOOQ ketika pengguna tidak secara spesifik menentukan pesanan. Ini kemudian akan menghasilkan pemesanan yang cukup acak tanpa biaya tambahan.

Lukas Eder
sumber
6

Untuk tabel dengan kolom data yang lebih banyak dan besar, saya lebih suka:

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.

Ini memiliki kinerja yang jauh lebih baik pada tabel dengan data besar seperti BLOB, karena fungsi ROW_NUMBER hanya perlu melihat satu kolom, dan hanya baris yang cocok dikembalikan dengan semua kolom.

Arthur van Dijk
sumber
5

Lihat pilih saya untuk paginator

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

Ini memecahkan pagination;)

PerfectLion
sumber
3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable
Jithin Shaji
sumber
Performa bijaksana tampaknya tidak optimal karena permintaan kemudian dieksekusi dua kali tidak perlu. Khususnya ketika pengguna pergi ke halaman yang lebih tinggi, permintaan untuk membuang baris yaitu bagian di bawah KECUALI akan lebih lama dan lebih lama.
vanval
2

Tergantung pada versi Anda, Anda tidak dapat melakukannya secara langsung, tetapi Anda dapat melakukan sesuatu seperti peretasan

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

di mana 'bidang' adalah kuncinya.

Tidak berbelit-belit
sumber
4
Solusi itu untuk SQL2000 tidak bekerja untuk halaman terakhir di set hasil, kecuali jumlah total baris kebetulan merupakan kelipatan dari ukuran halaman.
Bill Karwin
2

Berikut ini akan menampilkan 25 catatan tidak termasuk 50 catatan pertama bekerja di SQL Server 2012.

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

Anda dapat mengganti ID sebagai kebutuhan Anda

Shb
sumber
Tolong juga, tambahkan ini mungkin di SQL SERVER 2012
Usman Younas
2

Anda harus berhati-hati ketika menggunakan ROW_NUMBER() OVER (ORDER BY)pernyataan karena kinerjanya sangat buruk. Hal yang sama berlaku untuk menggunakan Common Table Expressions dengan ROW_NUMBER()itu bahkan lebih buruk. Saya menggunakan cuplikan berikut yang telah terbukti sedikit lebih cepat daripada menggunakan variabel tabel dengan identitas untuk memberikan nomor halaman.

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
sumber
Ini akan menghasilkan 11 baris, bukan 10.
Aaron Bertrand
1

Saya menggunakan teknik ini untuk pagination. Saya tidak mengambil semua baris. Misalnya, jika halaman saya perlu menampilkan 100 baris teratas, saya hanya mengambil 100 dengan klausa where. Output dari SQL harus memiliki kunci unik.

Tabelnya memiliki yang berikut:

ID, KeyId, Rank

Peringkat yang sama akan ditugaskan untuk lebih dari satu KeyId.

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

Untuk pertama kalinya saya lulus 0 untuk keduanya. Kali kedua lulus 1 & 14. Kali ketiga berlalu 2 dan 6 ....

Nilai catatan 10 Peringkat & Id diteruskan ke yang berikutnya

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

Ini akan memiliki paling sedikit tekanan pada sistem

Ravi Ramaswamy
sumber
1

Di SqlServer2005 Anda dapat melakukan hal berikut:

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
sumber
Bukankah seharusnya begitu @Offset + @Limit - 1? Jika @ Batas adalah 10, ini akan menghasilkan 11 baris.
Aaron Bertrand
1

Cara terbaik untuk melakukannya tanpa membuang waktu untuk memesan catatan adalah seperti ini:

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

dibutuhkan kurang dari satu detik!
solusi terbaik untuk tabel besar.

Pishgaman.org
sumber
0

Saya telah mencari jawaban ini untuk sementara waktu sekarang (untuk pertanyaan umum) dan menemukan cara lain untuk melakukannya di SQL Server 2000+ menggunakan ROWCOUNT dan kursor dan tanpa TOP atau tabel sementara.

Dengan menggunakan SET ROWCOUNT [OFFSET+LIMIT]Anda dapat membatasi hasil, dan dengan kursor, langsung ke baris yang Anda inginkan, lalu loop 'sampai akhir.

Jadi kueri Anda akan seperti ini:

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é
sumber
Saya benci melihat kinerja ini ketika Anda sampai di ujung meja ...
Aaron Bertrand
0

Dengan SQL Server 2012 (11.x) dan yang lebih baru dan Azure SQL Database, Anda juga dapat memiliki "fetch_row_count_expression", Anda juga dapat memiliki klausa ORDER BY bersama dengan ini.

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

Catatan OFFSET Menentukan jumlah baris yang dilewati sebelum mulai mengembalikan baris dari ekspresi kueri. Ini BUKAN nomor baris awal. Jadi, harus 0 untuk memasukkan catatan pertama.

Tejasvi Hegde
sumber