เท่ากับ LIMIT สำหรับ DB2


93

คุณทำได้อย่างไรLIMITใน DB2 สำหรับ iSeries

ฉันมีตารางที่มีระเบียนมากกว่า 50,000 รายการและฉันต้องการส่งคืนระเบียน 0 ถึง 10,000 และบันทึก 10,000 ถึง 20,000

ฉันรู้ว่าใน SQL คุณเขียน LIMIT 0,10000ในตอนท้ายของแบบสอบถามสำหรับ 0 ถึง 10,000 และLIMIT 10000,10000ในตอนท้ายของแบบสอบถามสำหรับ 10,000 ถึง 20,000

แล้วจะทำอย่างไรใน DB2? รหัสและไวยากรณ์คืออะไร? (ชื่นชมตัวอย่างแบบสอบถามแบบเต็ม)


ROW_NUMBER () ถูกใช้งานใน iSeries DB2 V5R4 เท่านั้น สำหรับเวอร์ชันก่อนหน้าให้ลองใช้ RRN () ซึ่งคล้ายกัน
Paul Morgan

RRN () แตกต่างจาก row_number () อย่างสิ้นเชิง
Brandon Peterson

ไม่ได้ผลสำหรับฉัน ข้อผิดพลาด Sytanx
elcool

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

1
DB2 ให้การสนับสนุนคำหลักแบบ จำกัด จาก DB2 9.7.2 อ้างอิงจาก programmingzen.com/2010/06/02/…
lakshman

คำตอบ:


141

ใช้FETCH FIRST [n] ROWS ONLY:

http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.perf/db2z_fetchfirstnrows.htm

SELECT LASTNAME, FIRSTNAME, EMPNO, SALARY
  FROM EMP
  ORDER BY SALARY DESC
  FETCH FIRST 20 ROWS ONLY;

ในการรับช่วงคุณต้องใช้ROW_NUMBER()(ตั้งแต่ v5r4) และใช้สิ่งนั้นภายในWHEREประโยค: (ขโมยจากที่นี่: http://www.justskins.com/forums/db2-select-how-to-123209.html )

SELECT code, name, address
FROM ( 
  SELECT row_number() OVER ( ORDER BY code ) AS rid, code, name, address
  FROM contacts
  WHERE name LIKE '%Bob%' 
  ) AS t
WHERE t.rid BETWEEN 20 AND 25;

ใช่ฉันเจอสิ่งนี้ด้วยฮิฮิ ฉันกำลังแก้ไขคำถามในเวลาเดียวกันเพื่อระบุว่าฉันต้องการแถวกลางด้วย
elcool

2
คุณต้องทำสิ่งนี้กับ ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe

ROW_NUMBERไม่ใช่คำหลักที่ถูกต้อง แต่ขอบคุณสำหรับลิงค์มันทำให้ฉันมีความคิดและมันใช้งานได้
elcool

13

พัฒนาวิธีนี้:

คุณต้องการตารางที่มีค่าเฉพาะที่สามารถสั่งซื้อได้

หากคุณต้องการแถว 10,000 ถึง 25,000 และตารางของคุณมี 40,000 แถวก่อนอื่นคุณต้องได้รับจุดเริ่มต้นและแถวทั้งหมด:

int start = 40000 - 10000;

int total = 25000 - 10000;

จากนั้นส่งรหัสเหล่านี้ไปยังแบบสอบถาม:

SELECT * FROM 
(SELECT * FROM schema.mytable 
ORDER BY userId DESC fetch first {start} rows only ) AS mini 
ORDER BY mini.userId ASC fetch first {total} rows only

โปรดทราบว่าแถวที่ 10,000 ถูกแยกออกจากชุดผลลัพธ์แถวแรกคือ 10001st
สีน้ำเงิน

1
ทางออกที่น่าสนใจ ฉันจะใช้มันเพื่อความเข้ากันได้กับฐานข้อมูลทดสอบ H2 ... แต่น่าเศร้าที่มันทำงานช้ากว่าวิธี SELECT row_number () OVER (ORDER BY code) ประมาณ 30 เท่า
manuna

9

เมื่อเร็ว ๆ นี้มีการเพิ่มการสนับสนุนสำหรับ OFFSET และ LIMIT ใน DB2 สำหรับ i 7.1 และ 7.2 คุณต้องมีระดับกลุ่ม DB PTF ต่อไปนี้เพื่อรับการสนับสนุนนี้:

  • SF99702 ระดับ 9 สำหรับ IBM i 7.2
  • SF99701 ระดับ 38 สำหรับ IBM i 7.1

ดูข้อมูลเพิ่มเติมได้ที่นี่: เอกสาร OFFSET และ LIMIT , DB2 for i Enhancement Wiki


7

นี่คือวิธีแก้ปัญหาที่ฉันคิดขึ้น:

select FIELD from TABLE where FIELD > LASTVAL order by FIELD fetch first N rows only;

โดยการเริ่มต้น LASTVAL เป็น 0 (หรือ '' สำหรับช่องข้อความ) จากนั้นตั้งค่าเป็นค่าสุดท้ายในชุดระเบียนล่าสุดนี้จะดำเนินการตามตารางในส่วนของระเบียน N


(ผมเริ่มคิดว่าคุณตั้งค่าในตารางซึ่งจะไม่ยี่หระปัญหาในระบบพร้อมกัน) ใช่นี้ควรจะทำงานในกรณีที่คุณกำลังทำอ่านตามลำดับผ่านโต๊ะแม้ว่าคุณจะต้องเรียงลำดับของบางอย่าง คอลัมน์ tie-breaker ในกรณีที่Nมีค่าน้อยกว่าจำนวนค่าที่เหมือนกันในคอลัมน์ (แม้ว่าจะเป็นจริงเมื่อใช้ROW_NUMBER()ด้วย) ต้องเลือกค่าเริ่มต้นด้วยความระมัดระวัง - 0จะเป็นปัญหาอย่างชัดเจนหากคอลัมน์มีค่าเป็นลบ จำเป็นต้องมีการดูแลด้วยค่าว่าง จะไม่ทำงานหากมีการข้ามหน้า
Clockwork-Muse

ขอบคุณสำหรับความคิดเห็น ฉันคิดว่ามีข้อสันนิษฐานโดยปริยายว่าฟิลด์ที่เราใช้ควบคุมคิวรีนั้นไม่ซ้ำกันและเพิ่มขึ้นอย่างจำเจ ฉันยอมรับว่าหากข้อสันนิษฐานเหล่านั้นไม่เป็นไปตามนี้จะใช้ไม่ได้ในการเยี่ยมชมบันทึกทั้งหมดในตาราง และแน่นอนคุณคิดถูกแล้วที่ต้องเริ่มต้นด้วย LASTVAL ที่สมเหตุสมผล โดยทั่วไปฉันคิดว่าคุณต้องการเริ่มต้นด้วยสิ่งที่ส่งกลับโดย "เลือกขั้นต่ำ (FIELD) จาก TABLE" หากมีการจัดทำดัชนีฟิลด์เอ็นจิน db ส่วนใหญ่จะทำได้ดีกว่าการอ่านทั้งตารางตามลำดับ
Tom Barron

2

โซลูชันของ @ elcoolเป็นแนวคิดที่ชาญฉลาด แต่คุณต้องรู้จำนวนแถวทั้งหมด (ซึ่งสามารถเปลี่ยนแปลงได้ในขณะที่คุณดำเนินการสืบค้น!) ดังนั้นฉันจึงเสนอเวอร์ชันที่แก้ไขซึ่งน่าเสียดายที่ต้องการ 3 แบบสอบถามย่อยแทนที่จะเป็น 2:

select * from (
    select * from (
        select * from MYLIB.MYTABLE
        order by MYID asc 
        fetch first {last} rows only 
        ) I 
    order by MYID desc
    fetch first {length} rows only
    ) II
order by MYID asc

ที่{last}ควรถูกแทนที่ด้วยหมายเลขแถวของระเบียนสุดท้ายที่ฉันต้องการและจะถูกแทนที่ด้วยจำนวนแถวที่ฉันต้องการคำนวณเป็น{length}last row - first row + 1

เช่นถ้าฉันต้องการแถวจาก 10 ถึง 25 (ทั้งหมด 16 แถว) {last}จะเป็น 25 และ{length}จะเป็น 25-10 + 1 = 16


ฉันดูถูกคนที่ลงคะแนนเมื่อคนอื่นใช้เวลาในการตอบคำถามของพวกเขา
jp2code

1

คุณควรพิจารณาอนุประโยค OPTIMIZE FOR n ROWS ด้วย รายละเอียดเพิ่มเติมเกี่ยวกับทั้งหมดนี้ในเอกสาร DB2 LUW ในหัวข้อแนวทางการ จำกัด คำสั่ง SELECT :

  • OPTIMIZE FOR อนุประโยคประกาศเจตนาที่จะดึงเฉพาะส่วนย่อยของผลลัพธ์หรือให้ความสำคัญกับการดึงข้อมูลเฉพาะสองสามแถวแรก จากนั้นเครื่องมือเพิ่มประสิทธิภาพสามารถเลือกแผนการเข้าถึงที่ลดเวลาตอบสนองสำหรับการเรียกข้อมูลสองสามแถวแรก


0

มี 2 ​​วิธีในการแบ่งหน้าอย่างมีประสิทธิภาพบนตาราง DB2:

1 - เทคนิคโดยใช้ฟังก์ชัน row_number () และประโยค OVER ซึ่งถูกนำเสนอในโพสต์อื่น ("SELECT row_number () OVER (ORDER BY ... )") บนโต๊ะขนาดใหญ่บางครั้งฉันสังเกตเห็นว่าการแสดงลดลง

2 - เทคนิคโดยใช้เคอร์เซอร์แบบเลื่อนได้ การนำไปใช้ขึ้นอยู่กับภาษาที่ใช้ เทคนิคนี้ดูเหมือนจะมีประสิทธิภาพมากขึ้นบนโต๊ะขนาดใหญ่

ฉันนำเสนอ 2 เทคนิคที่ใช้ใน PHP ระหว่างการสัมมนาในปีหน้า สามารถดูสไลด์ได้ที่ลิงค์นี้: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

ขออภัยเอกสารนี้เป็นภาษาฝรั่งเศสเท่านั้น


0

มีตัวเลือกที่มีให้เหล่านี้: -

DB2 has several strategies to cope with this problem.
You can use the "scrollable cursor" in feature.
In this case you can open a cursor and, instead of re-issuing a query you can FETCH forward and backward.
This works great if your application can hold state since it doesn't require DB2 to rerun the query every time.
You can use the ROW_NUMBER() OLAP function to number rows and then return the subset you want.
This is ANSI SQL 
You can use the ROWNUM pseudo columns which does the same as ROW_NUMBER() but is suitable if you have Oracle skills.
You can use LIMIT and OFFSET if you are more leaning to a mySQL or PostgreSQL dialect.  
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.