รับจำนวนแถวทั้งหมดจาก OFFSET / FETCH NEXT


92

ดังนั้นฉันจึงมีฟังก์ชันที่ส่งคืนระเบียนจำนวนหนึ่งที่ฉันต้องการใช้การเพจสำหรับเว็บไซต์ของฉัน มีการแนะนำให้ฉันใช้ Offset / Fetch Next ใน SQL Server 2012 เพื่อทำสิ่งนี้ให้สำเร็จ ในเว็บไซต์ของเราเรามีพื้นที่ที่แสดงจำนวนบันทึกทั้งหมดและหน้าที่คุณอยู่ในเวลานั้น

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

คำตอบ:


115

คุณสามารถใช้COUNT(*) OVER()... นี่คือตัวอย่างสั้น ๆ โดยใช้sys.all_objects:

DECLARE 
  @PageSize INT = 10, 
  @PageNum  INT = 1;

SELECT 
  name, object_id, 
  overall_count = COUNT(*) OVER()
FROM sys.all_objects
ORDER BY name
  OFFSET (@PageNum-1)*@PageSize ROWS
  FETCH NEXT @PageSize ROWS ONLY;

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


44
ในตารางที่มีบันทึก 3,500,000 รายการ COUNT (*) OVER () ใช้เวลา 1 นาที 3 วินาที วิธีการที่ James Moberg อธิบายไว้ด้านล่างใช้เวลา 13 วินาทีในการดึงข้อมูลชุดเดียวกัน ฉันแน่ใจว่าวิธีการนับมากกว่าใช้งานได้ดีสำหรับชุดข้อมูลที่มีขนาดเล็ก แต่เมื่อคุณเริ่มมีขนาดใหญ่มากมันจะช้าลงอย่างมาก
matthew_360

หรือคุณสามารถใช้ COUNT (1) OVER () ซึ่งเร็วกว่าเนื่องจากไม่ต้องอ่านข้อมูลจริงจากตารางเช่น count (*) ทำ
ldx

1
@AaronBertrand จริงเหรอ? นั่นต้องหมายความว่าคุณมีดัชนีที่มีคอลัมน์ทั้งหมดหรือสิ่งนี้ได้รับการปรับปรุงอย่างมากตั้งแต่ปี 2008R2 ในเวอร์ชันนั้นการนับ (*) จะทำงานตามลำดับซึ่งหมายความว่าเลือก * (เช่นเดียวกับใน: คอลัมน์ทั้งหมด) แรกจากนั้นจึงนับ หากคุณนับ (1) คุณเพียงแค่เลือกค่าคงที่ซึ่งเร็วกว่าการอ่านข้อมูลจริงมาก
ldx

5
@idx ไม่นั่นไม่ใช่วิธีการทำงานใน 2008 R2 ด้วยเช่นกันขอโทษ ฉันใช้ SQL Server มาตั้งแต่ 6.5 และฉันจำไม่ได้ว่ามีช่วงเวลาที่เอ็นจิ้นไม่ฉลาดพอที่จะสแกนดัชนีที่แคบที่สุดสำหรับทั้ง COUNT (*) หรือ COUNT (1) ไม่ใช่ตั้งแต่ปี 2000 แต่เดี๋ยวก่อนฉันมีตัวอย่างของ 2008 R2 คุณสามารถตั้งค่าการ repro บน SQLfiddle ที่แสดงให้เห็นถึงความแตกต่างที่คุณอ้างว่ามีอยู่ได้หรือไม่? ฉันยินดีที่จะลอง
Aaron Bertrand

2
บนฐานข้อมูล sql server 2016 ค้นหาบนตารางที่มีประมาณ 25 ล้านแถวเพจมากกว่า 3,000 ผลลัพธ์ (มีการรวมหลายตัวรวมถึงฟังก์ชันที่มีมูลค่าตาราง) ซึ่งใช้เวลามิลลิวินาที - ยอดเยี่ยมมาก!
jkerak

142

ฉันพบปัญหาประสิทธิภาพการทำงานโดยใช้วิธี COUNT ( ) OVER () (ฉันไม่แน่ใจว่าเป็นเซิร์ฟเวอร์หรือไม่เนื่องจากใช้เวลา 40 วินาทีในการส่งคืนข้อมูล 10 รายการจากนั้นจึงไม่มีปัญหาใด ๆ ) เทคนิคนี้ทำงานภายใต้เงื่อนไขทั้งหมดโดยไม่ต้องใช้ COUNT ( ) OVER () และทำ สิ่งเดียวกัน:

DECLARE 
    @PageSize INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, Name
    FROM Table
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)
SELECT *
FROM TempResult, TempCount
ORDER BY TempResult.Name
    OFFSET (@PageNum-1)*@PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY

32
มันจะดีมากถ้ามีความเป็นไปได้ที่จะบันทึกค่า COUNT (*) ให้กับตัวแปร ฉันจะสามารถตั้งเป็นพารามิเตอร์ OUTPUT ของ Stored Procedure ของฉันได้ ความคิดใด ๆ ?
ถึงกา

1
มีวิธีใดในการนับในตารางแยกหรือไม่? ดูเหมือนว่าคุณสามารถใช้ "TempResult" สำหรับคำสั่ง SELECT ก่อนหน้าเท่านั้น
matthew_360

4
ทำไมถึงได้ผลดี? ใน CTE แรกแถวทั้งหมดจะถูกเลือกจากนั้นแยกตามการดึงข้อมูล ฉันจะเดาได้ว่าการเลือกแถวทั้งหมดใน CTE แรกจะทำให้สิ่งต่าง ๆ ช้าลงอย่างมาก ไม่ว่าในกรณีใดขอบคุณสำหรับสิ่งนี้!
jbd

1
ในกรณีของฉันมันช้าลงกว่า COUNT (1) OVER () .. อาจเป็นเพราะฟังก์ชันในการเลือก
Tiju John

1
วิธีนี้เหมาะสำหรับฐานข้อมูลขนาดเล็กเมื่อแถวเป็นล้านต้องใช้เวลามากเกินไป
Kiya

1

จากคำตอบของ James Moberg :

นี่เป็นอีกทางเลือกหนึ่งในการใช้Row_Number()หากคุณไม่มี SQL Server 2012 และคุณไม่สามารถใช้ OFFSET ได้

DECLARE 
    @PageNumEnd INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, NAME
    FROM Tabla
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)

select * 
from
(
    SELECT
     ROW_NUMBER() OVER ( ORDER BY PolizaId DESC) AS 'NumeroRenglon', 
     MaxRows, 
     ID,
     Name
    FROM TempResult, TempCount

)resultados
WHERE   NumeroRenglon >= @PageNum
    AND NumeroRenglon <= @PageNumEnd
ORDER BY NumeroRenglon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.