ฉันจะทำอันดับ 1 ใน Oracle ได้อย่างไร


251

ฉันจะทำสิ่งต่อไปนี้ได้อย่างไร?

select top 1 Fname from MyTbl

ในOracle 11g ?




3
คุณสามารถบอกให้เราทราบการสั่งซื้อตามที่คุณต้องการ 'top 1'?
Andrew Wolfe

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

1
เนื้อหาที่มีประโยชน์มากในหัวข้อนี้use-the-index-luke.com/sql/partial-results/top-n-queries
Ilia Maskov

คำตอบ:


257

หากคุณต้องการเพียงแถวแรกที่เลือกคุณสามารถ:

select fname from MyTbl where rownum = 1

คุณยังสามารถใช้ฟังก์ชันการวิเคราะห์เพื่อจัดลำดับและรับค่า x สูงสุด:

select max(fname) over (rank() order by some_factor) from MyTbl

54
นี่เป็นสิ่งที่ดีถ้าคุณต้องการเพียง 1 แถวและไม่สนใจว่า หากคุณต้องการแถวที่เฉพาะเจาะจงเช่นบันทึกล่าสุดคุณจะต้องเรียงลำดับในการเลือกย่อยเช่นคำตอบของ Vash Oracle กำหนด rownums ก่อนการเรียงลำดับ
Scott Bailey

4
@Scott yup ถูกต้อง. และแพทริกจุดที่ดีฉันคิดว่าไวยากรณ์ไม่ถูกต้อง มันควรจะเป็นรักษา (dense_rank () ล่าสุด ... )
mcpeterson

2
ความแตกต่างระหว่างตัวอย่างที่หนึ่งและที่สองคือการที่คนแรกเลือกแถว (แถวใด ๆ โดยไม่มีคำสั่ง) ตัวอย่างที่สองได้รับค่าของแถวแรกโดยไม่ต้องทำแบบสอบถามภายในเพื่อ (ตามตัวอย่างด้านล่าง)
JulesLt

3
ไวยากรณ์ไม่ถูกต้องใน: select max (fname) over (rank () order by some_factor) จาก MyTbl
Stéphane Gerber

1
@ jclozano "ไม่ได้เป็นที่รู้จักกันดีในการทำงานของ Oracle" - ด้วยความเคารพฉันขอแตกต่างกันไป เรื่องนี้เป็นเพราะ "ฟังก์ชั่นที่ไม่เป็นที่รู้จักกันดี" มีแนวโน้มที่จะบ่งบอกถึงความสับสนและด้วยเหตุนี้จึงแนะนำให้เราหลีกเลี่ยง สิ่งนี้ไม่ชัดเจนและไม่ควรหลีกเลี่ยงการใช้งาน
Sepster

166
SELECT *
  FROM (SELECT * FROM MyTbl ORDER BY Fname )
 WHERE ROWNUM = 1;

8
คำตอบนี้ได้อย่างถูกต้องรับแถวบนสุด (สั่งผลลัพธ์ก่อนการ จำกัด ROWNUM)
JulesLt

คำตอบนี้ไม่ใช่การแปลที่แน่นอน - แบบสอบถามต้นฉบับไม่มี ORDER BY และไม่ส่งคืนคอลัมน์ทั้งหมดในตาราง
OMG Ponies

ฉันยืนแก้ไขแล้ว (ดูด้านล่าง) จะสลับคะแนนเมื่อหมดเวลา
JulesLt

7
@OMGPonies ใช่ แต่อาจเป็นสิ่งที่คนส่วนใหญ่ต้องการให้ผู้ที่มาที่หน้านี้ผ่านปัญหาของพวกเขาด้วย
Google

4
นี้แน่นอนต้องเป็นคำตอบที่ชนะในหัวข้อนี้ ฉันอาจเพิ่มบันทึกย่อที่top Xหนึ่งสามารถเปลี่ยนเป็นWHERE ROWNUM <= X
SomethingSomething

31

ด้วยOracle 12c (มิถุนายน 2013) คุณสามารถใช้งานได้ดังต่อไปนี้

SELECT * FROM   MYTABLE
--ORDER BY COLUMNNAME -OPTIONAL          
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY

6
คำสั่งที่น่าสนใจผมใช้ 12c ที่นี่และOFFSET 0 ROWSเห็นได้ชัดว่าไม่จำเป็นที่คุณสามารถใช้FETCH NEXT 1 ROWS ONLYหรือแม้กระทั่งการสั่งซื้อโดยมีความสำคัญหรือมันจะเป็นเพียงแค่เทียบเท่ากับการใช้FETCH FIRST ROW ONLY WHERE rownum = 1ฉันได้ลองใช้คำสั่งภายนอกของเราแล้วมันใช้งานได้เหมือนฟังก์ชั่น TOP ของ Ms-SQL ที่นั่น
Rafael Merlin

คุณถูก @RafaelMerlin หลังจากโพสต์ของคุณฉันได้รับการยอมรับว่า OFFSET 0 ROWS ไม่จำเป็น มันจะมีประโยชน์เมื่อทำการดึงข้อมูลระหว่าง top X และ top Y.
MSK

1
ตัวอย่างเพิ่มเติม: oracle-base.com/articles/12c/…
FixFaier

TIESดังนั้นไกลดังนั้นดีกับจุดเชื่อมต่อที่หายไปที่สำคัญของ อ้างถึงนี้สำหรับกรณีเมื่อความสัมพันธ์ที่เกิดขึ้นสำหรับรุ่น12c +และ12c -
Barbaros Özhan

10

คุณสามารถใช้ROW_NUMBER()กับประโยคหนึ่งในแบบสอบถามย่อยและใช้คอลัมน์นี้แทนORDER BY TOP Nสิ่งนี้สามารถอธิบายได้ทีละขั้นตอน

ดูตารางด้านล่างซึ่งมีสองคอลัมน์และNAMEDT_CREATED

ป้อนคำอธิบายรูปภาพที่นี่

หากคุณจำเป็นต้องใช้เพียงสองวันแรกโดยไม่คำนึงถึงNAMEคุณสามารถใช้แบบสอบถามด้านล่าง ตรรกะได้ถูกเขียนขึ้นภายในแบบสอบถาม

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
    -- Generates numbers in a column in sequence in the order of date
    SELECT ROW_NUMBER() OVER (ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

ผลลัพธ์

ป้อนคำอธิบายรูปภาพที่นี่

ในบางสถานการณ์เราต้องเลือกผลการค้นหาที่เกี่ยวข้องกับแต่ละTOP N NAMEในกรณีเช่นนี้เราสามารถใช้PARTITION BYกับORDER BYclause ในแบบสอบถามย่อย อ้างอิงแบบสอบถามด้านล่าง

-- The number of records can be specified in WHERE clause
SELECT RNO,NAME,DT_CREATED
FROM
(
  --Generates numbers in a column in sequence in the order of date for each NAME
    SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY DT_CREATED) AS RNO,
    NAME,DT_CREATED
    FROM DEMOTOP
)TAB
WHERE RNO<3;

ผลลัพธ์

ป้อนคำอธิบายรูปภาพที่นี่


1
การใช้ ROW_NUMBER () ... เป็นวิธีการแก้ปัญหาที่ถูกต้องมากกว่าในหัวข้อตอบ ปัญหาหนึ่งของการแก้ไขนี้ (และตัวแปรสูงสุด (ฟิลด์) ด้วย) ที่คุณไม่สามารถทำสิ่งต่าง ๆ เช่น "select ... (เลือก ROW_NUMBER () ... สำหรับการอัปเดต";
Alexo Po

และบางครั้งก็เป็นสิ่งสำคัญมากใน PL / SQL (ขออภัยไม่สามารถแก้ไขความคิดเห็นก่อนหน้านี้ภายใน 5 นาที)
Alexo Po

ในกรณีเช่นนี้เราสามารถใช้ CTE เช่นเดียวกับในส่วนนอก ขวา? @Alexo Po.
Sarath Avanavu

ฉันคิดว่าฉันไม่เข้าใจคุณ สำหรับส่วนคำสั่งการปรับปรุงสามารถใช้เมื่อ Oracle เป็น "ง่าย" ที่สงวนไว้โดย Oracle ดังนั้นการจัดกลุ่ม (และการจัดกลุ่มเนื่องจากการใช้คำสั่งวิเคราะห์) ซ่อน ROWID จริงและแถวไม่สามารถล็อคได้ และประการที่สอง CTE ( with (select ... ) as ส่วนคำสั่ง) จะไม่เปลี่ยนแปลงอะไรกับปัญหานี้ CTE มีจุดมุ่งหมายในการอ่านและสนับสนุนการสืบค้น ขวา? @Sarath Avanavu
Alexo Po

ทราบด้วยตัวเอง ปัญหาของ ROWID นั้นเกิดขึ้นจริงโดยเฉพาะเนื่องจากเงื่อนไขที่ RNO <3ในกรณีนี้ค่าของ RNO ไม่ได้เชื่อมต่อกับ ROWID ดังนั้น Oracle จึงไม่สามารถล็อคแถวได้
Alexo Po

9

คุณสามารถทำสิ่งที่ชอบ

    SELECT *
      FROM (SELECT Fname FROM MyTbl ORDER BY Fname )
 WHERE rownum = 1;

คุณสามารถใช้ฟังก์ชันวิเคราะห์RANKและ / หรือDENSE_RANKได้ แต่ROWNUMน่าจะง่ายที่สุด


1
คุณช่วยกรุณายกตัวอย่างของอันดับ ฯลฯ ได้
ไหม


5

ใช้:

SELECT x.*
  FROM (SELECT fname 
          FROM MyTbl) x
 WHERE ROWNUM = 1

ถ้าใช้ Oracle9i + คุณสามารถดูโดยใช้ฟังก์ชั่นการวิเคราะห์เช่น ROW_NUMBER () แต่พวกเขาจะไม่ดำเนินการเช่นเดียวกับ ROWNUM


1
คำตอบที่ดี แต่มีตัวพิมพ์เล็ก ๆ คุณบอกว่า Oracle9i + ไม่ควรเป็น 8i download-west.oracle.com/docs/cd/A87860_01/doc/server.817/…
Ian Carpenter

@carpenteri: จริงการวิเคราะห์มีให้บริการใน 8i - จำรายละเอียดไม่ได้ แต่การวิเคราะห์นั้นไม่เปิดเผยต่อสาธารณะจนถึง 9i
OMG Ponies

ความคิดเห็นเล็ก ๆ - คำตอบของ Vash ด้านล่างรวมถึง ORDER BY ในแบบสอบถามภายในซึ่งมีความสำคัญหากคุณต้องการค่า TOP ของชื่อแทนที่จะเป็น 'แรก' (ซึ่งอาจเป็นอะไรก็ได้แทรกแถวแรกได้มากที่สุด) อาจจะคุ้มค่าการแก้ไขหรือไม่
JulesLt

@JulesLt: การสืบค้นที่ระบุโดย OP ไม่ได้รวม ORDER BY ดังนั้นนี่คือคำตอบที่แสดงถึงคำแปลที่ถูกต้องในรูปแบบของ Oracle
OMG Ponies

ความเข้าใจผิดของฉันเกี่ยวกับไวยากรณ์ SQL SERVER TOP (สันนิษฐานผิดพลาดว่ามันคล้ายกับ FIRST ใน RANK ไม่ใช่ ROWNUM) โหวตขึ้น
JulesLt

3

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

เป็นครั้งแรก

select  max(Fname) from MyTbl;

ที่สอง

select  min(Fname) from MyTbl;

ที่สาม

select  Fname from MyTbl  where rownum = 1;

ที่สี่

select  max(Fname) from MyTbl where rowid=(select  max(rowid) from MyTbl)

3

ฉันมีปัญหาเดียวกันและฉันสามารถแก้ไขได้ด้วยวิธีนี้:

select a.*, rownum 
from (select Fname from MyTbl order by Fname DESC) a
where
rownum = 1

คุณสามารถสั่งซื้อผลลัพธ์ของคุณก่อนที่จะมีค่าแรกอยู่ด้านบน

โชคดี

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