Cách tốt nhất để phân trang kết quả trong SQL Server là gì


474

Cách tốt nhất (hiệu suất khôn ngoan) để phân trang kết quả trong SQL Server 2000, 2005, 2008, 2012 nếu bạn cũng muốn có được tổng số kết quả (trước khi phân trang)?


26
Tôi đã luôn tự hỏi tại sao họ không chỉ hỗ trợ chỉ định một phần bù là một phần của TOP (như hỗ trợ MySQL / Posgresql với LIMIT / OFFSET). Ví dụ: họ chỉ có thể có cú pháp "CHỌN TOP x, y ...." trong đó x = số hàng, y = bắt đầu bù. Nó cũng sẽ tương thích ngược.
gregmac

3
này, tôi cũng vậy ... triển khai phân trang năm 2005 của sql thật sự rất tệ ...
opensas

6
@gregmac - Sql Server 2012 hiện có giới hạn / bù.
OO

2
Giải pháp được chấp nhận không chỉ ra cách thức đó là cách tốt nhất (hiệu suất khôn ngoan). Bất kỳ dữ liệu sao lưu nó trên tập dữ liệu lớn?
OO

3
@OO: Một điểm chuẩn tốt có thể được tìm thấy ở đây: 4guysfromrolla.com/webtech/042606-1.shtml . Tuy nhiên, phương pháp tìm kiếm sẽ tốt hơn bất kỳ phân trang dựa trên bù nào.
Lukas Eder

Câu trả lời:


465

Lấy tổng số kết quả và phân trang là hai thao tác khác nhau. Vì lợi ích của ví dụ này, hãy giả sử rằng truy vấn bạn đang xử lý là

SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

Trong trường hợp này, bạn sẽ xác định tổng số kết quả bằng cách sử dụng:

SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'

... có vẻ không hiệu quả, nhưng thực sự khá hiệu quả, giả sử tất cả các chỉ mục, vv được thiết lập đúng.

Tiếp theo, để lấy lại kết quả thực tế theo kiểu phân trang, truy vấn sau đây sẽ hiệu quả nhất:

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

Điều này sẽ trả về các hàng 1-19 của truy vấn ban đầu. Điều thú vị ở đây, đặc biệt là đối với các ứng dụng web, là bạn không phải giữ bất kỳ trạng thái nào, ngoại trừ số hàng được trả về.


37
Chỉ cần lưu ý rằng ROW_NUMBER () không tồn tại trong SQL Server 2000
John Hunter

6
điều này có trả về tất cả các hàng từ truy vấn bên trong và sau đó lọc dựa trên truy vấn bên ngoài không? ví dụ: truy vấn bên trong trả về 100.000 và truy vấn bên ngoài chỉ trả về 20.
SoftwareGeek

2
@SoftwareGeek: nghĩ về nó như truy vấn con (truy vấn bên trong) trả về một luồng, sau đó được đọc cho đến khi mệnh đề WHERE bên ngoài được thỏa mãn. Làm thế nào các hàng có thể liên quan đến điều đó, phụ thuộc hoàn toàn vào truy vấn, nhưng trình tối ưu hóa thường làm rất tốt công việc giảm thiểu số đó. Sử dụng trình xem kế hoạch thực hiện đồ họa trong SQL Server Management Studio (sử dụng Truy vấn / Bao gồm Kế hoạch thực thi thực tế) rất có ý nghĩa về mặt đó.
mdb

2
ok, nếu bạn được cấp phép trong lựa chọn bên trong (như khi bạn có tham gia bên trong) thì bạn sử dụng khác biệt như thế nào vì RowNumber khác biệt và nó không hoạt động
user217648

10
Microsoft đã thêm một tính năng mới cho SQL 2012 giúp phân trang tương tự như MySQL. Theo liên kết này để tìm hiểu làm thế nào. Đây là một bài viết thú vị: dbadiaries.com/ từ
Arash

512

Cuối cùng, Microsoft SQL Server 2012 đã được phát hành, tôi thực sự thích sự đơn giản của nó cho việc phân trang, bạn không phải sử dụng các truy vấn phức tạp như đã trả lời ở đây.

Để nhận được 10 hàng tiếp theo, chỉ cần chạy truy vấn này:

SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#USE-offset-and-fetch-to-limit-the-rows- trả lại

Những điểm chính cần xem xét khi sử dụng nó:

  • ORDER BYlà bắt buộc để sử dụng OFFSET ... FETCHmệnh đề.
  • OFFSETmệnh đề là bắt buộc với FETCH. Bạn không thể sử dụng ORDER BY ... FETCH.
  • TOPkhông thể được kết hợp với OFFSETFETCHtrong cùng một biểu thức truy vấn.

12
Vẫn đang chờ LISTAGG()/ GROUP_CONCAT().
Bacon Bits

1
@BaconBits Xem câu trả lời này để biết cách thực hiện một cách lén lút với FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir

1
@ RichardMarskell-Drackir Có rất nhiều vấn đề với FOR XML PATH (''). Đầu tiên, nó thay thế các ký tự điều khiển XML bằng mã thực thể XML. Hy vọng bạn không có <, >hoặc &trong dữ liệu của bạn! Thứ hai, FOR XML PATH ('')được sử dụng theo cách này thực sự là cú pháp không có giấy tờ. Bạn phải chỉ định một cột được đặt tên hoặc một tên thành phần thay thế. Làm không phải là không có trong tài liệu, có nghĩa là hành vi là không đáng tin cậy. Thứ ba, chúng ta càng chấp nhận FOR XML PATH ('')cú pháp bị hỏng , thì càng ít khả năng MS thực sự cung cấp một chức năng thực sự LISTAGG() [ OVER() ] như họ cần.
Bacon Bits

4
xấu hổ vì sự hoàn hảo là rất tệ mssqlgirl.com / Sự kiện
Jon

5
@Jon, bài đăng trên blog được liên kết đó không phải là đại diện, theo nghĩa là nó tạo ra sự so sánh dựa trên việc trả về kết quả trang bằng cách tìm kiếm các giá trị của cột id.
Noel Abrahams

103

Thật đáng kinh ngạc, không có câu trả lời nào khác đề cập đến cách nhanh nhất để phân trang trong tất cả các phiên bản SQL Server. Độ lệch có thể rất chậm đối với số lượng trang lớn như được điểm chuẩn ở đây . Có một cách hoàn toàn khác, nhanh hơn nhiều để thực hiện phân trang trong SQL. Điều này thường được gọi là "phương pháp tìm kiếm" hoặc "phân trang keyset" như được mô tả trong bài đăng trên blog này ở đây .

SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

"Vị ngữ tìm kiếm"

Các giá trị @previousScore@previousPlayerIdlà các giá trị tương ứng của bản ghi cuối cùng từ trang trước. Điều này cho phép bạn tìm nạp trang "tiếp theo". Nếu ORDER BYhướng là ASC, chỉ cần sử dụng >thay thế.

Với phương pháp trên, bạn không thể ngay lập tức chuyển sang trang 4 mà không cần tìm nạp 40 bản ghi trước. Nhưng thông thường, bạn không muốn nhảy xa như vậy. Thay vào đó, bạn nhận được một truy vấn nhanh hơn nhiều có thể có thể tìm nạp dữ liệu trong thời gian liên tục, tùy thuộc vào việc lập chỉ mục của bạn. Ngoài ra, các trang của bạn vẫn "ổn định", bất kể dữ liệu cơ bản có thay đổi hay không (ví dụ trên trang 1, trong khi bạn ở trang 4).

Đây là cách tốt nhất để thực hiện phân trang khi lười tải nhiều dữ liệu hơn trong các ứng dụng web.

Lưu ý, "phương pháp tìm kiếm" còn được gọi là phân trang keyset .

Tổng số hồ sơ trước khi phân trang

Các COUNT(*) OVER()chức năng cửa sổ sẽ giúp bạn đếm số lượng tổng số hồ sơ "trước khi pagination". Nếu bạn đang sử dụng SQL Server 2000, bạn sẽ phải sử dụng hai truy vấn cho COUNT(*).


2
@ user960567: Về hiệu năng, phân trang keyset sẽ luôn đánh bại phân trang bù, bất kể bạn thực hiện phân trang bù với tiêu chuẩn SQL OFFSET .. FETCHhay với các ROW_NUMBER()thủ thuật trước đó .
Lukas Eder

21
Tôi có ba vấn đề với phương pháp tìm kiếm. [1] Một người dùng không thể nhảy đến trang. [2] nó giả sử các khóa liên tiếp, tức là nếu ai đó xóa khoảng 3 hàng, thì tôi nhận được một trang gồm 7 mục thay vì 10. RowNumbercung cấp cho tôi 10 mục nhất quán trên mỗi trang. [3] nó không hoạt động với các lưới hiện có giả định pagenumberpagesize.
Rebecca

7
@Junto: phân trang keyset không phù hợp với mọi trường hợp. Nó chắc chắn không dành cho lưới dữ liệu. Nhưng nó hoàn hảo cho các tình huống như cuộn trang vô hạn của trang Facebook. Không có vấn đề gì nếu bài viết mới đang được thêm ở trên cùng, bài đăng nguồn cấp tiếp theo của bạn sẽ được thêm chính xác vào cuối trong khi bạn cuộn xuống. Ví dụ sử dụng hoàn hảo cho việc này ... Điều đó sẽ khó thực hiện hơn nhiều khi chỉ sử dụng giới hạn bù / tìm nạp chỉ sử dụng số.
Robert Koritnik

4
Tôi phải đồng ý với Junto. Phương pháp này loại bỏ hoàn toàn một ứng dụng khách có số trang phân trang khá chuẩn là "Trước 1 2 3 (4) 5 6 Tiếp theo" nơi người dùng có thể nhảy lên trước. Đây không hẳn là một trường hợp
vượt trội

3
Bài viết phân trang keyset tại đây
Stphane

31

Từ SQL Server 2012, chúng tôi có thể sử dụng OFFSETFETCH NEXTkhoản để đạt được phân trang.

Hãy thử điều này, đối với SQL Server:

Trong SQL Server 2012, một tính năng mới đã được thêm vào trong mệnh đề ORDER BY, để truy vấn tối ưu hóa dữ liệu đã đặt, giúp công việc phân trang dữ liệu dễ dàng hơn cho bất kỳ ai viết bằng T-SQL cũng như cho toàn bộ Kế hoạch thực thi trong SQL Server.

Bên dưới tập lệnh T-SQL có cùng logic được sử dụng trong ví dụ trước.

--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE @PageNumber AS INT, @RowspPage AS INT
SET @PageNumber = 2
SET @RowspPage = 10 
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((@PageNumber - 1) * @RowspPage) ROWS
FETCH NEXT @RowspPage ROWS ONLY;

TechNet: Phân trang truy vấn với SQL Server


câu trả lời chính xác nhất trong thử nghiệm này
Vikrant

17

MSDN: ROW_NUMBER (Giao dịch-SQL)

Trả về số thứ tự của một hàng trong một phân vùng của tập kết quả, bắt đầu từ 1 cho hàng đầu tiên trong mỗi phân vùng.

Ví dụ sau đây trả về các hàng có các số từ 50 đến 60 được bao gồm theo thứ tự của OrderDate.

WITH OrderedOrders AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber, 
        FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
    FROM [dbo].[vSalesPerson]
) 
SELECT RowNumber, 
    FirstName, LastName, Sales YTD 
FROM OrderedOrders 
WHERE RowNumber > 50 AND RowNumber < 60;
  RowNumber FirstName    LastName               SalesYTD
  --- -----------  ---------------------- -----------------
  1   Linda        Mitchell               4251368.54
  2   Jae          Pak                    4116871.22
  3   Michael      Blythe                 3763178.17
  4   Jillian      Carson                 3189418.36
  5   Ranjit       Varkey Chudukatil      3121616.32
  6   José         Saraiva                2604540.71
  7   Shu          Ito                    2458535.61
  8   Tsvi         Reiter                 2315185.61
  9   Rachel       Valdez                 1827066.71
  10  Tete         Mensa-Annan            1576562.19
  11  David        Campbell               1573012.93
  12  Garrett      Vargas                 1453719.46
  13  Lynn         Tsoflias               1421810.92
  14  Pamela       Ansman-Wolfe           1352577.13

15

Có một tổng quan tốt về các kỹ thuật phân trang khác nhau tại http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

Tôi đã sử dụng phương pháp ROWCOUNT khá thường xuyên với SQL Server 2000 (cũng sẽ hoạt động với 2005 & 2008, chỉ đo hiệu suất so với ROW_NUMBER), nó nhanh như chớp, nhưng bạn cần chắc chắn rằng (các) cột được sắp xếp có (hầu hết) ) các giá trị duy nhất.


1
Thật thú vị, bài báo đó không đề cập đến phương pháp tìm kiếm , có thể thực hiện phân trang trong thời gian liên tục ... Vẫn là một bài viết hay
Lukas Eder

6

Đối với SQL Server 2000, bạn có thể mô phỏng ROW_NUMBER () bằng cách sử dụng biến bảng với cột IDENTITY:

DECLARE @pageNo int -- 1 based
DECLARE @pageSize int
SET @pageNo = 51
SET @pageSize = 20

DECLARE @firstRecord int
DECLARE @lastRecord int
SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001
SET @lastRecord = @firstRecord + @pageSize - 1   -- 1020

DECLARE @orderedKeys TABLE (
  rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
  TableKey int NOT NULL
)

SET ROWCOUNT @lastRecord
INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

SET ROWCOUNT 0

SELECT t.*
FROM Orders t
  INNER JOIN @orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= @firstRecord
ORDER BY o.rownum

Cách tiếp cận này có thể được mở rộng thành các bảng có các phím nhiều cột và nó không phải chịu chi phí hiệu năng khi sử dụng OR (bỏ qua việc sử dụng chỉ mục). Nhược điểm là lượng không gian tạm thời được sử dụng hết nếu tập dữ liệu rất lớn và một ở gần trang cuối cùng. Tôi đã không kiểm tra hiệu suất con trỏ trong trường hợp đó, nhưng nó có thể tốt hơn.

Lưu ý rằng phương pháp này có thể được tối ưu hóa cho trang dữ liệu đầu tiên. Ngoài ra, ROWCOUNT đã được sử dụng do TOP không chấp nhận một biến trong SQL Server 2000.


3

Cách tốt nhất để phân trang trong máy chủ sql 2012 là sử dụng offset và tìm nạp tiếp theo trong một thủ tục được lưu trữ. Từ khóa OFFSET - Nếu chúng tôi sử dụng offset theo thứ tự theo mệnh đề thì truy vấn sẽ bỏ qua số lượng bản ghi mà chúng tôi đã chỉ định trong OFFSET n Rows.

FETCH TIẾP THEO Từ khóa - Khi chúng tôi sử dụng Fetch Next với mệnh đề theo thứ tự, nó sẽ trả về không có hàng nào bạn muốn hiển thị trong phân trang, không có Offset thì SQL sẽ phát sinh lỗi. đây là ví dụ được đưa ra dưới đây.

create procedure sp_paging
(
 @pageno as int,
 @records as int
)
as
begin
declare @offsetcount as int
set @offsetcount=(@pageno-1)*@records
select id,bs,variable from salary order by id offset @offsetcount rows fetch Next @records rows only
end

bạn có thể thực hiện nó như sau.

exec sp_paging 2,3

2

Đây là những giải pháp của tôi để phân trang kết quả của truy vấn trong phía máy chủ SQL. các cách tiếp cận này khác nhau giữa SQL Server 2008 và 2012. Ngoài ra, tôi đã thêm khái niệm lọc và đặt hàng bằng một cột. Nó rất hiệu quả khi bạn phân trang và lọc và đặt hàng trong Gridview của bạn.

Trước khi kiểm tra, bạn phải tạo một bảng mẫu và chèn một số hàng trong bảng này: (Trong thế giới thực, bạn phải thay đổi mệnh đề Where xem xét các trường bảng của bạn và có thể bạn có một số tham gia và truy vấn phụ trong phần chính của lựa chọn)

Create Table VLT
(
    ID int IDentity(1,1),
    Name nvarchar(50),
    Tel Varchar(20)
)
GO


Insert INTO VLT
VALUES
    ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000

Trong tất cả các mẫu này, tôi muốn truy vấn 200 hàng trên mỗi trang và tôi đang tìm nạp hàng cho số trang 1200.

Trong máy chủ SQL 2008, bạn có thể sử dụng khái niệm CTE. Do đó, tôi đã viết hai loại truy vấn cho máy chủ SQL 2008+

- Máy chủ SQL 2008 trở lên

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT 
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1   
  ) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

GO

Và giải pháp thứ hai với CTE trong máy chủ SQL 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1     
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

- Máy chủ SQL 2012+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      *  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1         
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
ORDER BY 
    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
        THEN Data.ID END ASC,
    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
        THEN Data.ID END DESC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
        THEN Data.Tel END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
        THEN Data.Tel END ASC
OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;

1

Hãy thử phương pháp này:

SELECT TOP @offset a.*
FROM (select top @limit b.*, COUNT(*) OVER() totalrows 
        from TABLENAME b order by id asc) a
ORDER BY id desc;

1

Trường hợp sử dụng khôn ngoan sau đây dường như là dễ sử dụng và nhanh chóng. Chỉ cần đặt số trang.

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

cũng không có CTE

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
 ) AS SOD
WHERE SOD.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

1
Điều gì làm 1 = 1 làm gì thưa ông?
Errol Paleracio

0

Vâng, tôi đã sử dụng các truy vấn mẫu sau trong cơ sở dữ liệu SQL 2000 của tôi, nó hoạt động tốt cho SQL 2005 quá. Sức mạnh mà nó mang lại cho bạn là thứ tự động bằng cách sử dụng nhiều cột. Tôi nói với bạn ... điều này là mạnh mẽ :)

    ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 

@CompanyID  int,
@pageNumber     int,
@pageSize   int, 
@sort       varchar(200)
AS

DECLARE @sql nvarchar(4000)
DECLARE @strPageSize nvarchar(20)
DECLARE @strSkippedRows nvarchar(20)
DECLARE @strFields nvarchar(4000)
DECLARE @strFilter nvarchar(4000)
DECLARE @sortBy nvarchar(4000)
DECLARE @strFrom nvarchar(4000)
DECLARE @strID nvarchar(100)

If(@pageNumber < 0)
  SET @pageNumber = 1
SET @strPageSize = CAST(@pageSize AS varchar(20)) 
SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
SET @strID = 'ListingDbID'
SET @strFields = 'ListingDbID,
ListingID,  
[ExtraRoom]
'
SET @strFrom = ' vwListingSummary '

SET @strFilter = ' WHERE
        CompanyID = ' + CAST(@CompanyID As varchar(20)) 
End
SET @sortBy = ''
if(len(ltrim(rtrim(@sort))) > 0)
SET @sortBy = ' Order By ' + @sort

-- Total Rows Count

SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
EXEC sp_executesql @sql

--// This technique is used in a Single Table pagination
SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
    ' WHERE ' + @strID +  ' IN ' + 
   '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
             ' AND  ' + @strID + ' NOT IN ' + '
          (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
   + @SortBy + ') ' + @SortBy
Print @sql 
EXEC sp_executesql @sql

Phần tốt nhất là sp_executesql lưu trữ các cuộc gọi sau này, miễn là bạn truyền cùng một tham số tức là tạo cùng một văn bản sql.


0
   CREATE view vw_sppb_part_listsource as 
    select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
      select 
          part.SPPB_PART_ID
          , 0 as is_rev
          , part.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
      where prev.SPPB_PART_ID is null 
      union 
      select 
          part.SPPB_PART_ID
          , 1 as is_rev
          , prev.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
    ) sppb_part

sẽ khởi động lại idx khi nói đến init_id khác


0

Đối với ROW_NUMBERkỹ thuật, nếu bạn không có cột sắp xếp để sử dụng, bạn có thể sử dụng CURRENT_TIMESTAMPnhư sau:

SELECT TOP 20 
    col1,
    col2,
    col3,
    col4
FROM (
    SELECT 
         tbl.col1 AS col1
        ,tbl.col2 AS col2
        ,tbl.col3 AS col3
        ,tbl.col4 AS col4
        ,ROW_NUMBER() OVER (
            ORDER BY CURRENT_TIMESTAMP
            ) AS sort_row
    FROM dbo.MyTable tbl
    ) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row

Điều này đã làm việc tốt cho tôi khi tìm kiếm trên các kích thước bảng thậm chí lên tới 700.000.

Điều này lấy các bản ghi từ 11 đến 30.


Như một cách thực hành tốt, với phân trang, bạn nên cố gắng đặt hàng theo một nhóm cột duy nhất trong tập kết quả vì không nên coi thứ tự là đảm bảo.
Arin Taylor

2
Điều này lấy các bản ghi từ 11 đến 30.
Ardalan Shahgholi

0
create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;


    select  Id , NameEn     from Company  ORDER by Id ASC  
OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO

DECLARE   @return_value int

EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2

SELECT    'Return Value' = @return_value

GO

0

Bit này cung cấp cho bạn khả năng phân trang bằng SQL Server và các phiên bản mới hơn của MySQL và mang tổng số hàng trong mỗi hàng. Sử dụng khóa pimary của bạn để đếm số lượng hàng duy nhất.

WITH T AS
(  
  SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
  , (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL 
  FROM TABLE (NOLOCK)
)

SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL 
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200

Bạn có thể vui lòng để lại bất kỳ ý kiến ​​giải thích mã của bạn làm gì?
Doug F


0

Từ năm 2012 trở đi chúng ta có thể sử dụng OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY


-19

Bạn đã không chỉ định ngôn ngữ cũng như trình điều khiển bạn đang sử dụng. Vì vậy, tôi đang mô tả nó một cách trừu tượng.

  • Tạo một resultset cuộn / tập dữ liệu. Điều này đòi hỏi một chính trên (các) bảng
  • nhảy đến cuối
  • yêu cầu đếm hàng
  • nhảy đến đầu trang
  • cuộn qua các hàng cho đến cuối trang
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.