LIMIT 10..20 ใน SQL Server


161

ฉันพยายามทำสิ่งที่ชอบ:

SELECT * FROM table LIMIT 10,20

หรือ

SELECT * FROM table LIMIT 10 OFFSET 10

แต่ใช้ SQL Server

ทางออกเดียวที่ฉันค้นพบดูเหมือนว่าเป็น overkill

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

ฉันก็ค้นพบ :

SELECT TOP 10 * FROM stuff; 

... แต่ไม่ใช่สิ่งที่ฉันต้องการเพราะฉันไม่สามารถระบุขีด จำกัด เริ่มต้นได้

มีวิธีอื่นอีกไหมที่ฉันจะทำอย่างนั้น?

นอกจากนี้อยากรู้อยากเห็นมีเหตุผลทำไม SQL Server ไม่สนับสนุนLIMITฟังก์ชั่นหรือสิ่งที่คล้ายกัน? ฉันไม่ต้องการที่จะหมายถึง แต่ดูเหมือนว่าสิ่งที่ DBMS ต้องการ ... ถ้าเป็นเช่นนั้นฉันขอโทษที่ไม่รู้เช่นนั้น! ฉันทำงานกับ MySQL และ SQL + เป็นเวลา 5 ปีที่ผ่านมาดังนั้น ...


1
การใช้ CTE สำหรับROW_NUMBER()และ จำกัด ด้วยTOPสำหรับความกว้างของช่วงและWHEREเงื่อนไขสำหรับขอบเขตของขอบเขตนั้นดีที่สุดที่ฉันสามารถทำได้ ฉันได้สังเกตเห็นยังผลการดำเนินงานที่ดีมากถ้าTOPข้อใช้ตัวอักษรแทนของตัวแปร
สำเร็จความใคร่ด้วย

ปัญหาเกี่ยวกับวิธีแก้ปัญหาใด ๆ ที่เกี่ยวข้องกับ ROW_NUMBER () คือถ้าคุณไม่ทราบล่วงหน้าว่าคุณมีคอลัมน์อะไรและคุณมีส่วนร่วมและตารางที่เข้าร่วมมีชื่อคอลัมน์เดียวกันคุณจะได้รับ "คอลัมน์ ระบุ 'xxx' หลายครั้ง " สิ่งนี้ไม่ใช่เรื่องแปลกอย่างที่คิดในตอนแรก ฉันใช้ Dapper และตารางของฉันทั้งหมดมีคอลัมน์ Id Dapper แยกและแม็พกับที่ดังนั้นฉันไม่ต้องการเปลี่ยนชื่อพวกเขา แต่ฉันไม่สามารถใช้นามแฝง SELECT * FROM ([ต้นฉบับแบบสอบถาม]) ฉันยังไม่พบวิธีแก้ปัญหา!
Steve Owen

เป็นไปได้ที่ซ้ำกันของวิธีการใช้ LIMIT กับ Microsoft SQL Server?
kenorb

คำตอบ:


104

LIMITคำสั่งไม่ได้เป็นส่วนหนึ่งของ SQL มาตรฐาน มันได้รับการสนับสนุนเป็นส่วนขยายของผู้ขายไปยัง SQL โดย MySQL, PostgreSQL และ SQLite

ฐานข้อมูลยี่ห้ออื่นอาจมีคุณสมบัติที่คล้ายกัน (เช่น TOPใน Microsoft SQL Server) แต่สิ่งเหล่านี้อาจไม่ทำงานเหมือนกันเสมอไป

เป็นการยากที่จะใช้TOPใน Microsoft SQL Server เพื่อเลียนแบบLIMITประโยค มีหลายกรณีที่ไม่สามารถใช้งานได้

วิธีแก้ปัญหาที่คุณพบโดยใช้ ROW_NUMBER()งานได้ใน Microsoft SQL Server 2005 และใหม่กว่า นี่เป็นทางออกที่ดีที่สุด (ตอนนี้) ซึ่งทำงานเป็นส่วนหนึ่งของการค้นหาเท่านั้น

อีกวิธีคือใช้TOPเพื่อดึงจำนวนแถว+ ออฟเซ็ตแรกจากนั้นใช้ API เพื่อหาค่าออฟเซ็ตแรกที่ผ่านมาแถว

ดูสิ่งนี้ด้วย:


135

สำหรับ SQL Server 2012 + คุณสามารถใช้

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 

10
SQl Server 2012 จำเป็นต้องระบุ ORDER BY เมื่อคุณใช้ OFFSET 5 ROET FETCH NEXT 5 ROWS เท่านั้นขณะที่ MySql และ SQLite ไม่ต้องการ ORDER BY เมื่อคุณใช้ LIMIT 5,5
Tomas Kubes

4
@ qub1n - MySQL ไม่รับประกันว่าคุณจะได้แถวไหนในกรณีนั้น
Martin Smith

3
คุณต้องใช้offsetหรือคุณสามารถปล่อยให้บรรทัดนั้น (สมมติว่าคุณไม่ต้องการออฟเซ็ต)?
Cullub


คุณตัวอย่างแบบสอบถามทำงานได้ดี แต่ถ้าฉันเปลี่ยนชื่อตารางและสั่งซื้อโดย col ดังต่อไปนี้ SELECT * จาก DimProduct สั่งซื้อโดย ProductKey OFFSET 5 ROWS FETCH NEXT 5 ROWS เท่านั้นมันให้ข้อผิดพลาดParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat

36

ตามที่คุณพบนี่เป็นวิธีเซิร์ฟเวอร์ sql ที่ต้องการ:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10

ทำไมaหลังจากเลือกด้านใน? ฉันคิดว่าคุณกำลังให้นามแฝงที่เลือกภายใน แต่แล้วคุณดูเหมือนจะไม่เคยใช้มัน ... คุณควรจะทำa.rowแทนที่จะเป็นแค่row?
Lucas

3
@ Lucas คุณจะต้องใส่นามแฝงหลังจาก( )ตารางที่ได้รับมา แต่มันจะปล่อยให้มันไปถ้าคุณลืมที่จะใช้มันเพื่ออ้างถึงคอลัมน์ ฉันแก้ไขมันแล้ว ...
KM

ขอบคุณฉันพบว่าวิธีที่ยาก (พยายามที่จะออกนามแฝงออก)
ลูคัส

1
โหวต +1: อย่างไรก็ตามคำตอบของ @MartinSmith ได้รับการโหวตมากขึ้นหลังจากเปรียบเทียบแผนการดำเนินการกับแนวทางนี้ฉันพบว่าโซลูชันนี้ทำงานได้เร็วขึ้น
รุนแรง

10

หากคุณใช้ SQL Server 2012+ ให้คะแนนสำหรับคำตอบของ Martin Smithและใช้ส่วนขยายOFFSETและFETCH NEXTเพื่อORDER BY ,

หากคุณโชคร้ายพอที่จะติดอยู่กับเวอร์ชั่นก่อนหน้านี้คุณสามารถทำสิ่งนี้

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

ฉันเชื่อว่าสามารถใช้งานได้เทียบเท่า

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

และวิธีที่มีประสิทธิภาพดีที่สุดที่ฉันรู้ว่าทำไว้ใน TSQL ก่อนหน้า MS SQL 2012


หากมีแถวจำนวนมากคุณอาจได้รับประสิทธิภาพที่ดีขึ้นโดยใช้ตาราง temp แทน CTE


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

7

น่าเสียดายที่ROW_NUMBER()สิ่งที่ดีที่สุดที่คุณสามารถทำได้ จริงๆแล้วมันถูกต้องมากขึ้นเนื่องจากผลลัพธ์ของประโยคlimitหรือtopประโยคไม่มีความหมายจริงๆโดยไม่เกี่ยวข้องกับคำสั่งเฉพาะบางอย่าง แต่มันก็ยังเจ็บปวดอยู่

ปรับปรุง: SQL Server 2012 จะเพิ่มlimitคุณลักษณะเหมือนผ่านOFFSET และเรียกคำหลัก นี่เป็นวิธีมาตรฐาน ansi ซึ่งตรงข้ามกับLIMITซึ่งเป็นส่วนขยาย MySql ที่ไม่ได้มาตรฐาน


@Joel: คุณช่วยอธิบายได้ไหมว่าทำไม ROW_NUMBER () ไม่สามารถนับจำนวนแถวที่พวกเขาออกมาจาก ORDER BY ฉันสงสัยอยู่เสมอว่าทำไม "OVER (เรียงตามชื่อ)" เป็นข้อบังคับ แต่ฉันเดาว่ามีเหตุผลที่ดี หรืออย่างน้อยก็มีเหตุผล
Tomalak

3
เพราะไม่มีสิ่งใดเป็นคำสั่งโดยไม่มีคำสั่งตามข้อ คุณจะได้รับลำดับใดก็ตามที่บันทึกพร้อมใช้งานกับเซิร์ฟเวอร์และนั่นอาจเปลี่ยนจากการร้องขอแบบสอบถามเป็นคำขอแบบสอบถาม
Joel Coehoorn

1
@marcgg: ฉันไม่เคยอ่านสิ่งบ่งชี้ใด ๆ ที่ Microsoft วางแผนที่จะใช้งาน LIMIT แม้ว่าพวกเขาจะมีแผนดังกล่าวผู้ขายที่มาปิดมักจะไม่ประกาศคุณสมบัติก่อน แน่นอนว่ามันจะเป็นคุณสมบัติที่มีประโยชน์ แต่เราไม่ทราบว่าจะต้องใช้งานได้มากเพียงใดโดยรับรหัสของพวกเขา
Bill Karwin

3
หากคุณไม่ต้องการทำซ้ำตัวเองในประโยค ORDER BY ให้ใช้นามแฝง ROW_NUMBER () แทนชุดคอลัมน์เดิม
Peter Radocchia

2
@ Tomalak: เท่าที่เกี่ยวข้องกับ SQL Server การเรียงลำดับที่ใช้ในการคำนวณ ROW_NUMBER () ไม่เกี่ยวข้องกับการเรียงลำดับของ resultset อย่างสมบูรณ์ นั่นเป็นเหตุผลที่คุณต้องระบุแยกต่างหาก
ลุค

6

แล้วเรื่องนี้ล่ะ

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

มันให้ 10 แถวสุดท้ายของ 20 แถวแรก ข้อเสียเปรียบอย่างหนึ่งคือคำสั่งซื้อนั้นกลับรายการ แต่อย่างน้อยก็ง่ายต่อการจดจำ


6
เกิดอะไรขึ้นถ้ามีเพียง 14 แถวในตาราง? คุณได้รับแถว 14 ลงไป 5 ซึ่งไม่เหมือนกับแถวที่ส่งคืนโดย LIMIT 10 OFFSET 10 (ควรเป็นแถวที่ 14 ถึง 11)
Bill Karwin

2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

ควรให้บันทึก 11-20 อาจไม่มีประสิทธิภาพเกินไปหากเพิ่มขึ้นเพื่อรับหน้าเพิ่มเติมและไม่แน่ใจว่าจะได้รับผลกระทบจากการสั่งซื้ออย่างไร อาจต้องระบุสิ่งนี้ในทั้งสองคำสั่ง WHERE


1

วิธีที่ดีคือการสร้างโพรซีเดอร์:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

เช่นเดียวกับขีด จำกัด 0,2 ///////////////// ดำเนินการเลขหน้า


1

สำหรับโซลูชันการบันทึกที่ทำงานในเอนจินฐานข้อมูลส่วนใหญ่ แต่อาจไม่ได้มีประสิทธิภาพมากที่สุด:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

บันทึกย่อแบบย่อ: หน้าสุดท้ายจะยังคงมีแถว ReturnCount ไม่ว่า SkipCount คืออะไร แต่นั่นอาจเป็นสิ่งที่ดีในหลายกรณี


1

ค่าเทียบเท่า 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

0
select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

จะพิมพ์แถวตั้งแต่ 10 ถึง 15


0

จนถึงขณะนี้รูปแบบนี้ใช้งานได้สำหรับฉัน (ไม่ใช่ประสิทธิภาพที่ดีที่สุด):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

หมายเหตุที่ด้านข้างการคำนวณข้อมูลแบบไดนามิกสามารถนำไปสู่ผลลัพธ์ที่แปลก / ไม่คาดคิด


0

จากเอกสารออนไลน์ MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ) นี่คือตัวอย่างของพวกเขาที่ฉันได้ทดสอบและทำงานเพื่อดึงแถวชุดที่เฉพาะเจาะจง ROW_NUMBER ต้องการ OVER แต่คุณสามารถสั่งซื้อได้ตามที่คุณต้องการ:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;

0

ใช้เซิร์ฟเวอร์ SQL ทั้งหมด:; ด้วย tbl เป็น (SELECT ROW_NUMBER () มากกว่า (เรียงตาม (เลือก 1)) เป็น RowIndex, * จากตาราง) เลือก 10 อันดับแรกจาก tbl โดยที่ RowIndex> = 10


-3
 SELECT * FROM users WHERE Id Between 15 and 25

มันจะพิมพ์จาก 15 ถึง 25 เหมือนขีด จำกัด ใน MYSQl


2
เกิดอะไรขึ้นถ้าผู้ใช้ลบบันทึกระหว่าง 15 และ 25?
GökçerGökdal
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.