GIỚI HẠN 10..20 trong SQL Server


161

Tôi đang cố gắng làm một cái gì đó như:

SELECT * FROM table LIMIT 10,20

hoặc là

SELECT * FROM table LIMIT 10 OFFSET 10

nhưng sử dụng SQL Server

Giải pháp duy nhất tôi thấy giống như quá mức cần thiết:

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

Tôi cũng tìm thấy :

SELECT TOP 10 * FROM stuff; 

... nhưng đó không phải là điều tôi muốn làm vì tôi không thể chỉ định giới hạn bắt đầu.

Có cách nào khác để tôi làm điều đó?

Ngoài ra, chỉ tò mò, có lý do tại sao SQL Server không hỗ trợ LIMITchức năng hoặc một cái gì đó tương tự không? Tôi không muốn có ý xấu, nhưng điều đó thực sự nghe giống như một thứ mà DBMS cần ... Nếu có, thì tôi xin lỗi vì đã quá thờ ơ! Tôi đã làm việc với MySQL và SQL + trong 5 năm qua nên ...


1
Sử dụng CTE cho ROW_NUMBER()và giới hạn TOPđộ rộng của phạm vi và WHEREđiều kiện cho giới hạn của phạm vi là tốt nhất tôi có thể đạt được. Tôi cũng nhận thấy hiệu suất tốt hơn nhiều nếu TOPmệnh đề sử dụng một chữ thay vì biến
Jodrell

Vấn đề với bất kỳ giải pháp nào liên quan đến ROW_NUMBER () là nếu bạn không biết trước những cột nào bạn có và bạn đã tham gia và các bảng đã tham gia có cùng tên cột, bạn sẽ nhận được "Cột 'xxx' đã được chỉ định nhiều lần ". Điều này không phổ biến như ban đầu có thể nghe. Tôi sử dụng Dapper và tất cả các bảng của tôi đều có cột Id. Dapper phân tách và ánh xạ trên đó, vì vậy tôi không muốn đổi tên chúng, nhưng tôi không thể sử dụng bí danh CHỌN * TỪ ([truy vấn ban đầu]). Tôi vẫn chưa tìm ra giải pháp!
Steve Owen

Câu trả lời:


104

Các LIMITkhoản không phải là một phần của SQL tiêu chuẩn. Nó được hỗ trợ như một phần mở rộng của nhà cung cấp cho SQL bởi MySQL, PostgreSQL và SQLite.

Các thương hiệu cơ sở dữ liệu khác có thể có các tính năng tương tự (ví dụ: TOPtrong Microsoft SQL Server), nhưng các nhãn này không luôn hoạt động giống hệt nhau.

Thật khó để sử dụng TOPtrong Microsoft SQL Server để bắt chước LIMITmệnh đề. Có những trường hợp nó không hoạt động.

Giải pháp bạn đã chỉ ra, sử dụng ROW_NUMBER()có sẵn trong Microsoft SQL Server 2005 trở lên. Đây là giải pháp tốt nhất (hiện tại) chỉ hoạt động như một phần của truy vấn.

Một giải pháp khác là sử dụng TOPđể tìm nạp số đếm đầu tiên + hàng , sau đó sử dụng API để tìm kiếm qua các hàng đầu tiên .

Xem thêm:


135

Đối với SQL Server 2012 + bạn có thể sử dụng .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 

10
Máy chủ SQl 2012 yêu cầu chỉ định ĐẶT HÀNG
B BYNG

4
@ qub1n - MySQL không đảm bảo những hàng bạn nhận được trong trường hợp đó mặc dù.
Martin Smith

3
Bạn có phải sử dụng offsethoặc bạn có thể bỏ dòng đó ra không (giả sử bạn không muốn bù)?
Cullub


Ví dụ truy vấn của bạn chạy tốt nhưng nếu tôi thay đổi tên bảng và thứ tự theo col như bên dưới CHỌN * TỪ Dim Dimt ĐẶT HÀNG B ProductNG ProductKey OFFSET 5 ROWS FETCH TIẾP THEO 5 ROWS CHỈ Nó báo lỗiParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat

36

như bạn đã tìm thấy, đây là phương pháp máy chủ sql ưa thích:

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

Tại sao asau khi chọn bên trong? Tôi giả sử bạn đang đưa ra lựa chọn bên trong một bí danh, nhưng sau đó bạn dường như không bao giờ sử dụng nó ... Bạn có nên làm a.rowthay vì chỉ row?
Lucas

3
@Lucas, bạn được yêu cầu đặt bí danh sau ( )bảng dẫn xuất, nhưng nó sẽ để nó đi nếu sau đó bạn quên sử dụng nó để chỉ các cột. Tôi đã sửa nó mặc dù ...
KM.

cảm ơn, tôi thấy rằng ra một cách khó khăn (đã cố gắng để bí danh ra).
Lucas

1
Bình chọn +1: Tuy nhiên, câu trả lời của @MartinSmith được bình chọn nhiều hơn, sau khi so sánh kế hoạch thực hiện với phương pháp này, tôi phát hiện ra rằng, giải pháp này hoạt động nhanh hơn.
Harsh

10

Nếu bạn đang sử dụng SQL Server 2012+ bỏ phiếu cho câu trả lời của Martin Smith và sử dụng OFFSETvà các FETCH NEXTtiện ích mở rộng để ORDER BY,

Nếu bạn không may bị mắc kẹt với phiên bản cũ hơn, bạn có thể làm điều gì đó như thế này,

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

Tôi tin là chức năng tương đương với

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

và cách thực hiện tốt nhất mà tôi biết khi thực hiện trong TSQL, trước MS SQL 2012.


Nếu có rất nhiều hàng, bạn có thể có hiệu suất tốt hơn bằng cách sử dụng bảng tạm thời thay vì CTE.


Được khuyến khích để chỉ ra câu trả lời của Martin Smith (và liên kết với nó) trong khi cung cấp giải pháp trước năm 2012. Cũng cho lời khuyên bảng tạm thời vì bạn đúng :)
fujiiface

7

Thật không may, điều ROW_NUMBER()tốt nhất bạn có thể làm. Điều đó thực sự đúng hơn, bởi vì kết quả của một limithoặc topmệnh đề không thực sự có ý nghĩa mà không liên quan đến một số thứ tự cụ thể. Nhưng đó vẫn là một nỗi đau để làm.

Cập nhật: Sql Server 2012 bổ sung limittính năng tương tự thông qua các từ khóa OFFSET và FETCH . Đây là cách tiếp cận tiêu chuẩn ansi, trái ngược với LIMIT, đây là một phần mở rộng MySql không chuẩn.


@Joel: Bạn có thể giải thích tại sao ROW_NUMBER () không thể đánh số các hàng theo cách chúng đi ra khỏi ĐẶT HÀNG B BYNG KHÔNG? Tôi đã luôn tự hỏi tại sao "QUÁ (tên theo thứ tự)" là bắt buộc, nhưng tôi đoán có một lý do chính đáng cho nó. Hoặc ít nhất là một lý do.
Tomalak

3
bởi vì không có thứ gọi là thứ tự mà không có thứ tự theo mệnh đề. Bạn nhận được bất cứ thứ tự nào mà các bản ghi có sẵn cho máy chủ và điều đó có thể thay đổi từ yêu cầu truy vấn sang yêu cầu truy vấn.
Joel Coehoorn

1
@marcgg: Tôi chưa bao giờ đọc bất kỳ dấu hiệu nào cho thấy Microsoft có kế hoạch triển khai GIỚI HẠN. Ngay cả khi họ có kế hoạch như vậy, các nhà cung cấp nguồn đóng có xu hướng không công bố trước các tính năng. Nó chắc chắn sẽ là một tính năng hữu ích, nhưng chúng tôi không biết sẽ thực hiện bao nhiêu công việc, với mã của họ.
Bill Karwin

3
Nếu bạn không muốn lặp lại chính mình trong mệnh đề ORDER BY, hãy sử dụng bí danh ROW_NUMBER () thay vì tập hợp các cột ban đầu.
Peter Radocchia

2
@Tomalak: Theo như SQL Server, thứ tự được sử dụng để tính ROW_NUMBER () hoàn toàn không liên quan đến thứ tự của tập kết quả. Đó là lý do tại sao bạn phải chỉ định chúng một cách riêng biệt.
LukeH

6

Còn cái này thì sao?

SET ROWCOUNT 10 

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

Nó cung cấp cho bạn 10 hàng cuối cùng trong 20 hàng đầu tiên. Một nhược điểm là thứ tự bị đảo ngược, nhưng, ít nhất nó cũng dễ nhớ.


6
Nếu chỉ có 14 hàng trong bảng thì sao? Bạn nhận được các hàng 14 xuống 5, không giống như các hàng được trả về GIỚI HẠN 10 OFFSET 10 (nên là các hàng 14 xuống 11).
Bill Karwin

2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Nên cho hồ sơ 11-20. Có lẽ không quá hiệu quả nếu tăng để có thêm các trang và không chắc nó có thể bị ảnh hưởng như thế nào khi đặt hàng. Có thể phải xác định điều này trong cả hai câu lệnh WHERE.


1

Một cách tốt là tạo một thủ tục:

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

giống như giới hạn 0,2 /////////////// thực hiện phân trang 0,4


1

Chỉ dành cho giải pháp thu âm hoạt động trên hầu hết các công cụ cơ sở dữ liệu mặc dù có thể không hiệu quả nhất:

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

Ghi chú Pelase: trang cuối cùng vẫn sẽ chứa các hàng ReturnCount cho dù SkipCount là gì. Nhưng đó có thể là một điều tốt trong nhiều trường hợp.


1

Tương đương với LIMIT là SET ROWCOUNT, nhưng nếu bạn muốn phân trang chung thì tốt hơn là viết một truy vấn như thế này:

;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

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

Sẽ in hàng từ 10 đến 15.


0

Cho đến nay định dạng này là những gì đang làm việc cho tôi (mặc dù không phải là hiệu suất tốt nhất):

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}

Một lưu ý ở bên cạnh, phân trang trên dữ liệu động có thể dẫn đến kết quả lạ / bất ngờ.


0

Từ tài liệu trực tuyến của MS SQL Server ( http://technet.microsoft.com/en-us/l Library / ms186734.aspx ), đây là ví dụ của họ mà tôi đã thử nghiệm và hoạt động, để truy xuất một nhóm hàng cụ thể. ROW_NUMBER yêu cầu QUÁ, nhưng bạn có thể đặt hàng theo bất cứ thứ gì bạn thích:

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;

0

Sử dụng tất cả máy chủ SQL :; với tbl là (CHỌN ROW_NUMBER () qua (thứ tự theo (chọn 1)) làm Row Index, * từ bảng) chọn top 10 * từ tbl trong đó Row Index> = 10


-3
 SELECT * FROM users WHERE Id Between 15 and 25

nó sẽ in từ 15 đến 25 như giới hạn trong MYSQl


2
Điều gì xảy ra nếu người dùng xóa một bản ghi trong khoảng từ 15 đến 25?
Gökçer Gökdal
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.