Oracle SELECT 10 อันดับแรกของเรคคอร์ด


144

ฉันมีปัญหาใหญ่กับคำสั่ง SQL ใน Oracle ฉันต้องการเลือก 10 อันดับแรกของระเบียนที่เรียงลำดับโดย STORAGE_DB ซึ่งไม่ได้อยู่ในรายการจากรายการเลือกอื่น

อันนี้ทำงานได้ดีสำหรับบันทึกทั้งหมด:

SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
      STORAGE_GB IS NOT NULL AND 
        APP_ID NOT IN (SELECT APP_ID
                       FROM HISTORY
                        WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009') 

แต่เมื่อฉันเพิ่ม

AND ROWNUM <= 10
ORDER BY STORAGE_GB DESC

ฉันได้รับการบันทึกแบบ "สุ่ม" บางอย่าง ฉันคิดว่าเพราะข้อ จำกัด เกิดขึ้นก่อนการสั่งซื้อ

ใครบางคนมีทางออกที่ดี? ปัญหาอื่น ๆ : แบบสอบถามนี้ช้าจริง ๆ (10k + บันทึก)


ข้อมูลซ้ำที่น่าจะเป็นไปได้: stackoverflow.com/questions/2306744/…
APC

คำตอบ:


199

คุณจะต้องใส่ข้อความค้นหาปัจจุบันในแบบสอบถามย่อยดังต่อไปนี้:

SELECT * FROM (
  SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
    STORAGE_GB IS NOT NULL AND 
      APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
  ORDER BY STORAGE_GB DESC )
WHERE ROWNUM <= 10

Oracle ใช้rownumกับผลลัพธ์หลังจากที่ส่งคืนแล้ว
คุณต้องกรองผลลัพธ์หลังจากส่งคืนแล้วจึงต้องการแบบสอบถามย่อย นอกจากนี้คุณยังสามารถใช้ฟังก์ชันRANK ()เพื่อรับผลลัพธ์ยอดนิยม N

เพื่อประสิทธิภาพในการลองใช้ในสถานที่ของNOT EXISTS NOT INดูสิ่งนี้เพิ่มเติม


EXISTS ไม่ทำงานในสถานการณ์นี้ (ตัวดำเนินการสัมพันธ์ที่ไม่ถูกต้อง) APP_ID ไม่ใช่ EXISTS (SELEC ... )
opHASnoNAME

3
บางคนอาจบอกว่านี่เหมาะที่จะปิดผู้คนไปยัง Oracle
MrBoJangles

2
ตรวจสอบFETCH NEXT N ROWS ONLYคำตอบด้านล่าง
Mohnish

@Padmarag: เมื่อใด rownum จะนำไปใช้ในแบบสอบถามแบบนี้ - เลือก * จาก SomeTable โดย someColumn = '123' และ rownum <= 3 คือหลังจากเลือกผลลัพธ์จาก [Select * จาก SomeTable โดย someColumn = '123']
Shirgill Farhan

55

หากคุณใช้ Oracle 12c ให้ใช้:

เรียก NEXT Nแถวเท่านั้น

SELECT DISTINCT 
  APP_ID, 
  NAME, 
  STORAGE_GB, 
  HISTORY_CREATED, 
  TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE  
  FROM HISTORY WHERE 
    STORAGE_GB IS NOT NULL AND 
      APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
  ORDER BY STORAGE_GB DESC
FETCH NEXT 10 ROWS ONLY

ข้อมูลเพิ่มเติม: http://docs.oracle.com/javadb/10.5.3.0/ref/rrefsqljoffsetfetch.html


2
นี่คือทองคำเมื่อเทียบกับคำตอบอื่น ๆ
aswzen

ฉันเห็นด้วยกับ aswzen
Austin Springer

1
ฉันต้องการให้คำตอบนี้ 100 upvotes! แต่อนิจจาฉันมีเพียงรางวัลเดียวเท่านั้น หนึ่งมันคือ!
eidylon

23

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

WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009') 

ถ้า HISTORY_DATE เป็นคอลัมน์วันจริง ๆ และถ้ามีดัชนีการเขียนใหม่นี้จะทำงานได้ดีขึ้น:

WHERE HISTORY_DATE = TO_DATE ('06.02.2009', 'DD.MM.YYYY')  

นี่เป็นเพราะการแปลงประเภทข้อมูลปิดการใช้งานดัชนี B-Tree



11

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

 select * from 
     (select empno,
             ename,
             sal,
             row_number() over(order by sal desc nulls last) rnm
    from emp) 
 where rnm<=10
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.