Hàng bù trong SQL Server


133

Có cách nào trong SQL Server để có được kết quả bắt đầu ở một độ lệch cho trước không? Ví dụ: trong một loại cơ sở dữ liệu SQL khác, bạn có thể thực hiện:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

để có kết quả 51-75. Cấu trúc này dường như không tồn tại trong SQL Server.

Làm cách nào tôi có thể thực hiện việc này mà không tải tất cả các hàng tôi không quan tâm? Cảm ơn!


Bạn có thể sử dụng offset và tìm nạp câu lệnh tiếp theo. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Câu trả lời:


152

Tôi sẽ tránh sử dụng SELECT *. Chỉ định các cột bạn thực sự muốn mặc dù nó có thể là tất cả chúng.

Máy chủ SQL 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

Máy chủ SQL 2000

Phân trang hiệu quả thông qua các tập kết quả lớn trong SQL Server 2000

Một phương pháp hiệu quả hơn để phân trang thông qua các tập kết quả lớn


6
Tại sao bạn đề nghị tránh CHỌN ngay cả khi bạn đang chọn tất cả các cột?
Adam Ness

12
Tôi chắc rằng anh ấy đã sử dụng "*" bởi vì nó đơn giản hơn để gõ và có điểm tốt hơn "col1, col2, ... colN"
gillonba

9
Về lý do tại sao không sử dụng nó, SELECT *có nghĩa là nếu cấu trúc của bảng thay đổi, truy vấn của bạn vẫn chạy, nhưng cho kết quả khác nhau. Nếu một cột được thêm vào, điều này có thể hữu ích (mặc dù bạn vẫn phải sử dụng nó theo tên ở đâu đó); nếu một cột bị xóa hoặc đổi tên, tốt hơn hết là SQL của bạn sẽ phá vỡ rõ ràng hơn là mã tiếp tục hành xử kỳ quặc vì một biến không được khởi tạo.
IMSoP

5
Chọn tất cả dữ liệu của bảng và cắt? nếu có 5000000000 hàng? chọn 5000000000 hàng và cắt cho mỗi truy vấn? Nó không hiệu quả cho cpu và bộ nhớ của máy chủ.
điện tử

3
Xin lưu ý rằng 2012+ đã thực hiện theo cách tốt hơn. Xem câu trả lời của + Martin Smith
meridius

100

Nếu bạn sẽ xử lý tất cả các trang theo thứ tự thì chỉ cần nhớ giá trị khóa cuối cùng được thấy trên trang trước và sử dụng TOP (25) ... WHERE Key > @last_key ORDER BY Keycó thể là phương pháp hoạt động tốt nhất nếu các chỉ mục phù hợp tồn tại để cho phép tìm kiếm này một cách hiệu quả - hoặc con trỏ API nếu chúng không .

Để chọn một trang tùy ý, giải pháp tốt nhất cho SQL Server 2005 - 2008 R2 có lẽ là ROW_NUMBERBETWEEN

Đối với SQL Server 2012+, bạn có thể sử dụng mệnh đề ORDER BY nâng cao cho nhu cầu này.

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

Mặc dù vẫn còn phải xem tùy chọn này sẽ hoạt động tốt như thế nào .


2
Hiện đã có trong SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/l
Library / gg699618 (v = sql.110) .aspx

13
Đã đến lúc họ thêm cái này vào tQuery
JohnFx

3
Chỉ dành cho Sql Server 2012 :(
e-information128

22

Đây là một cách (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

và đây là một cách khác (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

Chỉ cần làm rõ ở cái đầu tiên ... (@pageSize) là một trình giữ chỗ ở đây cho giá trị thực tế. Bạn sẽ phải làm 'TOP 25' một cách cụ thể; SQL Server 2000 không hỗ trợ các biến trong mệnh đề TOP. Điều này làm cho nó một nỗi đau liên quan đến SQL động.
Cowan

5
Giải pháp đó cho SQL2000 không hoạt động cho trang cuối cùng trong tập kết quả, trừ khi tổng số hàng xảy ra là bội số của kích thước trang.
Bill Karwin

10

Bạn có thể sử dụng ROW_NUMBER()chức năng để có được những gì bạn muốn:

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

7

OFFSET .. FETCHtrong SQL Server 2012, nhưng bạn sẽ cần chỉ định một ORDER BYcột.

Nếu bạn thực sự không có bất kỳ cột rõ ràng nào mà bạn có thể vượt qua dưới dạng ORDER BYcột (như những người khác đã đề xuất), thì bạn có thể sử dụng thủ thuật này:

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

... hoặc là

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

Chúng tôi đang sử dụng nó trong jOOQ khi người dùng không chỉ định rõ ràng một đơn đặt hàng. Điều này sau đó sẽ tạo ra thứ tự khá ngẫu nhiên mà không có bất kỳ chi phí bổ sung.


6

Đối với các bảng có nhiều cột dữ liệu lớn hơn, tôi thích:

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.

Nó có hiệu suất tốt hơn nhiều trên các bảng có dữ liệu lớn như BLOB, vì hàm ROW_NUMBER chỉ phải xem qua một cột và chỉ các hàng phù hợp được trả về với tất cả các cột.


5

Xem lựa chọn của tôi cho 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

Điều này giải quyết phân trang;)


3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable

Hiệu suất khôn ngoan dường như không tối ưu vì truy vấn sau đó được thực hiện không cần thiết hai lần. Đặc biệt là khi người dùng đi đến các trang cao hơn, truy vấn để loại bỏ các hàng, tức là phần bên dưới EXCEPT sẽ mất nhiều thời gian hơn.
vanval

2

Tùy thuộc vào phiên bản của bạn, bạn không thể làm điều đó trực tiếp, nhưng bạn có thể làm điều gì đó như hack

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

trong đó 'trường' là chìa khóa.


4
Giải pháp đó cho SQL2000 không hoạt động cho trang cuối cùng trong tập kết quả, trừ khi tổng số hàng xảy ra là bội số của kích thước trang.
Bill Karwin

2

Sau đây sẽ hiển thị 25 bản ghi trừ 50 bản ghi đầu tiên hoạt động trong SQL Server 2012.

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

bạn có thể thay thế ID theo yêu cầu của bạn


Xin vui lòng, thêm điều này là có thể trong SQL SERVER 2012
Usman Younas

2

Bạn nên cẩn thận khi sử dụng ROW_NUMBER() OVER (ORDER BY)câu lệnh vì hiệu suất khá kém. Tương tự với việc sử dụng Biểu thức bảng chung với ROW_NUMBER()điều đó thậm chí còn tồi tệ hơn. Tôi đang sử dụng đoạn mã sau đã được chứng minh là nhanh hơn một chút so với sử dụng biến bảng có nhận dạng để cung cấp số trang.

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

Điều này sẽ trả về 11 hàng, không phải 10.
Aaron Bertrand

1

Tôi sử dụng kỹ thuật này để phân trang. Tôi không lấy tất cả các hàng. Ví dụ: nếu trang của tôi cần hiển thị 100 hàng trên cùng, tôi chỉ tìm nạp 100 với mệnh đề where. Đầu ra của SQL phải có một khóa duy nhất.

Bảng có các mục sau:

ID, KeyId, Rank

Thứ hạng tương tự sẽ được chỉ định cho nhiều hơn một KeyId.

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

Lần đầu tiên tôi vượt qua 0 cho cả hai. Lần thứ hai vượt qua 1 & 14. Lần thứ 3 vượt qua 2 và 6 ....

Giá trị của Xếp hạng & Id của bản ghi thứ 10 được chuyển sang tiếp theo

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

Điều này sẽ có ít căng thẳng nhất trên hệ thống


1

Trong SqlServer2005, bạn có thể làm như sau:

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) 

Có nên không @Offset + @Limit - 1? Nếu @Limit là 10 thì sẽ trả về 11 hàng.
Aaron Bertrand

1

Cách tốt nhất để làm điều đó mà không lãng phí thời gian để đặt hàng hồ sơ là như thế này:

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

phải mất ít hơn một giây!
giải pháp tốt nhất cho bàn lớn.


0

Tôi đã tìm kiếm câu trả lời này một thời gian rồi (cho các truy vấn chung) và tìm ra một cách khác để thực hiện nó trên SQL Server 2000+ bằng cách sử dụng ROWCOUNT và các con trỏ và không có TOP hoặc bất kỳ bảng tạm thời nào.

Sử dụng SET ROWCOUNT [OFFSET+LIMIT]bạn có thể giới hạn kết quả và với con trỏ, đi thẳng đến hàng bạn muốn, sau đó lặp 'cho đến khi kết thúc.

Vì vậy, truy vấn của bạn sẽ như thế này:

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

Tôi ghét phải xem hiệu suất của điều này khi bạn đi đến cuối bàn ...
Aaron Bertrand

0

Với SQL Server 2012 (11.x) trở lên và Cơ sở dữ liệu SQL Azure, bạn cũng có thể có "fetch_row_count_expression", bạn cũng có thể có mệnh đề ORDER BY cùng với điều này.

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

Lưu ý OFFSET Chỉ định số lượng hàng cần bỏ qua trước khi bắt đầu trả về các hàng từ biểu thức truy vấn. Nó KHÔNG phải là số hàng bắt đầu. Vì vậy, nó phải là 0 để bao gồm bản ghi đầu tiên.

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.