วิธีเลือกแถวล่างสุด


100

ฉันสามารถทำ SELECT TOP (200) ... แต่ทำไมไม่ BOTTOM (200) ล่ะ?

เพื่อไม่ให้เข้าใจถึงปรัชญาที่ฉันหมายถึงคือฉันจะทำอย่างไรให้เทียบเท่ากับ TOP (200) แต่ในทางกลับกัน (จากด้านล่างเช่นที่คุณคาดหวังให้ BOTTOM ทำ ... )

คำตอบ:


89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC

2
ทำไมคุณถึงใช้ตารางที่ได้มา?
RichardOD

14
หากคุณต้องการส่งคืนแถวตามลำดับ A-> Z แต่เลือก 200 อันดับแรกใน Z-> A นี่เป็นวิธีหนึ่งที่ทำได้ คำตอบอื่น ๆ ที่แนะนำเพียงแค่เปลี่ยน ORDER BY จะไม่ส่งคืนผลลัพธ์เดียวกันกับที่อธิบายไว้ในคำถามเนื่องจากจะไม่เป็นระเบียบ (เว้นแต่คำสั่งจะไม่สำคัญซึ่ง OP ไม่ได้บอกไว้)
Tom H

3
@ ทอมเอชต้องคิดสักสองสามวินาทีว่าคุณหมายถึงอะไร (ฉันอยู่มา 14 ชั่วโมงแล้ว) ตอนแรกฉันไม่เห็นความแตกต่างระหว่างคำสั่งของคุณกับคำสั่ง แต่ตอนนี้ฉันทำได้แล้ว ดังนั้น +1
RichardOD

1
คำตอบนี้และคำตอบอื่น ๆ ใช้ได้ดีเมื่อคุณทำงานบนโต๊ะขนาดเล็ก ฉันไม่คิดว่ามันจะคุ้มค่าที่จะสั่งทั้งตารางทีละคอลัมน์เมื่อคุณสนใจแค่สองสามแถวล่าง
steadyfish

1
คำตอบที่ดี imho ที่ดีที่สุด ... ปัญหาที่แท้จริงคือฉันไม่สามารถทำสิ่งนี้จากสคริปต์ ASP ได้ดังนั้นฉันคิดว่าฉันต้องจัดลำดับ objRecordset ใหม่ด้วยตนเองหรือด้วยฟังก์ชันที่ ASP มีให้ ....
Andrea_86

98

มันไม่จำเป็น คุณสามารถใช้ORDER BYและเพียงแค่เปลี่ยนการจัดเรียงเพื่อDESCให้ได้เอฟเฟกต์เดียวกัน


3
Google พูดในสิ่งเดียวกันและตอนนี้คุณ 9 คนเห็นด้วยดีพอสำหรับฉันขอบคุณ: D
CloudMeta

9
การใช้ DESC จะส่งคืน N แถวสุดท้าย แต่แถวที่ส่งคืนจะอยู่ในลำดับย้อนกลับจาก N แถวแรกด้วย
RickNZ

11
จะเกิดอะไรขึ้นถ้าไม่มีดัชนีบนโต๊ะของคุณให้ ORDER BY?
ป้องกัน

8
@ จัสติน: ลองนึกภาพว่ามีคอลัมน์เดียวที่มีค่า varchar ORDER BY จะเรียงตามตัวอักษรซึ่ง (อาจ) ไม่ใช่สิ่งที่เราต้องการ
Protector one

3
Tom H. เป็น anwser ที่ถูกต้องมิฉะนั้นแถวของคุณจะอยู่ในลำดับย้อนกลับ
Pierre-Olivier Goulet

39

ขออภัยฉันคิดว่าฉันไม่เห็นคำตอบที่ถูกต้องในความคิดของฉัน

TOPฟังก์ชั่น x แสดงระเบียนในการสั่งซื้อไม่ได้กำหนด จากนิยามดังกล่าวBOTTOMทำให้ไม่สามารถกำหนดฟังก์ชันได้

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

  • สิ่งนี้ใช้ได้เฉพาะเมื่อมีคอลัมน์รหัสที่สร้างขึ้นโดยอัตโนมัติ
  • มีผลกระทบต่อประสิทธิภาพอย่างมากหากคุณเปรียบเทียบกับTOPฟังก์ชัน

คำตอบที่ถูกต้องควรเป็นไม่มีและไม่สามารถเทียบเท่ากับTOPการรับแถวล่างสุด


3
จริงดูเหมือนจะไม่มีทางเทียบเท่ามีเพียงวิธีแก้ปัญหาเท่านั้น
โผล่

4
TOP ไม่มีส่วนเกี่ยวข้องกับลำดับที่รายการถูกเพิ่มลงในตาราง แต่หมายถึง "ให้ระเบียน X แรกที่ตรงกับข้อความค้นหาของฉัน"
ลุค

4
ใช่มันทำให้คุณมีระเบียนแรกที่เพิ่มลงในตารางที่ตรงกับข้อความค้นหาของคุณ
Martijn Burger

4
ฉันเห็นด้วยกับลุค ตารางฐานข้อมูลไม่เรียงลำดับตามความหมาย เราไม่ควรพึ่งพาคำสั่งที่จัดทำโดย RDBMS เมื่อคำสั่ง select ไม่มีคำสั่ง ORDER BY อ่านที่นี่ใน wiki อย่างไรก็ตามระบบฐานข้อมูลไม่รับประกันการเรียงลำดับของแถวใด ๆ เว้นแต่จะระบุคำสั่ง ORDER BY ในคำสั่ง SELECT ที่ค้นหาตาราง
Zohar Peled

2
ฉันเห็นด้วยกับคำตอบนี้ แต่ในทางปฏิบัติคำตอบของ Tomช่วยแก้ปัญหาสำหรับการใช้งานจริงทั้งหมด
Antonio

18

ตามเหตุผล

BOTTOM (x) is all the records except TOP (n - x), where n is the count; x <= n

เช่นเลือก 1000 อันดับสุดท้ายจากพนักงาน:

ใน T-SQL

DECLARE 
@bottom int,
@count int

SET @bottom = 1000 
SET @count = (select COUNT(*) from Employee)

select * from Employee emp where emp.EmployeeID not in 
(
SELECT TOP (@count-@bottom) Employee.EmployeeID FROM Employee
)

1
สวัสดีปี 2014 โดยไม่ต้องใช้ "ORDER BY" ผลลัพธ์ของคุณจะเป็นแบบสุ่ม
bummi

5
คุณพูดถูก แต่นั่นคือสิ่งที่ทำให้คำตอบนี้ถูกต้อง เลือก TOP ตัวเองเป็น "สุ่ม" ในทางทฤษฎีและนี่คือการใช้งานที่ถูกต้องสำหรับ Select BOTTOM ในตาราง 5,000 ระเบียน 1,000 อันดับล่างคือทุกอย่างยกเว้น 4000 อันดับสูงสุด
tzachs

9

ดูเหมือนว่าคำตอบใด ๆ ที่ใช้คำสั่ง ORDER BY ในการแก้ปัญหานั้นขาดประเด็นหรือไม่เข้าใจว่า TOP ส่งคืนอะไรให้คุณ

TOP ส่งคืนชุดผลลัพธ์แบบสอบถามที่ไม่เรียงลำดับซึ่ง จำกัด ระเบียนที่ตั้งไว้ที่ระเบียน N แรกที่ส่งคืน (จากมุมมองของ Oracle จะคล้ายกับการเพิ่มโดยที่ ROWNUM <(N + 1)

โซลูชันใด ๆ ที่ใช้คำสั่งอาจส่งคืนแถวที่ส่งคืนโดยส่วนคำสั่ง TOP (เนื่องจากชุดข้อมูลนั้นไม่ได้เรียงลำดับตั้งแต่แรก) ขึ้นอยู่กับเกณฑ์ที่ใช้ในการเรียงลำดับโดย

ประโยชน์ของ TOP คือเมื่อชุดข้อมูลถึงขนาดที่กำหนด N จะหยุดดึงข้อมูลแถว คุณจะรู้สึกได้ว่าข้อมูลมีลักษณะอย่างไรโดยไม่ต้องดึงข้อมูลทั้งหมด

ในการใช้งาน BOTTOM อย่างถูกต้องจะต้องดึงชุดข้อมูลทั้งหมดที่ไม่ได้เรียงลำดับจากนั้นจึง จำกัด ชุดข้อมูลไว้ที่ระเบียน N สุดท้าย สิ่งนี้จะไม่ได้ผลเป็นพิเศษหากคุณกำลังจัดการกับโต๊ะขนาดใหญ่ ไม่จำเป็นต้องให้สิ่งที่คุณคิดว่าคุณกำลังขอ ส่วนท้ายของชุดข้อมูลอาจไม่จำเป็นต้องเป็น "แถวสุดท้ายที่แทรก" (และอาจไม่เหมาะสำหรับแอปพลิเคชันที่เน้น DML ส่วนใหญ่)

ในทำนองเดียวกันโซลูชันที่ใช้ ORDER BY นั้นน่าเสียดายที่อาจเป็นหายนะเมื่อต้องจัดการกับชุดข้อมูลขนาดใหญ่ ถ้าฉันมีบอกว่า 10 พันล้านเรกคอร์ดและต้องการ 10 คนสุดท้ายมันค่อนข้างโง่ที่จะสั่งซื้อ 10 พันล้านเรกคอร์ดและเลือก 10 รายการสุดท้าย

ปัญหาตรงนี้คือ BOTTOM ไม่ได้มีความหมายอย่างที่เราคิดเมื่อเปรียบเทียบกับ TOP

เมื่อบันทึกถูกแทรกลบแทรกลบซ้ำแล้วซ้ำอีกช่องว่างบางส่วนจะปรากฏในที่จัดเก็บข้อมูลและในภายหลังแถวจะถูก slotted ถ้าเป็นไปได้ แต่สิ่งที่เรามักจะเห็นเมื่อเราเลือก TOP ดูเหมือนจะเป็นการจัดเรียงข้อมูลเนื่องจากอาจถูกแทรกในช่วงต้นของการมีอยู่ของตาราง หากตารางไม่พบการลบหลายครั้งอาจดูเหมือนว่ามีการสั่งซื้อ (เช่นวันที่สร้างอาจย้อนเวลากลับไปได้ไกลพอ ๆ กับการสร้างตารางเอง) แต่ความจริงก็คือถ้านี่เป็นตารางที่มีการลบมากแถว TOP N อาจไม่เป็นเช่นนั้นเลย

ดังนั้น - สิ่งที่สำคัญที่สุดที่นี่ (ตั้งใจให้เล่นสำนวน) คือคนที่ขอบันทึก BOTTOM N ไม่รู้จริง ๆ ว่าพวกเขากำลังขออะไร หรืออย่างน้อยที่สุดสิ่งที่พวกเขาขอและความหมายของ BOTTOM นั้นไม่ใช่สิ่งเดียวกัน

ดังนั้น - การแก้ปัญหาอาจตอบสนองความต้องการทางธุรกิจที่แท้จริงของผู้ร้องขอ ... แต่ไม่ตรงตามเกณฑ์สำหรับการเป็น BOTTOM


1
คำอธิบายที่ยอดเยี่ยม โหวตให้ความกระจ่างมากขึ้นในหัวข้อนี้
rohrl77

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

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

3

คำตอบที่ "จัสตินเอเทียร์" ยอมรับในปัจจุบันไม่ใช่คำตอบที่ถูกต้องตามที่ "ผู้พิทักษ์วัน" ชี้ไว้

เท่าที่ฉันเห็น ณ ตอนนี้ไม่มีคำตอบหรือความคิดเห็นอื่นใดที่เทียบเท่ากับ BOTTOM (x) ที่ผู้เขียนถาม

ขั้นแรกให้พิจารณาสถานการณ์ที่จำเป็นต้องใช้ฟังก์ชันนี้:

SELECT * FROM Split('apple,orange,banana,apple,lime',',')

สิ่งนี้ส่งคืนตารางหนึ่งคอลัมน์และห้าระเบียน:

  • แอปเปิ้ล
  • ส้ม
  • กล้วย
  • แอปเปิ้ล
  • มะนาว

ดังที่คุณเห็น: เราไม่มีคอลัมน์ ID; เราไม่สามารถสั่งซื้อตามคอลัมน์ที่ส่งคืนได้ และเราไม่สามารถเลือกสองระเบียนล่างโดยใช้ SQL มาตรฐานเหมือนกับที่เราสามารถทำได้สำหรับสองระเบียนแรก

นี่คือความพยายามของฉันที่จะหาทางแก้ไข:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
SELECT TOP 2 * FROM #mytemptable ORDER BY tempID DESC
DROP TABLE #mytemptable

และนี่คือโซลูชันที่สมบูรณ์ยิ่งขึ้น:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
DELETE FROM #mytemptable WHERE tempID <= ((SELECT COUNT(*) FROM #mytemptable) - 2)
ALTER TABLE #mytemptable DROP COLUMN tempID
SELECT * FROM #mytemptable
DROP TABLE #mytemptable

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



1

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

DECLARE @NumberOfRows int;
SET @NumberOfRows = (SELECT COUNT(*) FROM TheTable);

SELECT col1, col2,...
FROM (
    SELECT col1, col2,..., ROW_NUMBER() OVER (ORDER BY col1) AS intRow
    FROM TheTable
) AS T
WHERE intRow > @NumberOfRows - 20;

2
1) หากเปลี่ยนทิศทางของ ORDER BY clause ของคุณ "ไม่ได้ใช้ประโยชน์จากดัชนี" ให้ได้ RDBMS ที่เหมาะสม! RDBMS ไม่ควรสนใจว่ามันจะนำทางดัชนีไปข้างหน้าหรือข้างหลัง 2) คุณกังวลเกี่ยวกับการใช้ดัชนี แต่โซลูชันของคุณแนบลำดับกับทุกแถวในตารางนั่นเป็นวิธีหนึ่งในการรับประกันว่าดัชนีที่เหมาะสมจะไม่ถูกใช้
ท้อใจ

1

คำตอบ "Tom H" ด้านบนถูกต้องและเหมาะกับฉันในการรับแถว 5 ล่างสุด

SELECT [KeyCol1], [KeyCol2], [Col3]
FROM
(SELECT TOP 5 [KeyCol1],
       [KeyCol2],
       [Col3]
  FROM [dbo].[table_name]
  ORDER BY [KeyCol1],[KeyCol2] DESC) SOME_ALAIS
  ORDER BY [KeyCol1],[KeyCol2] ASC

ขอบคุณ.


0

ลองดู

declare @floor int --this is the offset from the bottom, the number of results to exclude
declare @resultLimit int --the number of results actually retrieved for use
declare @total int --just adds them up, the total number of results fetched initially

--following is for gathering top 60 results total, then getting rid of top 50. We only keep the last 10
set @floor = 50 
set @resultLimit = 10
set @total = @floor + @resultLimit

declare @tmp0 table(
    --table body
)

declare @tmp1 table(
    --table body
)

--this line will drop the wanted results from whatever table we're selecting from
insert into @tmp0
select Top @total --what to select (the where, from, etc)

--using floor, insert the part we don't want into the second tmp table
insert into @tmp1
select top @floor * from @tmp0

--using select except, exclude top x results from the query
select * from @tmp0
except 
select * from @tmp1

อะไรทำให้รหัสของคุณสำหรับ OP? โปรดเพิ่มคำอธิบายเพิ่มเติมเล็กน้อยเกี่ยวกับวิธีที่คุณพยายามแก้ไขปัญหา
techspider

ฉันทำการแก้ไข ฉันหวังว่ามันจะอธิบายได้ดีขึ้นเล็กน้อยและแสดงความคิดเห็นเพิ่มเติม แนวคิดหลักคือเลือก top x จากตารางจากนั้นเลือก top x - num ที่ต้องการจากนั้นใช้คำสั่ง except เพื่อยกเว้นผลลัพธ์ที่ไม่ต้องการ
HumbleWebDev

0

ฉันได้หาวิธีแก้ปัญหานี้ที่ไม่ต้องการให้คุณทราบจำนวนแถวที่ส่งคืน

ตัวอย่างเช่นหากคุณต้องการรับตำแหน่งทั้งหมดที่บันทึกไว้ในตารางยกเว้น 1 (หรือ 2 หรือ 5 หรือ 34) ล่าสุด

SELECT * 
FROM
    (SELECT ROW_NUMBER() OVER (ORDER BY CreatedDate) AS Row, * 
    FROM Locations
    WHERE UserId = 12345) AS SubQuery
WHERE Row > 1 -- or 2, or 5, or 34

0

การค้นหาคำค้นหาย่อยแบบง่ายที่เรียงลำดับจากมากไปหาน้อยตามด้วยการเรียงลำดับจากคอลัมน์เดียวกันจากน้อยไปหามากจะเป็นเคล็ดลับ

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]


0

ขั้นแรกให้สร้างดัชนีในแบบสอบถามย่อยตามลำดับเดิมของตารางโดยใช้:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL) ) AS RowIndex

จากนั้นเรียงลำดับตารางจากRowIndexคอลัมน์ที่คุณสร้างในแบบสอบถามหลัก:

ORDER BY RowIndex DESC

และสุดท้ายใช้TOPกับจำนวนแถวที่คุณต้องการ:

    SELECT TOP 1 * --(or 2, or 5, or 34)
    FROM   (SELECT ROW_NUMBER() OVER (ORDER BY  (SELECT NULL) ) AS RowIndex, * 
            FROM MyTable) AS SubQuery
    ORDER BY RowIndex DESC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.