เพจกับ Oracle


100

ฉันไม่คุ้นเคยกับ Oracle เท่าที่ฉันอยากจะเป็น ฉันมีระเบียน 250k และฉันต้องการแสดง 100 รายการต่อหน้า ขณะนี้ฉันมีกระบวนงานที่จัดเก็บไว้หนึ่งขั้นตอนซึ่งดึงข้อมูลทั้งหมดในสี่ล้านระเบียนไปยังชุดข้อมูลโดยใช้อะแดปเตอร์ข้อมูลและชุดข้อมูลและ dataadapter วิธีการกรอกข้อมูล (ชุดข้อมูล) ในผลลัพธ์จาก proc ที่เก็บไว้ หากฉันมี "หมายเลขหน้า" และ "จำนวนระเบียนต่อหน้า" เป็นค่าจำนวนเต็มฉันสามารถส่งผ่านเป็นพารามิเตอร์วิธีใดจะเป็นวิธีที่ดีที่สุดในการเรียกคืนเฉพาะส่วนนั้น สมมติว่าถ้าฉันส่ง 10 เป็นเลขหน้าและ 120 เป็นจำนวนหน้าจากคำสั่งที่เลือกมันจะให้ฉันอยู่ที่ 1880 ถึง 1200 หรือประมาณนั้นคณิตศาสตร์ในหัวของฉันอาจจะดับ

ฉันกำลังทำสิ่งนี้ใน. NET ด้วย C # คิดว่าไม่สำคัญถ้าฉันสามารถทำให้มันถูกต้องทางด้าน sql ฉันก็ควรจะเจ๋ง

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

คำตอบ:


148

สิ่งนี้ควรใช้งานได้: จากบล็อกของ Frans Bouma

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)

4
ใช่มันเป็นคอลัมน์ 'ในตัว' ที่ Oracle รองรับโดยจะเริ่มต้นที่ 1 และเพิ่มทีละแถวสำหรับแต่ละแถว ดังนั้นในข้อมูลโค้ดนี้หากคุณมี 1,000 แถวระบบจะใช้ลำดับการจัดเรียงจากนั้นแต่ละแถวจะกำหนดแถว ตัวเลือกด้านนอกใช้หมายเลขแถวเหล่านั้นเพื่อค้นหา 'เพจ' ที่คุณกำลังมองหาตามขนาดเพจของคุณ
Brian Schmitt

10
นี่เป็นสิ่งที่ดี แต่ช้าอย่างน่ากลัวสำหรับการเลือกขนาดใหญ่เพียงตรวจสอบว่าเวลาใดที่จะเลือก 0 ถึง 1,000 และ 500.000 ถึง 501.000 ... ฉันใช้โครงสร้างการเลือกประเภทนี้ตอนนี้ฉันกำลังค้นหาวิธีแก้ปัญหา
นิวเฮ้าส์


7
ฉันสงสัยว่าทำไมทั้งสองWHEREไม่สามารถรวมกันได้ANDแล้วพบสิ่งนี้: orafaq.com/wiki/ROWNUM
Mengdi Gao

2
เลขหน้า Oracle ทำลายวันของฉัน
Aetherus

138

ถามทอมเกี่ยวกับการแบ่งหน้าและฟังก์ชันการวิเคราะห์ที่มีประโยชน์มาก

ข้อความนี้ตัดตอนมาจากหน้านั้น:

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
    from all_objects
)
where rn between :n and :m
order by rn;

8
นี่เป็นการใช้งานที่ดีกว่ามากแม้ว่าจะหาได้ยากในโพสต์นั้นก็ตาม เมื่อคุณมีเพจขนาดใหญ่จำนวนมากคำตอบอื่น ๆ จะต้องอยู่เหนือแถวทั้งหมดจากเพจก่อนหน้าเช่นกัน ในการสืบค้นที่ซับซ้อนหมายความว่าหน้าต่อมาจะทำงานได้แย่กว่าหน้าก่อนหน้านี้
tallseth

@tallseth คุณพูดถูก หาดูได้ยากในหน้านั้น มีการเพิ่มข้อความที่ตัดตอนมา
Chobicus

นี่คือคำตอบที่ถูกต้องหากคุณต้องการเปลี่ยนลำดับของคุณแบบไดนามิก
chakeda

80

เพื่อความสมบูรณ์สำหรับผู้ที่กำลังมองหาโซลูชันที่ทันสมัยกว่าในOracle 12cมีคุณสมบัติใหม่บางอย่างรวมถึงการเพจที่ดีขึ้นและการจัดการด้านบน

เพจ

เพจมีลักษณะดังนี้:

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

อันดับ N Records

การรับบันทึกอันดับต้น ๆ มีลักษณะดังนี้:

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

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

ฉันไม่พบหน้าอ้างอิงของ Oracle ที่ดีสำหรับFETCHหรือOFFSETแต่หน้านี้มีภาพรวมที่ดีของคุณสมบัติใหม่เหล่านี้

ประสิทธิภาพ

ดังที่ @wweicker ชี้ให้เห็นในความคิดเห็นด้านล่างประสิทธิภาพเป็นปัญหากับไวยากรณ์ใหม่ใน 12c ฉันไม่มีสำเนาของ 18c เพื่อทดสอบว่า Oracle ได้ปรับปรุงแล้วหรือไม่

ที่น่าสนใจก็คือผลลัพธ์ที่แท้จริงของฉันกลับมาเร็วขึ้นเล็กน้อยในครั้งแรกที่ฉันเรียกใช้แบบสอบถามบนตารางของฉัน (113 ล้าน + แถว) สำหรับวิธีการใหม่:

  • วิธีใหม่: 0.013 วินาที
  • วิธีการเก่า: 0.107 วินาที

อย่างไรก็ตามตามที่ @wweicker กล่าวไว้แผนการอธิบายดูแย่ลงมากสำหรับวิธีการใหม่:

  • วิธีใหม่ราคา: 300,110
  • ราคาวิธีเก่า: 30

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

มาดูกันว่าเมื่อรวมคอลัมน์ที่ยังไม่ได้ทำดัชนีเดียวในชุดข้อมูลก่อนหน้านี้:

  • เวลาวิธีใหม่ / ต้นทุน: 189.55 วินาที / 998,908
  • เวลา / ต้นทุนของวิธีการแบบเก่า: 1.973 วินาที / 256

สรุป: ใช้ด้วยความระมัดระวังจนกว่า Oracle จะปรับปรุงการจัดการนี้ หากคุณมีดัชนีที่ใช้งานได้บางทีคุณอาจหลีกเลี่ยงการใช้วิธีการใหม่นี้

หวังว่าฉันจะมีสำเนาของ 18c ให้เล่นเร็ว ๆ นี้และสามารถอัปเดตได้


นี่เป็นคำตอบที่ดีสำหรับผู้ใช้ 12c
Lalji Gajera

2
ไวยากรณ์สะอาดกว่า แต่ประสิทธิภาพแย่ลง ( dba-presents.com/index.php/databases/oracle/… )
wweicker

สิ่งที่ควรทราบขอบคุณ @wweicker หวังว่าประสิทธิภาพจะได้รับการแก้ไขโดย Oracle เร็ว ๆ นี้ แม้ว่าการรู้จัก Oracle นี่อาจเป็นความหวังที่ห่างไกล!
JoelC

ไวยากรณ์เป็นแบบใหม่และจะเปลี่ยนเป็นการเรียก ROW_NUMBER / RANK ปกติ ที่เกี่ยวข้องฉันจะ จำกัด จำนวนแถวที่ส่งคืนโดยแบบสอบถาม Oracle หลังจากสั่งซื้อได้อย่างไร
Lukasz Szozda

@JoelC มีการเปลี่ยนแปลงความคิดเห็นของคุณหรือไม่?
Ryan

11

แค่อยากสรุปคำตอบและความคิดเห็น มีหลายวิธีในการแบ่งหน้า

ก่อนหน้า oracle 12c ไม่มีฟังก์ชัน OFFSET / FETCH ดังนั้นโปรดดูเอกสารทางเทคนิคไวท์เปเปอร์ตามที่ @jasonk แนะนำ เป็นบทความที่สมบูรณ์ที่สุดที่ฉันพบเกี่ยวกับวิธีการต่างๆพร้อมคำอธิบายโดยละเอียดเกี่ยวกับข้อดีและข้อเสีย ต้องใช้เวลาพอสมควรในการคัดลอกวางที่นี่ดังนั้นฉันจะไม่ทำ

นอกจากนี้ยังมีบทความดีๆจากผู้สร้าง jooq ที่อธิบายคำเตือนทั่วไปเกี่ยวกับ oracle และฐานข้อมูลอื่น ๆ บล็อกโพสต์ของ jooq

ข่าวดีเนื่องจาก oracle 12c เรามีฟังก์ชัน OFFSET / FETCH ใหม่ OracleMagazine 12c คุณสมบัติใหม่ โปรดดูที่ "ข้อความค้นหายอดนิยมและเลขหน้า"

คุณสามารถตรวจสอบเวอร์ชัน Oracle ของคุณได้โดยการออกคำสั่งต่อไปนี้

SELECT * FROM V$VERSION

7

ลองทำดังต่อไปนี้:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

ผ่าน[tecnicume]


0

ในโครงการของฉันฉันใช้ของ Oracle และ Java รหัสเพจมีลักษณะดังนี้:

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.