วิธีใช้ Oracle ORDER BY และ ROWNUM อย่างถูกต้อง?


126

ฉันมีปัญหาในการแปลงกระบวนงานที่จัดเก็บจาก SQL Server เป็น Oracle เพื่อให้ผลิตภัณฑ์ของเราเข้ากันได้กับมัน

ฉันมีคำถามที่ส่งคืนระเบียนล่าสุดของตารางบางตารางโดยอิงตามการประทับเวลา:

เซิร์ฟเวอร์ SQL:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> นั่นจะส่งคืนบันทึกล่าสุดให้ฉัน

แต่Oracle:

SELECT *
FROM raceway_input_labo 
WHERE  rownum <= 1
ORDER BY t_stamp DESC

=> นั่นจะส่งคืนบันทึกที่เก่าแก่ที่สุดให้ฉัน (อาจขึ้นอยู่กับดัชนี) โดยไม่คำนึงถึงORDER BYคำสั่ง!

ฉันสรุปการสืบค้น Oracle ด้วยวิธีนี้เพื่อให้ตรงกับความต้องการของฉัน:

SELECT * 
FROM 
    (SELECT *
     FROM raceway_input_labo 
     ORDER BY t_stamp DESC)
WHERE  rownum <= 1

และได้ผล แต่ดูเหมือนเป็นการแฮ็คที่น่ากลัวสำหรับฉันโดยเฉพาะอย่างยิ่งถ้าฉันมีประวัติจำนวนมากในตารางที่เกี่ยวข้อง

วิธีที่ดีที่สุดในการบรรลุเป้าหมายนี้คืออะไร?



4
สิ่งที่คุณทำในแบบสอบถามล่าสุดถูกต้อง คุณเลือกแถวที่ 1 ของรายการระเบียนที่เรียงลำดับ เพียงแค่ Query encapsulation
araknoid

1
เอกสารนี้ระบุไว้อย่างชัดเจนในคู่มือ: docs.oracle.com/cd/E11882_01/server.112/e26088/…
a_horse_with_no_name

5
@a_horse_with_no_name คุณหมายถึงเอกสารข้อผิดพลาด 404 นี้อย่างชัดเจน
anthonybrice

3
@anthonybrice: ขอบคุณ Oracle เปลี่ยน URL ทั้งหมดเป็นคู่มือ ลิงค์ล่าสุดคือ: docs.oracle.com/cd/E11882_01/server.112/e41084/…
a_horse_with_no_name

คำตอบ:


120

whereคำสั่งที่ได้รับการดำเนินการก่อนที่จะ order byดังนั้นข้อความค้นหาที่คุณต้องการคือ " รับแถวแรกแล้วเรียงลำดับโดย t_stamp desc " และนั่นไม่ใช่สิ่งที่คุณตั้งใจ

วิธีการสืบค้นย่อยเป็นวิธีการที่เหมาะสมสำหรับการดำเนินการนี้ใน Oracle

หากคุณต้องการเวอร์ชันที่ใช้งานได้ในทั้งสองเซิร์ฟเวอร์คุณสามารถใช้:

select ril.*
from (select ril.*, row_number() over (order by t_stamp desc) as seqnum
      from raceway_input_labo ril
     ) ril
where seqnum = 1

ด้านนอก*จะส่งกลับ "1" ในคอลัมน์สุดท้าย คุณจะต้องแสดงรายการแต่ละคอลัมน์เพื่อหลีกเลี่ยงปัญหานี้


40

ใช้ROW_NUMBER()แทน ROWNUMเป็น pseudocolumn และROW_NUMBER()เป็นฟังก์ชัน คุณสามารถอ่านเกี่ยวกับความแตกต่างและดูความแตกต่างในผลลัพธ์ของแบบสอบถามด้านล่าง:

SELECT * FROM (SELECT rownum, deptno, ename
           FROM scott.emp
        ORDER BY deptno
       )
 WHERE rownum <= 3
 /

ROWNUM    DEPTNO    ENAME
---------------------------
 7        10    CLARK
 14       10    MILLER
 9        10    KING


 SELECT * FROM 
 (
  SELECT deptno, ename
       , ROW_NUMBER() OVER (ORDER BY deptno) rno
  FROM scott.emp
 ORDER BY deptno
 )
WHERE rno <= 3
/

DEPTNO    ENAME    RNO
-------------------------
10    CLARK        1
10    MILLER       2
10    KING         3

3
ROWNUMอาจเร็วกว่าที่ROW_NUMBER()ควรจะใช้หรือไม่ขึ้นอยู่กับปัจจัยหลายประการ
David Faber

ขออภัยสำหรับการโหวตลงคะแนนโดยไม่ได้ตั้งใจ! น่าเสียดายที่ฉันไม่สามารถนำมันกลับคืนมาได้ในตอนนี้
Athafoud

0

ทางเลือกอื่นที่ฉันขอแนะนำในกรณีการใช้งานนี้คือการใช้ MAX (t_stamp) เพื่อรับแถวล่าสุด ... เช่น

select t.* from raceway_input_labo t
where t.t_stamp = (select max(t_stamp) from raceway_input_labo) 
limit 1

การตั้งค่ารูปแบบการเข้ารหัสของฉัน (อาจ) - เชื่อถือได้โดยทั่วไปทำงานได้ดีหรือดีกว่าการพยายามเลือกแถวที่ 1 จากรายการที่เรียงลำดับ - นอกจากนี้จุดประสงค์ยังอ่านได้ชัดเจนกว่า
หวังว่านี่จะช่วยได้ ...

SQLer


3
Oracle ไม่มี LIMIT คุณกำลังขอร้องคำถาม
philipxy

0

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

WHERE m_api_log.created_date BETWEEN TO_DATE('10/23/2015 05:00', 'MM/DD/YYYY HH24:MI') 
                                 AND TO_DATE('10/30/2015 23:59', 'MM/DD/YYYY HH24:MI')  

จะลดผลลัพธ์ลงอย่างมาก จากนั้นคุณสามารถสั่งซื้อหรือแม้แต่ทำแบบสอบถามภายนอกเพื่อ จำกัด แถว

นอกจากนี้ฉันคิดว่า TOAD มีคุณสมบัติในการ จำกัด แถว แต่ไม่แน่ใจว่าจะ จำกัด อยู่ในแบบสอบถามจริงบน Oracle ไม่แน่ใจ.

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