Oracle PL/SQL BULK COLLECT: ตัวอย่าง FORALL

⚡ สรุปอย่างชาญฉลาด

บริการรวบรวมสินค้าจำนวนมาก Oracle PL/SQL ดึงข้อมูลหลายแถวพร้อมกันลงในคอลเลกชัน ในขณะที่ FORALL จะส่งคำสั่ง DML จำนวนมากกลับไปยังฐานข้อมูล ทั้งสองวิธีนี้ช่วยลดการสลับบริบทระหว่างเอนจิน SQL และ PL/SQL ซึ่งช่วยเพิ่มประสิทธิภาพ

  • 📦 บริการรวบรวมสินค้าจำนวนมาก: ดึงข้อมูลหลายแถวในคราวเดียวลงในตัวแปรคอลเลกชัน แทนที่การดึงข้อมูลทีละแถวซึ่งใช้เวลานาน
  • 🔁 สำหรับทุกคน: ดำเนินการคำสั่ง INSERT, UPDATE หรือ DELETE เพียงครั้งเดียวกับข้อมูลทั้งหมดในคอลเลกชันด้วยการสลับบริบทเพียงครั้งเดียว
  • 📏 ข้อกำหนด LIMIT: จำกัดจำนวนแถวที่แต่ละคำสั่ง BULK COLLECT ดึงข้อมูล เพื่อป้องกันการใช้หน่วยความจำในเซสชันกับตารางขนาดใหญ่
  • 📊 คุณลักษณะของการรวบรวมจำนวนมาก: แอตทริบิวต์ %BULK_ROWCOUNT(n) จะรายงานจำนวนแถวที่คำสั่ง DML FORALL ลำดับที่ n ได้รับผลกระทบ
  • ⚙️ เอกสารที่ต้องรวบรวม: เงื่อนไข INTO ต้องกำหนดเป้าหมายไปยังประเภทคอลเลกชัน เช่น ตารางซ้อน หรืออาร์เรย์แบบเชื่อมโยง
  • 🤖 ความช่วยเหลือจาก AI: ผู้ช่วย AI เช่น GitHub Copilot จะร่างบล็อก BULK COLLECT และ FORALL และแจ้งเตือนหากขาดข้อความ LIMIT

Oracle ภาพรวมของคำสั่ง PL/SQL BULK COLLECT และ FORALL พร้อมเงื่อนไข LIMIT

BULK COLLECT คืออะไร?

BULK COLLECT ช่วยลดการสลับบริบทระหว่างกัน SQL และเอนจิน PL/SQL และอนุญาตให้เอนจิน SQL ดึงข้อมูลบันทึกทั้งหมดได้ในครั้งเดียว

Oracle PL / SQL ฟังก์ชันนี้ช่วยให้สามารถดึงข้อมูลจำนวนมากพร้อมกันได้ แทนที่จะดึงทีละรายการ คำสั่ง BULK COLLECT นี้สามารถใช้ในคำสั่ง SELECT เพื่อดึงข้อมูลจำนวนมาก หรือเพื่อดึงข้อมูลจำนวนหนึ่งได้ เคอร์เซอร์ เป็นจำนวนมาก เนื่องจาก BULK COLLECT ดึงข้อมูลมาเป็นจำนวนมาก ดังนั้นในส่วนของ INTO ควรมีตัวแปรประเภทคอลเลกชันเสมอ ข้อดีหลักของการใช้ BULK COLLECT คือช่วยเพิ่มประสิทธิภาพโดยลดการโต้ตอบระหว่างฐานข้อมูลและเอนจิน PL/SQL

ไวยากรณ์:

SELECT <column1> BULK COLLECT INTO bulk_variable FROM <table name>;
FETCH <cursor_name> BULK COLLECT INTO <bulk_variable>;

ในไวยากรณ์ข้างต้น BULK COLLECT ใช้เพื่อรวบรวมข้อมูลจากคำสั่ง SELECT และ FETCH

ข้อ FORALL

คำสั่ง FORALL ดำเนินการ การดำเนินการ DML คำสั่ง FORALL ใช้สำหรับประมวลผลข้อมูลจำนวนมาก คล้ายกับคำสั่ง FOR loop แต่ใน FOR loop การกระทำจะเกิดขึ้นที่ระดับเรคอร์ด ในขณะที่ FORALL ไม่มีแนวคิดเรื่องลูป แต่จะประมวลผลข้อมูลทั้งหมดที่อยู่ในช่วงที่กำหนดพร้อมกัน

ไวยากรณ์:

FORALL <loop_variable> in <lower range> .. <higher range>

<DML operations>;

ในไวยากรณ์ข้างต้น การดำเนินการ DML ที่กำหนดจะถูกดำเนินการกับข้อมูลทั้งหมดที่อยู่ระหว่างช่วงล่างและช่วงบน

ข้อ จำกัด

แนวคิดการรวบรวมข้อมูลแบบกลุ่ม (Bulk Collect) จะโหลดข้อมูลทั้งหมดลงในตัวแปรคอลเลกชันเป้าหมายในคราวเดียว แต่ไม่แนะนำให้ใช้วิธีนี้หากจำนวนระเบียนทั้งหมดที่ต้องโหลดมีจำนวนมาก เพราะเมื่อ PL/SQL พยายามโหลดข้อมูลทั้งหมด มันจะใช้หน่วยความจำเซสชันมากขึ้น ดังนั้นจึงควรจำกัดขนาดของการดำเนินการรวบรวมข้อมูลแบบกลุ่มนี้เสมอ

ข้อจำกัดด้านขนาดนี้สามารถทำได้ง่ายๆ โดยการใช้เงื่อนไข ROWNUM ในคำสั่ง SELECT ในขณะที่ในกรณีของเคอร์เซอร์นั้นไม่สามารถทำได้

เพื่อเอาชนะสิ่งนี้ Oracle ได้กำหนดเงื่อนไข LIMIT ไว้ ซึ่งระบุจำนวนระเบียนที่ต้องรวมอยู่ในกลุ่มข้อมูลขนาดใหญ่

ไวยากรณ์:

FETCH <cursor_name> BULK COLLECT INTO <bulk_variable> LIMIT <size>;

ในไวยากรณ์ข้างต้น คำสั่งดึงข้อมูลเคอร์เซอร์ใช้คำสั่ง BULK COLLECT ร่วมกับข้อกำหนด LIMIT

BULK รวบรวมคุณสมบัติ

เช่นเดียวกับแอตทริบิวต์ของเคอร์เซอร์ BULK COLLECT มี %BULK_ROWCOUNT(n) ที่ส่งคืนจำนวนแถวที่ได้รับผลกระทบในคำสั่ง DML ที่ n ของคำสั่ง FORALL กล่าวคือ จะให้จำนวนระเบียนที่ได้รับผลกระทบในคำสั่ง FORALL สำหรับแต่ละค่าจากตัวแปรคอลเลกชัน คำว่า 'n' ระบุลำดับของค่าในคอลเลกชันที่ต้องการนับจำนวนแถว

1 ตัวอย่าง: ในตัวอย่างนี้ เราจะดึงรายชื่อพนักงานทั้งหมดจากตาราง emp โดยใช้ BULK COLLECT และเราจะเพิ่มเงินเดือนของพนักงานทั้งหมดขึ้น 5000 โดยใช้ FORALL

ภาพหน้าจอด้านล่างแสดงตัวอย่างคำสั่ง BULK COLLECT และ FORALL พร้อมกับผลลัพธ์ที่ได้ Oracle.

ตัวอย่างการใช้คำสั่ง BULK COLLECT ร่วมกับ LIMIT และ FORALL เพื่ออัปเดตเงินเดือนพนักงาน Oracle PL / SQL

DECLARE
CURSOR guru99_det IS SELECT emp_name FROM emp;
TYPE lv_emp_name_tbl IS TABLE OF VARCHAR2(50);
lv_emp_name lv_emp_name_tbl;
BEGIN
OPEN guru99_det;
FETCH guru99_det BULK COLLECT INTO lv_emp_name LIMIT 5000;
FOR c_emp_name IN lv_emp_name.FIRST .. lv_emp_name.LAST
LOOP
Dbms_output.put_line('Employee Fetched:'||c_emp_name);
END LOOP;
FORALL i IN lv_emp_name.FIRST .. lv_emp_name.LAST
UPDATE emp SET salary=salary+5000 WHERE emp_name=lv_emp_name(i);
COMMIT;
Dbms_output.put_line('Salary Updated');
CLOSE guru99_det;
END;
/

เอาท์พุต

Employee Fetched:BBB
Employee Fetched:XXX
Employee Fetched:YYY
Salary Updated

Code คำอธิบาย:

  • Code สาย 2: ประกาศเคอร์เซอร์ guru99_det สำหรับคำสั่ง 'SELECT emp_name FROM emp'
  • Code สาย 3: ประกาศ lv_emp_name_tbl เป็นชนิดตาราง VARCHAR2(50)
  • Code สาย 4: ประกาศตัวแปร lv_emp_name เป็นชนิด lv_emp_name_tbl
  • Code สาย 6: การเปิดเคอร์เซอร์
  • Code สาย 7: ดึงข้อมูลเคอร์เซอร์โดยใช้คำสั่ง BULK COLLECT โดยกำหนดขนาด LIMIT เป็น 5000 และบันทึกไว้ในตัวแปร lv_emp_name
  • Code บรรทัดที่ 8-11: ตั้งค่าลูป FOR เพื่อพิมพ์ข้อมูลทั้งหมดในคอลเลกชัน lv_emp_name
  • Code สาย 12: ใช้คำสั่ง FORALL เพื่อปรับเงินเดือนของพนักงานทั้งหมดเพิ่มขึ้น 5000 บาท
  • Code สาย 14: การกระทำ การทำธุกรรม.

คำถามที่พบบ่อย

ไม่ การใช้คำสั่ง BULK COLLECT SELECT จะไม่ทำให้เกิดข้อผิดพลาด NO_DATA_FOUND แต่จะคืนค่าคอลเลกชันว่างเปล่าแทน ควรตรวจสอบคอลเลกชันด้วยเมธอด .COUNT ก่อนทำการดูข้อมูลเสมอpingมิเช่นนั้น คุณอาจประมวลผลข้อมูลเป็นศูนย์แถวโดยไม่มีการแจ้งเตือนใดๆ

คำสั่ง SAVE EXCEPTIONS ช่วยให้ FORALL ทำงานต่อไปได้แม้ว่าแต่ละแถวจะล้มเหลว แถวที่ล้มเหลวจะถูกเก็บไว้ใน SQL%BULK_EXCEPTIONS จากนั้น Oracle ทำให้เกิดข้อผิดพลาด ORA-24381 ซึ่งคุณสามารถดักจับได้ใน ข้อยกเว้น ตัวจัดการเพื่อตรวจสอบข้อผิดพลาดแต่ละรายการ

ใช้คำสั่ง BULK COLLECT ทุกครั้งที่ลูปอ่านข้อมูลหลายแถว เคอร์เซอร์ ลูป FOR จะดึงข้อมูลหนึ่งแถวต่อการสลับหนึ่งครั้ง ดังนั้นการดึงข้อมูลจำนวนมากร่วมกับ FORALL จึงสามารถทำงานได้เร็วกว่าหลายเท่าในชุดผลลัพธ์ขนาดใหญ่

BULK COLLECT ส่งคืนข้อมูลหลายแถวพร้อมกัน ดังนั้นจึงต้องใช้คอนเทนเนอร์ที่รองรับหลายแถว เป้าหมาย INTO ต้องเป็น ชุด เช่น ตารางซ้อนกัน, VARRAY หรืออาร์เรย์แบบเชื่อมโยง ไม่ใช่ตัวแปรสเกลาร์ตัวเดียว

ไม่ คำสั่ง FORALL จะควบคุมคำสั่ง INSERT, UPDATE, DELETE หรือ MERGE เพียงครั้งเดียวเท่านั้น เฉพาะค่าในส่วน VALUES และ WHERE เท่านั้นที่อาจเปลี่ยนแปลงได้ในแต่ละรอบ หากต้องการใช้คำสั่งหลายคำสั่ง ให้ใช้คำสั่ง FORALL แยกกัน

การประมวลผลแบบกลุ่มสามารถเร็วกว่าการเขียนโค้ดทีละแถวหลายเท่าไปจนถึงมากกว่าร้อยเท่า เนื่องจาก BULK COLLECT และ FORALL ช่วยลดการสลับบริบทของเอนจินหลายพันครั้งให้เหลือเพียงไม่กี่ครั้ง ซึ่งช่วยลดภาระการทำงานบนข้อมูลขนาดใหญ่ได้อย่างมาก

ใช่. นักบิน GitHub ร่างคำสั่ง BULK COLLECT สำหรับการดึงข้อมูล, ลูป FORALL DML และข้อกำหนด LIMIT จากความคิดเห็น และแนะนำการประกาศประเภทคอลเลกชัน แม้ว่าคุณควรตรวจสอบขนาดชุดข้อมูลและการจัดการข้อผิดพลาดด้วยตนเองก็ตาม

ผู้ช่วย AI จะสแกนลูปที่ดึงหรือเปลี่ยนแปลงข้อมูลทีละแถว และแนะนำให้เขียนใหม่โดยใช้ BULK COLLECT, LIMIT และ FORALL การตรวจสอบด้วยแมชชีนเลิร์นนิงนี้จะตรวจจับข้อจำกัดของ LIMIT ที่ขาดหายไปและปัญหาคอขวดด้านประสิทธิภาพก่อนนำไปใช้งานจริง

สรุปโพสต์นี้ด้วย: