LIMIT 10..20 di SQL Server

161

Saya mencoba melakukan sesuatu seperti:

SELECT * FROM table LIMIT 10,20

atau

SELECT * FROM table LIMIT 10 OFFSET 10

tetapi menggunakan SQL Server

Satu-satunya solusi yang saya temukan sepertinya berlebihan:

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

Saya juga menemukan :

SELECT TOP 10 * FROM stuff; 

... tapi bukan itu yang ingin saya lakukan karena saya tidak bisa menentukan batas awal.

Apakah ada cara lain bagi saya untuk melakukan itu?

Juga, hanya ingin tahu, apakah ada alasan mengapa SQL Server tidak mendukung LIMITfungsi atau yang serupa? Saya tidak ingin menjadi kejam, tapi itu benar-benar terdengar seperti sesuatu yang dibutuhkan DBMS ... Jika ya, maka saya minta maaf karena begitu bodoh! Saya telah bekerja dengan MySQL dan SQL + selama 5 tahun terakhir jadi ...

marcgg
sumber
1
Menggunakan CTE untuk ROW_NUMBER()dan membatasi dengan TOPuntuk lebar rentang dan WHEREkondisi untuk batas jangkauan adalah yang terbaik yang bisa saya capai. Saya juga memperhatikan kinerja yang jauh lebih baik jika TOPklausa menggunakan variabel literal dan bukan variabel
Jodrell
Masalah dengan solusi apa pun yang melibatkan ROW_NUMBER () adalah bahwa jika Anda tidak tahu sebelumnya kolom apa yang akan Anda miliki, dan Anda telah bergabung, dan tabel yang digabung memiliki nama kolom yang sama, Anda akan mendapatkan "Kolom 'xxx' telah ditentukan beberapa kali ". Ini tidak biasa seperti awalnya mungkin terdengar. Saya menggunakan Dapper, dan semua tabel saya memiliki kolom ID. Dapper membagi dan memetakannya, jadi saya tidak ingin mengubah nama mereka, tetapi saya tidak bisa menggunakan SELECT * FROM ([query asli])). Saya belum menemukan solusi!
Steve Owen

Jawaban:

104

The LIMITklausul bukan bagian dari SQL standar. Ini didukung sebagai ekstensi vendor ke SQL oleh MySQL, PostgreSQL, dan SQLite.

Basis data merek lain mungkin memiliki fitur serupa (misalnya TOPdalam Microsoft SQL Server), tetapi ini tidak selalu berfungsi secara identik.

Sulit digunakan TOPdi Microsoft SQL Server untuk meniru LIMITklausa. Ada kasus di mana itu tidak berfungsi.

Solusi yang Anda tunjukkan, menggunakan ROW_NUMBER()tersedia di Microsoft SQL Server 2005 dan yang lebih baru. Ini adalah solusi terbaik (untuk saat ini) yang hanya berfungsi sebagai bagian dari kueri.

Solusi lain adalah menggunakan TOPuntuk mengambil hitungan + baris offset pertama , dan kemudian menggunakan API untuk mencari melewati baris offset pertama .

Lihat juga:

Bill Karwin
sumber
135

Untuk SQL Server 2012 + dapat Anda gunakan .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 
Martin Smith
sumber
10
SQl Server 2012 perlu menentukan ORDER OLEH saat Anda menggunakan OFFSET 5 ROWS FETCH BERIKUTNYA 5 ROWS HANYA sementara MySql dan SQLite tidak memerlukan ORDER OLEH saat Anda menggunakan LIMIT 5,5
Tomas Kubes
4
@ qub1n - MySQL tidak menjamin baris apa yang Anda dapatkan kembali dalam kasus itu.
Martin Smith
3
Apakah Anda harus menggunakan offset, atau bisakah Anda meninggalkan garis itu (dengan asumsi Anda tidak ingin offset)?
Cullub
Anda contoh permintaan berjalan dengan baik tetapi Jika saya mengubah nama tabel dan urutan dengan col seperti di bawah ini SELECT * DARI ORDER DimProduk OLEH ProductKey OFFSET 5 ROWS FETCH BERIKUTNYA 5 ROWS ONLY HANYA memberikan kesalahanParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat
36

seperti yang Anda temukan, ini adalah metode server sql yang disukai:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10
KM.
sumber
Kenapa asetelah inner pilih? Saya berasumsi Anda memberikan nama alias pilih bagian dalam, tetapi kemudian Anda sepertinya tidak pernah menggunakannya ... Haruskah Anda melakukannya a.rowsebagai ganti hanya row?
Lucas
3
@Lucas, Anda diharuskan untuk menempatkan alias setelah ( )tabel turunan, tetapi ia akan membiarkannya pergi jika Anda kemudian lupa menggunakannya untuk merujuk ke kolom. Saya memperbaikinya ...
KM.
terima kasih, saya menemukan itu dengan cara yang sulit (mencoba untuk meninggalkan alias keluar).
Lucas
1
Memilih +1: Namun, jawaban @MartinSmith dipilih lebih banyak, setelah membandingkan rencana eksekusi dengan pendekatan ini, saya mengetahui bahwa, solusi ini bekerja lebih cepat.
Harsh
10

Jika Anda menggunakan SQL Server 2012+ suara untuk jawaban Martin Smith dan menggunakan OFFSETdan FETCH NEXTekstensi untuk ORDER BY,

Jika Anda kurang beruntung terjebak dengan versi sebelumnya, Anda bisa melakukan sesuatu seperti ini,

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

Saya percaya secara fungsional setara dengan

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

dan cara berkinerja terbaik yang saya tahu melakukannya di TSQL, sebelum MS SQL 2012.


Jika ada sangat banyak baris, Anda mungkin mendapatkan kinerja yang lebih baik menggunakan tabel temp daripada CTE.

Jodrell
sumber
Terpilih untuk menunjukkan jawaban Martin Smith (dan menautkan ke sana) sambil memberikan solusi pra-2012. Juga untuk saran tabel temp karena Anda benar :)
fujiiface
7

Sayangnya, ROW_NUMBER()ini adalah yang terbaik yang dapat Anda lakukan. Ini sebenarnya lebih benar, karena hasil dari a limitatau topklausa tidak benar-benar memiliki makna tanpa memperhatikan beberapa urutan tertentu. Tapi itu masih menyakitkan untuk dilakukan.

Pembaruan: Sql Server 2012 menambahkan limitfitur- like via kata kunci OFFSET dan FETCH . Ini adalah pendekatan ansi-standar, yang bertentangan dengan LIMIT, yang merupakan ekstensi MySql non-standar.

Joel Coehoorn
sumber
@ Joel: Bisakah Anda menjelaskan mengapa ROW_NUMBER () tidak dapat memberi nomor pada baris bagaimana mereka keluar dari ORDER BY? Saya selalu bertanya-tanya mengapa "LEBIH (ORDER DENGAN nama)" adalah wajib, tapi saya kira ada alasan bagus untuk itu. Atau setidaknya sebuah alasan.
Tomalak
3
karena tidak ada yang namanya pesanan tanpa pesanan dengan klausa. Anda mendapatkan pesanan apa pun yang tersedia untuk server, dan itu bisa berubah dari permintaan kueri ke permintaan kueri.
Joel Coehoorn
1
@marcgg: Saya belum pernah membaca indikasi bahwa Microsoft berencana untuk mengimplementasikan LIMIT. Bahkan jika mereka memiliki rencana seperti itu, vendor sumber tertutup cenderung tidak mengumumkan fitur sebelumnya. Ini tentu saja akan menjadi fitur yang bermanfaat, tetapi kami tidak tahu berapa banyak pekerjaan yang akan diterapkan, mengingat kode mereka.
Bill Karwin
3
Jika Anda tidak ingin mengulangi sendiri dalam klausa ORDER BY, gunakan alias ROW_NUMBER () daripada kumpulan kolom yang asli.
Peter Radocchia
2
@ Tomalak: Sejauh menyangkut SQL Server, pemesanan yang digunakan untuk menghitung ROW_NUMBER () sama sekali tidak terkait dengan pemesanan resultset. Itu sebabnya Anda harus menentukannya secara terpisah.
LukeH
6

Bagaimana dengan ini?

SET ROWCOUNT 10 

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

Ini memberi Anda 10 baris terakhir dari 20 baris pertama. Salah satu kelemahannya adalah urutannya terbalik, tetapi setidaknya mudah diingat.

David Patrick
sumber
6
Bagaimana jika hanya ada 14 baris dalam tabel? Anda mendapatkan baris 14 hingga 5, yang tidak sama dengan baris yang dikembalikan oleh LIMIT 10 OFFSET 10 (harus baris 14 hingga 11).
Bill Karwin
2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Harus memberi catatan 11-20. Mungkin tidak terlalu efisien jika menambah untuk mendapatkan halaman lebih lanjut, dan tidak yakin bagaimana itu akan terpengaruh oleh pemesanan. Mungkin harus menentukan ini di kedua pernyataan WHERE.

Andy
sumber
1

Cara yang baik adalah membuat prosedur:

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

seperti batas 0,2 /////////////// jalankan pagination 0,4

Wahaj Latif
sumber
1

Hanya untuk solusi rekam yang bekerja di sebagian besar mesin basis data meskipun mungkin bukan yang paling efisien:

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

Catatan Pelase: halaman terakhir masih berisi baris ReturnCount tidak peduli apa SkipCount itu. Tetapi itu mungkin merupakan hal yang baik dalam banyak kasus.

YB
sumber
1

Setara dengan LIMIT adalah SET ROWCOUNT, tetapi jika Anda ingin pagination umum lebih baik menulis kueri seperti ini:

;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
sumber
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

Akan mencetak baris dari 10 hingga 15.

sjith
sumber
0

Sejauh ini format ini berfungsi untuk saya (bukan performa terbaik):

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}

Catatan di sampingnya, penomoran data dinamis dapat menyebabkan hasil yang aneh / tidak terduga.

Charlie Affumigato
sumber
0

Dari dokumentasi online MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ), berikut ini adalah contoh mereka yang telah saya uji dan bekerja, untuk mengambil serangkaian baris tertentu. ROW_NUMBER membutuhkan OVER, tetapi Anda dapat memesan dengan apa pun yang Anda suka:

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
sumber
0

Gunakan semua server SQL:; dengan tbl sebagai (SELECT ROW_NUMBER () di atas (pesan oleh (pilih 1)) sebagai RowIndex, * from table) pilih top 10 * dari tbl di mana RowIndex> = 10

Phạm Tấn Lợi
sumber
-3
 SELECT * FROM users WHERE Id Between 15 and 25

itu akan mencetak 15-25 sebagai batas seperti di MYSQl

SR1
sumber
2
Bagaimana jika pengguna menghapus catatan antara 15 dan 25?
Gökçer Gökdal