เป็นวิธีที่ดีที่สุดในการเลขหน้าผลลัพธ์ใน SQL Server อะไร


474

เป็นวิธีที่ดีที่สุด (ประสิทธิภาพฉลาด) เพื่อเลขหน้าผลลัพธ์ใน SQL Server 2000, 2005, 2008, 2012 ถ้าคุณต้องการได้รับจำนวนผลรวม (ก่อนที่จะเลขหน้า) คืออะไร?


26
ฉันสงสัยอยู่เสมอว่าทำไมพวกเขาถึงไม่สนับสนุนการระบุออฟเซ็ตซึ่งเป็นส่วนหนึ่งของ TOP (เช่น MySQL / Posgresql ที่รองรับ LIMIT / OFFSET) ตัวอย่างเช่นพวกเขาสามารถมีไวยากรณ์ "SELECT TOP x, y .... " โดยที่ x = จำนวนแถว y = เริ่มต้นการชดเชย มันจะเข้ากันได้ย้อนหลัง
gregmac

3
เฮ้ฉันก็เช่นกัน ... การดำเนินการในปี 2005 ของ sql การให้บริการเป็นเรื่องที่ดีดังนั้น akward ...
opensas

6
@gregmac - SQL Server 2012 มีการ จำกัด / ชดเชยในขณะนี้
OO

2
วิธีการแก้ปัญหาที่ยอมรับไม่ได้แสดงว่ามันเป็นวิธีที่ดีที่สุด (ประสิทธิภาพฉลาด) ข้อมูลใดที่สำรองไว้ในชุดข้อมูลขนาดใหญ่?
OO

3
@OO: เป็นมาตรฐานที่ดีสามารถพบได้ที่นี่: 4guysfromrolla.com/webtech/042606-1.shtml อย่างไรก็ตามวิธีการค้นหาจะดีกว่าเลขหน้าออฟเซ็ตใด ๆ
Lukas Eder

คำตอบ:


465

การรับจำนวนผลลัพธ์ทั้งหมดและการให้เลขหน้าเป็นการดำเนินการสองอย่าง เพื่อประโยชน์ของตัวอย่างนี้สมมติว่าแบบสอบถามที่คุณกำลังเผชิญคือ

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

ในกรณีนี้คุณจะพิจารณาจำนวนผลลัพธ์ทั้งหมดโดยใช้:

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

... ซึ่งอาจดูเหมือนไม่มีประสิทธิภาพ แต่จริง ๆ แล้วเป็นนักแสดงที่น่ารักสมมติว่ามีการตั้งค่าดัชนี ฯลฯ อย่างเหมาะสม

ถัดไปเพื่อให้ได้ผลลัพธ์ที่แท้จริงกลับมาเป็นแบบเพจการค้นหาต่อไปนี้จะมีประสิทธิภาพมากที่สุด:

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

สิ่งนี้จะส่งคืนแถวที่ 1-19 ของแบบสอบถามต้นฉบับ สิ่งดีๆที่นี่โดยเฉพาะอย่างยิ่งสำหรับเว็บแอปคือคุณไม่จำเป็นต้องมีสถานะใด ๆ ยกเว้นว่าจะส่งคืนหมายเลขแถว


37
เพียงสังเกตว่า ROW_NUMBER () ไม่มีอยู่ใน SQL Server 2000
John Hunter

6
สิ่งนี้ส่งคืนแถวทั้งหมดจากคำค้นหาภายในแล้วกรองตามการสืบค้นภายนอกหรือไม่ สำหรับอดีต: ผลตอบแทนภายในแบบสอบถาม 100,000 และผลตอบแทนแบบสอบถามด้านนอกเพียง 20
SoftwareGeek

2
@SoftwareGeek: คิดว่ามันเป็นแบบสอบถามย่อย (แบบสอบถามภายใน) ส่งกระแสข้อมูลซึ่งจะอ่านจนกว่าประโยค WHERE ด้านนอกเป็นที่พอใจ แถวอาจเกี่ยวข้องกับเรื่องนั้นอย่างไรขึ้นอยู่กับแบบสอบถามทั้งหมด แต่โดยทั่วไปเครื่องมือเพิ่มประสิทธิภาพจะทำงานได้ดีมากในการลดจำนวนนั้น การใช้โปรแกรมดูแผนการดำเนินการกราฟิกใน SQL Server Management Studio (ใช้ Query / Include Actual Execution Plan) เป็นเรื่องที่ให้ความรู้อย่างมาก
mdb

2
ตกลงจะเกิดอะไรขึ้นถ้าคุณได้รับการเผยแพร่ในตัวเลือกภายใน (เช่นเมื่อคุณมีการรวมภายใน) คุณจะใช้วิธีที่แตกต่างได้อย่างไรเนื่องจาก RowNumber แตกต่างกันและมันใช้งานไม่ได้
user217648

10
Microsoft ได้เพิ่มฟีเจอร์ใหม่ให้กับ SQL 2012 ที่ทำให้การแบ่งหน้าคล้ายกับ MySQL ตามลิงค์นี้เพื่อเรียนรู้วิธี มันเป็นบทความที่น่าสนใจ: dbadiaries.com/…
Arash

511

ในที่สุดMicrosoft SQL Server 2012เปิดตัวฉันชอบความเรียบง่ายสำหรับการแบ่งหน้าคุณไม่ต้องใช้คำสั่งที่ซับซ้อนเช่นตอบที่นี่

สำหรับการรับ 10 แถวถัดไปเพียงเรียกใช้แบบสอบถามนี้:

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#using-offset-and-fetch-to-limit-the-rows- กลับ

ประเด็นสำคัญที่ควรพิจารณาเมื่อใช้งาน:

  • ORDER BYจำเป็นต้องใช้OFFSET ... FETCHประโยค
  • OFFSETFETCHข้อมีผลบังคับใช้กับ ORDER BY ... FETCHคุณไม่สามารถใช้
  • TOPไม่สามารถใช้ร่วมกับOFFSETและFETCHในนิพจน์แบบสอบถามเดียวกันได้

12
ยังคงรอ/LISTAGG() GROUP_CONCAT()
เบคอน Bits

1
@BaconBits ดูคำตอบนี้ด้วยวิธีการลับๆด้วยFOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir

1
@ RichardMarskell-Drackir FOR XML PATH ('')มีจำนวนมากที่มีปัญหาคือ อันดับแรกจะแทนที่อักขระควบคุม XML ด้วยรหัสเอนทิตี XML หวังว่าคุณไม่ได้มี<, >หรือ&ในข้อมูลของคุณ! ประการที่สองที่FOR XML PATH ('')ใช้ในลักษณะนี้เป็นไวยากรณ์ที่ไม่มีเอกสารจริง คุณควรระบุคอลัมน์ที่ระบุชื่อหรือชื่อองค์ประกอบสำรอง การไม่ทำไม่ใช่ในเอกสารหมายความว่าพฤติกรรมไม่น่าเชื่อถือ ประการที่สามยิ่งเรายอมรับFOR XML PATH ('')ไวยากรณ์ที่ใช้งานไม่ได้มีโอกาสน้อยที่ MS จะให้ฟังก์ชันจริงตามที่ LISTAGG() [ OVER() ]ต้องการ
เบคอน Bits

4
ความอัปยศที่สมบูรณ์แบบนั้นแย่มากmssqlgirl.com/…
Jon

5
@ จอนบล็อกโพสต์ที่เชื่อมโยงนั้นไม่ได้เป็นตัวแทนในแง่ที่จะทำการเปรียบเทียบโดยขึ้นอยู่กับการส่งคืนผลลัพธ์ของหน้าเว็บโดยค้นหาค่าของคอลัมน์ id
Noel Abrahams

103

ไม่มีคำตอบอื่น ๆ ที่กล่าวถึงวิธีที่เร็วที่สุดในการแบ่งหน้าใน SQL Server ทุกรุ่น ชดเชยสามารถชะมัดช้าหมายเลขหน้าขนาดใหญ่ที่เป็นที่วัดประสิทธิผลที่นี่ มีวิธีที่แตกต่างกันอย่างสิ้นเชิงเร็วกว่ามากในการแบ่งหน้าใน SQL นี้มักจะถูกเรียกว่า "แสวงหาวิธีการ" หรือ "เลขชุดคีย์" ที่อธิบายไว้ในบล็อกโพสต์ที่นี่

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

"ค้นหาคำกริยา"

@previousScoreและ@previousPlayerIdค่าเป็นค่าตามลำดับของระเบียนสุดท้ายจากหน้าก่อนหน้านี้ สิ่งนี้ช่วยให้คุณสามารถดึงข้อมูลหน้า "ถัดไป" หากORDER BYทิศทางเป็นASCเพียงใช้>แทน

ด้วยวิธีการข้างต้นคุณไม่สามารถข้ามไปที่หน้า 4 ได้ทันทีโดยไม่ต้องดึงข้อมูล 40 รายการก่อนหน้า แต่บ่อยครั้งที่คุณไม่ต้องการที่จะกระโดดไกลเลยล่ะค่ะ แต่คุณจะได้รับแบบสอบถามที่เร็วกว่าซึ่งอาจดึงข้อมูลได้ในเวลาที่แน่นอนทั้งนี้ขึ้นอยู่กับการจัดทำดัชนีของคุณ นอกจากนี้หน้าเว็บของคุณยังคง "เสถียร" ไม่ว่าข้อมูลนั้นจะเปลี่ยนแปลงไป (เช่นในหน้า 1 ในขณะที่คุณอยู่ในหน้า 4)

นี่เป็นวิธีที่ดีที่สุดในการใช้การแบ่งหน้าเมื่อสันหลังยาวโหลดข้อมูลเพิ่มเติมในเว็บแอปพลิเคชัน

หมายเหตุที่ "แสวงหาวิธีการ" จะเรียกว่าเลขชุดคีย์

บันทึกทั้งหมดก่อนการแบ่งหน้า

COUNT(*) OVER()ฟังก์ชั่นหน้าต่างจะช่วยให้คุณนับจำนวนของระเบียนทั้งหมด "ก่อนที่จะแบ่งหน้า" หากคุณกำลังใช้ SQL Server 2000 COUNT(*)คุณจะต้องหันไปใช้แบบสอบถามที่สองสำหรับ


2
@ user960567: ในแง่ของประสิทธิภาพการเพจคีย์เซ็ตจะเอาชนะการเพจจิ้งเสมอไม่ว่าคุณจะใช้เพจจิ้งออฟเซ็ตด้วยมาตรฐาน SQL OFFSET .. FETCHหรือด้วยROW_NUMBER()เทคนิคก่อนหน้านี้
Lukas Eder

21
ฉันมีสามประเด็นด้วยวิธีการค้นหา [1] ผู้ใช้ไม่สามารถข้ามไปที่หน้า [2] ถือว่าเป็นคีย์ต่อเนื่องเช่นถ้ามีคนลบบางแถว 3 แถวจากนั้นฉันจะได้หน้า 7 รายการแทนที่จะเป็น 10 RowNumberให้ 10 รายการต่อหน้า [3] มันไม่ได้ทำงานกับกริดที่มีอยู่ที่สมมติและpagenumber pagesize
รีเบคก้า

7
@Junto: การเพจชุดคีย์ไม่เหมาะสำหรับทุกกรณี ไม่ใช่สำหรับกริดข้อมูล แต่มันสมบูรณ์แบบสำหรับสถานการณ์เช่นการเลื่อนหน้าฟีด Facebook แบบไม่ จำกัด ไม่สำคัญว่าจะมีการเพิ่มโพสต์ใหม่ที่ด้านบนโพสต์ฟีดที่ตามมาของคุณจะถูกเพิ่มที่ด้านล่างในขณะที่คุณเลื่อนลง ตัวอย่างการใช้งานที่สมบูรณ์แบบสำหรับสิ่งนี้ ... สิ่งนี้จะเป็นเรื่องยากมากที่จะใช้งานโดยใช้ offset limit / fetch โดยใช้ตัวเลขเท่านั้น
Robert Koritnik

4
ฉันต้องเห็นด้วยกับ Junto วิธีการนี้จะออกกฎไคลเอนต์ที่มีเลขหน้ามาตรฐานที่ค่อนข้างธรรมดาของ "ก่อนหน้า 1 2 3 (4) 5 6 ถัดไป" ซึ่งผู้ใช้สามารถข้ามไปข้างหน้าได้ นี้ไม่ว่ากรณีขอบในประสบการณ์ของฉัน ...
AaronHS

3
บทเลข Keyset ที่นี่
Stphane

31

จาก SQL Server 2012 เราสามารถใช้OFFSETและFETCH NEXTข้อเพื่อให้ได้เลขหน้า

ลองนี้สำหรับ SQL Server:

ใน SQL Server 2012 มีการเพิ่มคุณสมบัติใหม่ในส่วนคำสั่งซื้อเพื่อเพิ่มประสิทธิภาพของชุดข้อมูลทำให้ทำงานได้ง่ายขึ้นด้วยการสลับหน้าข้อมูลสำหรับผู้ที่เขียนใน T-SQL เช่นกันสำหรับแผนปฏิบัติการทั้งหมดใน SQL Server

ด้านล่างสคริปต์ T-SQL พร้อมกับตรรกะเดียวกับที่ใช้ในตัวอย่างก่อนหน้า

--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: การเพจการสอบถามด้วย SQL Server


คำตอบที่ถูกต้องที่สุดในการทดลองนี้
Vikrant

17

MSDN: ROW_NUMBER (Transact-SQL)

ส่งคืนหมายเลขลำดับของแถวภายในพาร์ติชันของชุดผลลัพธ์เริ่มต้นที่ 1 สำหรับแถวแรกในแต่ละพาร์ติชัน

ตัวอย่างต่อไปนี้จะส่งคืนแถวที่มีตัวเลขรวม 50 ถึง 60 ตามลำดับของวันที่สั่งซื้อ

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

มีภาพรวมที่ดีของเทคนิคการเพจที่แตกต่างกันที่http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

ฉันใช้วิธี ROWCOUNT บ่อยครั้งส่วนใหญ่กับ SQL Server 2000 (จะทำงานร่วมกับ 2005 และ 2008 ด้วยเพียงแค่วัดประสิทธิภาพเทียบกับ ROW_NUMBER) มันเร็วมาก แต่คุณต้องแน่ใจว่าคอลัมน์เรียงมี (ส่วนใหญ่) ) ค่าที่ไม่ซ้ำ


1
น่าสนใจบทความนั้นไม่ได้พูดถึงวิธีการสืบค้นซึ่งสามารถทำเพจจิ้งในเวลาคงที่ ... ยังเป็นบทความที่ดี
Lukas Eder

6

สำหรับ SQL Server 2000 คุณสามารถจำลอง ROW_NUMBER () โดยใช้ตัวแปรตารางที่มีคอลัมน์ประจำตัว:

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

วิธีการนี้สามารถขยายไปยังตารางที่มีปุ่มหลายคอลัมน์และไม่ทำให้เกิดค่าใช้จ่ายในการใช้หรือ (ซึ่งข้ามการใช้ดัชนี) ข้อเสียคือจำนวนพื้นที่ว่างชั่วคราวที่ใช้ไปหากชุดข้อมูลมีขนาดใหญ่มากและมีพื้นที่ใกล้เคียงกับหน้าสุดท้าย ฉันไม่ได้ทดสอบประสิทธิภาพของเคอร์เซอร์ในกรณีนั้น แต่อาจจะดีกว่า

โปรดทราบว่าวิธีนี้สามารถปรับให้เหมาะสมสำหรับหน้าแรกของข้อมูล นอกจากนี้ยังใช้ ROWCOUNT เนื่องจาก TOP ไม่ยอมรับตัวแปรใน SQL Server 2000


3

วิธีที่ดีที่สุดสำหรับการเพจใน sql server 2012 คือการใช้อ็อฟเซ็ตและดึงข้อมูลถัดไปในกระบวนงานที่เก็บไว้ คำสำคัญ OFFSET - ถ้าเราใช้ offset กับคำสั่งตามข้อแบบสอบถามจะข้ามจำนวนของบันทึกที่เราระบุไว้ใน OFFSET n แถว

FETCH NEXT Keywords - เมื่อเราใช้ Fetch Next พร้อมกับคำสั่งซื้อตามคำสั่งเท่านั้นจะส่งคืนจำนวนแถวที่คุณต้องการแสดงในเพจโดยไม่ต้อง Offset จากนั้น SQL จะสร้างข้อผิดพลาด นี่คือตัวอย่างที่ระบุด้านล่าง

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

คุณสามารถรันมันได้ดังต่อไปนี้

exec sp_paging 2,3

2

นี่คือโซลูชันของฉันสำหรับการเพจตามผลลัพธ์ของเคียวรีในฝั่งเซิร์ฟเวอร์ SQL วิธีการเหล่านี้แตกต่างกันระหว่าง SQL Server 2008 และ 2012 นอกจากนี้ฉันได้เพิ่มแนวคิดของการกรองและการเรียงลำดับด้วยคอลัมน์เดียว มันมีประสิทธิภาพมากเมื่อคุณเพจและกรองและสั่งซื้อใน Gridview ของคุณ

ก่อนการทดสอบคุณต้องสร้างตารางตัวอย่างหนึ่งตารางและแทรกบางแถวในตารางนี้: (ในโลกแห่งความเป็นจริงคุณต้องเปลี่ยนตำแหน่งข้อพิจารณาเขตข้อมูลตารางของคุณและบางทีคุณอาจมีส่วนร่วมและแบบสอบถามย่อยในส่วนหลักของการเลือก)

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

ในตัวอย่างเหล่านี้ทั้งหมดฉันต้องการสอบถาม 200 แถวต่อหน้าและฉันกำลังดึงแถวสำหรับหมายเลขหน้า 1200

ใน SQL Server 2008 คุณสามารถใช้แนวคิด CTE เนื่องจากว่าฉันได้เขียนแบบสอบถามสองประเภทสำหรับ SQL Server 2008+

- SQL Server 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.

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

และโซลูชั่นที่สองที่มี CTE ใน SQL Server 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

- SQL Server 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

ลองวิธีนี้:

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

ใช้กรณีที่ฉลาดต่อไปนี้ดูเหมือนจะใช้งานง่ายและรวดเร็ว เพียงตั้งหมายเลขหน้า

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)

ยังไม่มี 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
ที่ 1 = 1 ท่านทำอะไร
Errol Paleracio

0

ดีฉันได้ใช้แบบสอบถามตัวอย่างต่อไปนี้ในฐานข้อมูล SQL 2000 ของฉันมันทำงานได้ดีสำหรับ SQL 2005 ด้วย พลังที่ให้คุณสามารถจัดเรียงแบบไดนามิกโดยใช้หลายคอลัมน์ ฉันบอกคุณว่า ... มันมีพลังมาก :)

    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

ส่วนที่ดีที่สุดคือ sp_executesql แคชสายภายหลังให้คุณผ่านพารามิเตอร์เดียวกันคือสร้างข้อความ 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

จะรีสตาร์ท idx เมื่อมันมาถึง init_id ที่แตกต่างกัน


0

สำหรับROW_NUMBERเทคนิคถ้าคุณไม่มีคอลัมน์การเรียงลำดับที่จะใช้คุณสามารถใช้CURRENT_TIMESTAMPดังต่อไปนี้:

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

สิ่งนี้ทำงานได้ดีสำหรับฉันสำหรับการค้นหาในขนาดตารางที่สูงถึง 700,000

สิ่งนี้ดึงระเบียน 11 ถึง 30


เป็นการปฏิบัติที่ดีด้วยการแบ่งหน้าคุณควรพยายามเรียงลำดับคอลัมน์ที่ไม่ซ้ำกันในชุดผลลัพธ์เนื่องจากไม่ควรคำนึงถึงลำดับนั้น
Arin Taylor

2
สิ่งนี้นำบันทึก 11 ถึง 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

บิตนี้ช่วยให้คุณสามารถให้เลขหน้าโดยใช้ SQL Server และ MySQL รุ่นใหม่กว่าและมีจำนวนแถวทั้งหมดในทุกแถว ใช้คีย์ pimary ของคุณเพื่อนับจำนวนแถวที่ไม่ซ้ำกัน

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

คุณช่วยกรุณาแสดงความคิดเห็นใด ๆ ที่อธิบายว่ารหัสของคุณทำอะไรได้บ้าง?
Doug F

0

นี่เป็นคำถามซ้ำซ้อนของคำถาม SO ปี 2012: วิธีที่มีประสิทธิภาพในการปรับใช้เพจจิ้ง

จาก [TableX] เรียงตาม [FieldX] OFFSET 500 ROW FETCH ถัดไป 100 ROWS เท่านั้น

นี่คือหัวข้อที่มีการกล่าวถึงในรายละเอียดมากขึ้นและด้วยวิธีการอื่น



-19

คุณไม่ได้ระบุภาษาหรือไดรเวอร์ที่คุณใช้ ดังนั้นฉันอธิบายมันอย่างเป็นนามธรรม

  • สร้างชุดผลลัพธ์ / ชุดข้อมูลที่เลื่อนได้ สิ่งนี้จำเป็นต้องมีหลักบนโต๊ะ
  • ข้ามไปยังจุดสิ้นสุด
  • ขอจำนวนแถว
  • ข้ามไปที่จุดเริ่มต้นของหน้า
  • เลื่อนไปตามแถวจนจบหน้า
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.