เทียบเท่า LIMIT และ OFFSET สำหรับ SQL Server หรือไม่


172

ใน PostgreSQL มีLimitและOffsetคำหลักซึ่งจะช่วยให้การแบ่งหน้าง่ายของชุดผลลัพธ์

ไวยากรณ์เทียบเท่าสำหรับ SQL Server คืออะไร


สำหรับเซิร์ฟเวอร์ sql 2012 คุณลักษณะนี้มีการใช้งานในวิธีที่ง่าย ดูคำตอบของฉัน
Somnath Muluk

ขอบคุณที่ถามคำถามนี้เรากำลังถูกบังคับให้เปลี่ยนจาก MySQL เป็น
MsSQL

คุณสามารถใช้อ็อฟเซ็ตและดึงคำสั่งถัดไปในเซิร์ฟเวอร์ SQL โดยเรียงตามลำดับ ลองใช้youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

คำตอบ:


139

เทียบเท่าLIMITคือSET ROWCOUNTแต่ถ้าคุณต้องการเลขหน้าทั่วไปมันจะดีกว่าถ้าเขียนเคียวรีดังนี้

;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

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

หมายเหตุ:@Offsetพารามิเตอร์ควรใช้การจัดทำดัชนีหนึ่งที่ใช้สำหรับการนี้มากกว่าปกติการจัดทำดัชนี zero-based


22
เก่าแล้วตอนนี้ Sql Server 2012 และใหม่กว่ารองรับ OFFSET / FETCH
Joel Coehoorn

31
@JoelCoehoorn ไม่แก่ ฉันเพิ่งได้รับมอบหมายให้โครงการโดยใช้ SLQ Server 2008 ที่มีการใช้ mysql เท่านั้นในอดีต ...
Cthulhu

มันค่อนข้างดี แต่จำเป็นต้องได้รับการดูแลเล็กน้อยWHERE 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
พอล

2
@Aaraught ถ้าฉันTableมีประวัติ 200k มันจะดึงทั้งหมดมาก่อน แบบสอบถามนี้มีประสิทธิภาพหรือไม่
Jigar

231

ฟีเจอร์นี้ทำให้ง่ายใน SQL Server 2012 ซึ่งทำงานจาก SQL Server 2012 เป็นต้นไป

จำกัด ด้วย offset เพื่อเลือก 11 ถึง 20 แถวใน SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: จำนวนแถวที่ข้าม
  • NEXT: จำนวนแถวถัดไปที่ต้องการ

การอ้างอิง: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017


4
มีการSQL_CALC_FOUND_ROWSใช้งานเท่ากันเมื่อใช้สิ่งนี้?
Petah

1
@Petah @@ Rowcount จะให้สิ่งที่ฉันคิด
Rob Sedgwick

GOTCHA: คุณไม่สามารถใช้สิ่งนี้จากภายใน CTE มันจะต้องมีการใช้งานในแบบสอบถามหลัก ฉันต้องการ จำกัด จำนวนแถวที่ส่งคืน (การแบ่งหน้า) และจากนั้นทำการคำนวณราคาแพงให้กับแถวที่ 10 หรือมากกว่านั้นกลับมาแทนที่จะกำหนดแถวทำการคำนวณราคาแพงแล้วข้าม / ทำตามที่ฉันต้องการ @ คำตอบของ Aaronaught จะใช้ได้ผลกับผู้ที่ต้องการ จำกัด แถวภายใน CTE
Derreck Dean

@Somnath Muluk การชดเชยและการดึงข้อมูลใช้เวลานานสำหรับปริมาณข้อมูลที่สูงขึ้นด้วยการชดเชย 1000000 ฉันจะจัดการกับสิ่งนี้ได้อย่างไร
Saroj Shrestha

1
@SarojShrestha: นี่ไม่ใช่ปัญหา Offset และ Fetch คุณควรกลับไปที่สถาปัตยกรรมของตารางของคุณทันที พิจารณาการแบ่งพาร์ติชันของตาราง, แถวข้อมูลของคุณ, และชนิดของคอลัมน์และขนาดตารางรวม, พิจารณาการจัดเก็บแถวบางแถวถ้าไม่จำเป็นเป็นประจำ, ตรวจสอบรายละเอียดเซิร์ฟเวอร์ของคุณ
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}

หมายเหตุ: วิธีนี้จะใช้ได้เฉพาะใน SQL Server 2005 หรือใหม่กว่าROW_NUMBER()เท่านั้น


ฉันใช้แบบสอบถามนี้มาระยะหนึ่งแล้วและใช้งานได้ดีมากขอบคุณมาก ฉันแค่สงสัยว่า 'xx' หมายถึงอะไร?
Urbley

แบบสอบถามย่อยต้องมีชื่อ เพราะฉันไม่ได้ใช้มันแค่ใส่ xx นั่น
jorgeu

2
xx เป็นเพียงนามแฝงของตาราง อาจชัดเจนกว่านี้ถ้าคุณพูดAS xx
Concrete Gannet

มีใครรู้วิธีการออกจากการเข้าร่วมในการค้นหานี้ไหม
Drenyl

12

คุณสามารถใช้ ROW_NUMBER ใน Common Table Expression เพื่อทำสิ่งนี้

;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

สำหรับฉันการใช้ OFFSET และ FETCH ร่วมกันนั้นช้าดังนั้นฉันจึงใช้การรวมกันของ TOP และ OFFSET เช่นนี้ (ซึ่งเร็วกว่า):

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

หมายเหตุ:หากคุณใช้ TOP และ OFFSET ร่วมกันในแบบสอบถามเดียวกันเช่น:

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

จากนั้นคุณจะได้รับข้อผิดพลาดดังนั้นสำหรับการใช้ TOP และ OFFSET คุณต้องแยกมันออกด้วยแบบสอบถามย่อย

และถ้าคุณต้องการใช้ SELECT DISTINCT เคียวรีจะเป็นดังนี้:

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

หมายเหตุ:การใช้ SELECT ROW_NUMBER กับ DISTINCT ไม่ได้ผลสำหรับฉัน


1
ฉันได้รับ "A TOP ไม่สามารถใช้ในแบบสอบถามหรือแบบสอบถามย่อยเดียวกันกับ OFFSET"
MichaelRushton

คุณถูกต้อง @MichaelRushton ไม่สามารถใช้ในแบบสอบถามเดียวกันหรือในแบบสอบถามย่อยเดียวกันจากนั้นคุณต้องใช้แบบสอบถามย่อยเพื่อแยก ดังนั้นถ้าคุณมี SQL เช่นคุณต้องเปลี่ยนมันเหมือนSELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1ฉันจะแก้ไขคำตอบของฉัน ขอบคุณและขอโทษด้วยภาษาอังกฤษของฉัน
sebasdev

2

ตัวอย่างอื่น:

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
ฉันลบคำพูดแสดงความเกลียดชังต่อต้านไมโครซอฟท์ของคุณ อย่าพูดถึงสงครามศักดิ์สิทธิ์ที่นี่ เพียงตอบและถามคำถามด้วยวิธีที่ไม่ใช่อัตวิสัย
Earlz

2

นอกจากนี้ที่นี่มีคนบอกเกี่ยวกับคุณลักษณะนี้ใน SQL 2011 มันน่าเศร้าที่พวกเขาเลือกคำหลักที่แตกต่างกันเล็ก ๆ น้อย ๆ "OFFSET / เรียก" แต่ไม่ได้มาตรฐาน ok แล้วมัน


2

การเพิ่มความแตกต่างเล็กน้อยในการแก้ปัญหาของ Aaronaught ฉันมักจะ parametrize หมายเลขหน้า (@PageNum) และขนาดหน้า (@PageSize) วิธีนี้แต่ละเหตุการณ์การคลิกหน้าเพียงแค่ส่งหมายเลขหน้าที่ร้องขอพร้อมกับขนาดหน้าที่กำหนดค่าได้:

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

สิ่งที่ฉันสามารถทำได้ใกล้เคียงที่สุดคือ

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

ซึ่งฉันเดาว่าคล้ายกับ 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

เนื่องจากยังไม่มีใครให้รหัสนี้:

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

จุดสำคัญ:

  • สั่งซื้อโดยจะต้องเหมือนกัน
  • @limit สามารถแทนที่ด้วยจำนวนผลลัพธ์ที่จะเรียกคืนได้
  • @offset คือจำนวนผลลัพธ์ที่จะข้าม
  • โปรดเปรียบเทียบประสิทธิภาพกับโซลูชันก่อนหน้าเนื่องจากอาจมีประสิทธิภาพมากกว่า
  • โซลูชันนี้ซ้ำwhereและส่วนorder byคำสั่งและจะให้ผลลัพธ์ที่ไม่ถูกต้องหากไม่มีการซิงค์
  • ในทางกลับกันorder byหากมีสิ่งที่ต้องการอย่างชัดเจน

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

โดยเฉพาะสำหรับ SQL-SERVER คุณสามารถทำสิ่งนั้นได้หลายวิธีตัวอย่างที่แท้จริงเรานำตารางลูกค้ามาที่นี่

ตัวอย่างที่ 1: ด้วย "SET ROWCOUNT"

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

หากต้องการส่งคืนแถวทั้งหมดให้ตั้ง ROWCOUNT เป็น 0

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

ตัวอย่างที่ 2: ด้วย "ROW_NUMBER และ OVER"

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

ตัวอย่างที่ 3: ด้วย "OFFSET และ FETCH" แต่ด้วย "ORDER BY" นี้เป็นสิ่งจำเป็น

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

หวังว่านี่จะช่วยคุณได้



-1

ตั้งแต่ฉันทดสอบมากกว่านี้สคริปต์นี้มีประโยชน์มากกว่า 1 ล้านบันทึกในแต่ละหน้า 100 บันทึกด้วยการให้เลขหน้าเร็วขึ้นพีซีของฉันใช้สคริปต์นี้ 0 วินาทีในขณะที่เปรียบเทียบกับ mysql มีข้อ จำกัด ของตัวเองและชดเชยประมาณ 4.5 วินาทีเพื่อให้ได้ผลลัพธ์

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

ROW_NUMBER () มากกว่า (เรียงตาม (เลือกเป็นศูนย์))

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}

อธิบาย:

  • {LIMIT}: จำนวนระเบียนสำหรับแต่ละหน้า
  • {OFFSET}: จำนวนของการข้ามบันทึก

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