Tương đương LIMIT và OFFSET cho SQL Server?


172

Trong PostgreSQL có sự LimitOffsettừ khóa mà sẽ cho phép pagination rất dễ dàng của tập kết quả.

Cú pháp tương đương cho SQL Server là gì?


Đối với máy chủ sql 2012, tính năng này được triển khai một cách dễ dàng. Xem câu trả lời của tôi
Somnath Muluk

Cảm ơn bạn đã đặt câu hỏi này, chúng tôi buộc phải chuyển từ MySQL sang
MsQuery

Bạn có thể sử dụng offset và tìm nạp câu lệnh tiếp theo trong máy chủ SQL theo thứ tự theo mệnh đề. Hãy dùng thử youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Câu trả lời:


139

Tương đương LIMITSET ROWCOUNT, nhưng nếu bạn muốn pagination chung nó tốt hơn để 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

Ưu điểm ở đây là tham số hóa của phần bù và giới hạn trong trường hợp bạn quyết định thay đổi tùy chọn phân trang của mình (hoặc cho phép người dùng làm như vậy).

Lưu ý: các @Offsettham số nên sử dụng một căn cứ lập chỉ mục cho thay này hơn việc lập chỉ mục zero-based bình thường.


22
Cũ rồi. Sql Server 2012 và sau đó hỗ trợ OFFSET / FETCH
Joel Coehoorn

31
@JoelCoehoorn Không cũ. Tôi vừa được chỉ định cho dự án sử dụng SLQ Server 2008, trước đây chỉ sử dụng mysql ...
Cthulhu

Điều này là khá tốt nhưng cần phải được điều chỉnh một chútWHERE RowNum >= (@Offset + 1)
Eric Herlitz

5
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2.
Paul

2
@Aaronaught Nếu tôi Tablecó 200k hồ sơ, nó sẽ tìm nạp tất cả trước, sau đó áp dụng giới hạn? Là truy vấn này có hiệu quả?
Jigar

231

Tính năng này hiện được thực hiện dễ dàng trong SQL Server 2012. Điều này hoạt động từ SQL Server 2012 trở đi.

Giới hạn với offset để chọn 11 đến 20 hàng trong SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: số lượng hàng bị bỏ qua
  • NEXT: số lượng hàng tiếp theo yêu cầu

Tham khảo: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017


4
Có một đẳng thức SQL_CALC_FOUND_ROWSkhi sử dụng này?
Petah

1
@Petah @@ Rowcount sẽ mang đến cho bạn điều mà tôi nghĩ
Rob Sedgwick

GOTCHA: Bạn không thể sử dụng điều này từ trong CTE. Nó phải được sử dụng trong truy vấn chính. Tôi muốn giới hạn số lượng hàng được trả lại (phân trang) và sau đó thực hiện một phép tính đắt tiền cho 10 hàng trở lại, thay vì xác định các hàng, thực hiện tính toán đắt tiền, sau đó bỏ qua / lấy những gì tôi cần. Câu trả lời của @ Aaronaught sẽ hoạt động cho những người cần hạn chế các hàng trong CTE.
Derreck Dean

@Somnath Muluk Phần bù và tìm nạp này mất rất nhiều thời gian cho khối lượng dữ liệu ví dụ cao hơn với độ lệch là 1000000. Làm thế nào tôi có thể xử lý vấn đề này.
Saroj Shrestha

1
@SarojShrestha: Đây không phải là vấn đề Offset và Fetch. Bạn nên xem lại kiến ​​trúc của bảng của bạn bây giờ. Xem xét Phân vùng bảng, hàng dữ liệu của bạn và các loại cột khác nhau và tổng kích thước bảng, xem xét việc lưu trữ một số hàng nếu không được yêu cầu thường xuyên, kiểm tra thông số kỹ thuật máy chủ của bạn.
Somnath Muluk

23
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

Lưu ý: Giải pháp này sẽ chỉ hoạt động trong SQL Server 2005 trở lên, vì điều này là khi ROW_NUMBER()được triển khai.


Tôi đã sử dụng truy vấn này được một lúc rồi và nó hoạt động rất tốt vì vậy cảm ơn vì điều đó. Tôi chỉ tự hỏi những gì 'xx' đại diện?
Urbley

truy vấn phụ yêu cầu một tên. vì tôi không sử dụng nó, chỉ cần đặt xx ở đó
jorgeu

2
Xx chỉ là một bí danh bảng. Nó có thể rõ ràng hơn một chút nếu bạn nóiAS xx
Concrete Gannet

Bất cứ ai biết làm thế nào để làm trái tham gia vào truy vấn này?
Drenyl

12

Bạn có thể sử dụng ROW_NUMBER trong Biểu thức bảng chung để đạt được điều này.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row

4

Đối với tôi, việc sử dụng OFFSET và FETCH cùng nhau rất chậm, vì vậy tôi đã sử dụng kết hợp TOP và OFFSET như thế này (nhanh hơn):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Lưu ý: Nếu bạn sử dụng TOP và OFFSET cùng một truy vấn như:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Sau đó, bạn gặp lỗi, vì vậy để sử dụng TOP và OFFSET cùng nhau, bạn cần tách nó bằng một truy vấn phụ.

Và nếu bạn cần sử dụng CHỌN DISTINCT thì truy vấn sẽ như sau:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Lưu ý: Việc sử dụng CHỌN ROW_NUMBER với DISTINCT không hiệu quả với tôi.


1
Tôi nhận được "Không thể sử dụng TOP trong cùng một truy vấn hoặc truy vấn phụ dưới dạng OFFSET."
MichaelRushton

Bạn đúng @MichaelRushton, không thể được sử dụng trong cùng một truy vấn hoặc trong cùng một truy vấn phụ, sau đó bạn phải sử dụng một truy vấn phụ để phân tách nó. Vì vậy, nếu bạn có SQL thích SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, bạn phải chuyển đổi nó như thế nào SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Tôi sẽ chỉnh sửa câu trả lời của tôi. Cảm ơn và xin lỗi tiếng Anh của tôi.
sebasdev

2

Một mẫu khác:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;

15
Tôi loại bỏ bài phát biểu ghét microsoft của bạn. Đừng thảo luận về các cuộc chiến thánh ở đây; chỉ trả lời và đặt câu hỏi theo cách không chủ quan.
Earlz

2

ở đây một ai đó kể về tính năng này trong sql 2011, nó buồn họ chọn một chút từ khóa khác nhau "OFFSET / FETCH" nhưng không phải của nó standart sau đó ok.


2

Thêm một biến thể nhỏ vào giải pháp của Aaronaught, tôi thường tham số số trang (@PageNum) và kích thước trang (@PageSize). Bằng cách này, mỗi sự kiện nhấp vào trang chỉ gửi số trang được yêu cầu cùng với kích thước trang có thể định cấu hình:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end

2

Gần nhất tôi có thể làm là

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Mà tôi đoán tương tự như select * from [db].[dbo].[table] LIMIT 0, 10


1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET

1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum

1

Vì chưa ai cung cấp mã này:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Điểm quan trọng:

  • ĐẶT HÀNG B mustNG phải giống hệt
  • @limit có thể được thay thế bằng số lượng kết quả để lấy,
  • @offset là số kết quả cần bỏ qua
  • Vui lòng so sánh hiệu suất với các giải pháp trước đây vì chúng có thể hiệu quả hơn
  • giải pháp này trùng lặp whereorder bymệnh đề và sẽ cung cấp kết quả không chính xác nếu chúng không đồng bộ
  • mặt khác order bylà có rõ ràng nếu đó là những gì cần thiết

1
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY

1

Cụ thể cho SQL-SERVER, bạn có thể đạt được điều đó theo nhiều cách khác nhau. Ví dụ thực tế, chúng tôi đã lấy bảng Khách hàng tại đây.

Ví dụ 1: Với "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Để trả về tất cả các hàng, đặt ROWCOUNT thành 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Ví dụ 2: Với "ROW_NUMBER và QUÁ"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Ví dụ 3: Với "OFFSET và FETCH", nhưng với "ORDER BY" này là bắt buộc

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Hy vọng điều này sẽ giúp bạn.


-1

Trong máy chủ SQL, bạn sẽ sử dụng TOP cùng với ROW_NUMBER ()


-1

Vì, tôi đã kiểm tra tập lệnh này nhiều lần hơn, hữu ích hơn 1 triệu bản ghi mỗi trang 100 bản ghi với phân trang hoạt động nhanh hơn PC của tôi thực thi tập lệnh này 0 giây trong khi so sánh với mysql có giới hạn riêng và bù khoảng 4,5 giây để có kết quả.

Ai đó có thể bỏ lỡ việc hiểu Row_Number () luôn sắp xếp theo trường cụ thể. Trong trường hợp chúng ta chỉ cần xác định hàng theo thứ tự nên sử dụng:

ROW_NUMBER () QUÁ (ĐẶT HÀNG B (NG (CHỌN NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Giải thích:

  • {LIMIT}: Số lượng hồ sơ cho mỗi trang
  • {OFFSET}: Số lượng hồ sơ bỏ qua

2
Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do giải quyết vấn đề này thực sự sẽ giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều lượt bình chọn hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những hạn chế và giả định được áp dụng.
Brian
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.