程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle數據庫基礎 >> Oracle數據庫的BULK COLLECT用法之批量增刪改

Oracle數據庫的BULK COLLECT用法之批量增刪改

編輯:Oracle數據庫基礎

Oracle數據庫的BULK COLLECT用法之批量增刪改的相關知識是本文我們主要要介紹的內容,FORALL語句的一個關鍵性改進,它可以大大簡化代碼,並且對於那些要在PL/SQL程序中更新很多行數據的程序來說,它可顯著提高其性能。

用FORALL來增強DML的處理能力

Oracle為Oracle8i中的PL/SQL引入了兩個新的數據操縱語言(DML)語句:BULK COLLECT和FORALL。這兩個語句在PL/SQL內部進行一種數組處理;BULK COLLECT提供對數據的高速檢索,FORALL可大大改進INSERT、UPDATE和DELETE操作的性能。Oracle數據庫使用這些語句大大減少了。

PL/SQL與SQL語句執行引擎的環境切換次數,從而使其性能有了顯著提高。使用BULK COLLECT,你可以將多個行引入一個或多個集合中,而不是單獨變量或記錄中。下面這個BULK COLLECT的實例是將標題中包含有"PL/SQL"的所有書籍檢索出來並置於記錄的一個關聯數組中,它們都位於通向該數據庫的單一通道中。

  1. DECLARE
  2. TYPE books_aat
  3. IS TABLE OF book%ROWTYPE
  4. INDEX BY PLS_INTEGER;
  5. books books_aat;
  6. BEGIN
  7. SELECT *
  8. BULK COLLECT INTO book
  9. FROM books
  10. WHERE title LIKE '%PL/SQL%';
  11. ...
  12. END;

類似地,FORALL將數據從一個PL/SQL集合傳送給指定的使用集合的表。下面的代碼實例給出一個過程,即接收書籍信息的一個嵌套表,並將該集合(綁定數組)的全部內容插入該書籍表中。注意,這個例子還利用了Oracle9i的FORALL的增強功能,可以將一條記錄直接插入到表中。BULK COLLECT和FORALL都非常有用,它們不僅提高了性能,而且還簡化了為PL/SQL中的SQL操作所編寫的代碼。下面的多行FORALL INSERT相當清楚地說明了為什麼PL/SQL被認為是Oracle數據庫的最佳編程語言。

  1. CREATE TYPE books_nt
  2. IS TABLE OF book%ROWTYPE;
  3. /
  4. CREATE OR REPLACE PROCEDURE add_books (
  5. books_in IN books_nt)
  6. IS
  7. BEGIN
  8. FORALL book_index
  9. IN books_in.FIRST .. books_in.LAST
  10. INSERT INTO book
  11. VALUES books_in(book_index);
  12. ...
  13. END;

不過在Oracle數據庫10g之前,以FORAll方式使用集合有一個重要的限制:該數據庫從IN范圍子句中的第一行到最後一行,依次讀取集合的內容。如果在該范圍內遇到一個未定義的行,Oracle數據庫將引發ORA-22160異常事件:ORA-22160: element at index [N] does notexist,對於FORALL的簡單應用,這一規則不會引起任何麻煩。但是,如果想盡可能地充分利用FORALL,那麼要求任意FORALL驅動數組都要依次填充可能會增加程序的復雜性並降低性能。

在Oracle數據庫10g中,PL/SQL現在在FORALL語句中提供了兩個新子句:INDICES OF與VALUES OF,它們使你能夠仔細選擇驅動數組中該由擴展DML語句來處理的行。

當綁定數組為稀疏數組或者包含有間隙時,INDICES OF會非常有用。該語句的語法結構為:

  1. FORALL indx IN INDICES
  2. OF sparse_collection
  3. INSERT INTO my_table
  4. VALUES sparse_collection (indx);

VALUES OF用於一種不同的情況:綁定數組可以是稀疏數組,也可以不是,但我只想使用該數組中元素的一個子集。那麼我就可以使用VALUES OF來指向我希望在DML操作中使用的值。該語句的語法結構為:

  1. FORALL indx IN VALUES OF pointer_array
  2. INSERT INTO my_table
  3. VALUES binding_array (indx);

不用FOR循環而改用FORALL

假定我需要編寫一個程序,對合格員工(由comp_analysis.is_eligible函數確定)加薪,編寫關於不符合加薪條件的員工的報告並寫入employee_history表。我在一個非常大的公司工作;我們的員工非常非常多。對於一位PL/SQL開發人員來說,這並不是一項十分困難的工作。我甚至不需要使用BULKCOLLECT或FORALL就可以完成這項工作,如清單1所示,我使用一個CURSORFOR循環和單獨的INSERT及UPDATE語句。這樣的代碼簡潔明了;不幸地是,我花了10分鐘來運行此代碼,我的"老式"方法要運行30分鐘或更長時間。

清單 1:

  1. CREATE OR REPLACE PROCEDUREgive_raises_in_department (
  2. dept_in IN employee.department_id%TYPE
  3. , newsal IN employee.salary%TYPE
  4. )
  5. IS
  6. CURSOR emp_cur
  7. IS
  8. SELECT employee_id, salary, hire_date
  9. FROM employee
  10. WHERE department_id = dept_in;
  11. BEGIN
  12. FOR emp_rec IN emp_cur
  13. LOOP
  14. IF comp_analysis.is_eligible (emp_rec.employee_id)
  15. THEN
  16. UPDATE employee
  17. SET salary = newsal
  18. WHERE employee_id =emp_rec.employee_id;
  19. ELSE
  20. INSERT INTO employee_history
  21. (employee_id, salary
  22. , hire_date, activity
  23. )
  24. VALUES (emp_rec.employee_id,emp_rec.salary
  25. , emp_rec.hire_date,'RAISE DENIED'
  26. );
  27. END IF;
  28. END LOOP;
  29. END give_raises_in_department;

好在我公司的數據庫升級到了Oracle9i,而且更幸運的是,在最近的Oracle研討會上(以及Oracle技術網站提供的非常不錯的演示中)我了解到了批量處理方法。所以我決定使用集合與批量處理方法重新編寫程序。寫好的程序如清單2所示。

清單 2:

  1. CREATE OR REPLACE PROCEDUREgive_raises_in_department (
  2. dept_in IN employee.department_id%TYPE
  3. ,newsal IN employee.salary%TYPE
  4. )
  5. IS
  6. TYPE employee_aat IS TABLE OF employee.employee_id%TYPE
  7. INDEX BY PLS_INTEGER;
  8. TYPE salary_aat IS TABLE OF employee.salary%TYPE
  9. INDEX BY PLS_INTEGER;
  10. TYPE hire_date_aat IS TABLE OF employee.hire_date%TYPE
  11. INDEX BY PLS_INTEGER;
  12. employee_ids employee_aat;
  13. salarIEs salary_aat;
  14. hire_dates hire_date_aat;
  15. approved_employee_ids employee_aat;
  16. denIEd_employee_ids employee_aat;
  17. denied_salarIEs salary_aat;
  18. denIEd_hire_dates hire_date_aat;
  19. PROCEDURE retrIEve_employee_info
  20. IS
  21. BEGIN
  22. SELECT employee_id, salary, hire_date
  23. BULK COLLECT INTO employee_ids, salarIEs, hire_dates
  24. FROM employee
  25. WHERE department_id = dept_in;
  26. END;
  27. PROCEDURE partition_by_eligibility
  28. IS
  29. BEGIN
  30. FOR indx IN employee_ids.FIRST .. employee_ids.LAST
  31. LOOP
  32. IF comp_analysis.is_eligible (employee_ids (indx))
  33. THEN
  34. approved_employee_ids (indx) :=employee_ids (indx);
  35. ELSE
  36. denIEd_employee_ids (indx) :=employee_ids (indx);
  37. denied_salarIEs (indx) :=salarIEs (indx);
  38. denIEd_hire_dates (indx) :=hire_dates (indx);
  39. END IF;
  40. END LOOP;
  41. END;
  42. PROCEDURE add_to_history
  43. IS
  44. BEGIN
  45. FORALL indx IN denied_employee_ids.FIRST .. denIEd_employee_ids.LAST
  46. INSERT INTO employee_history
  47. (employee_id
  48. , salary
  49. , hire_date, activity
  50. )
  51. VALUES (denIEd_employee_ids(indx)
  52. , denied_salarIEs (indx)
  53. , denied_hire_dates(indx), 'RAISE DENIED'
  54. );
  55. END;
  56. PROCEDURE give_the_raise
  57. IS
  58. BEGIN
  59. FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST
  60. UPDATE employee
  61. SET salary = newsal
  62. WHERE employee_id =approved_employee_ids (indx);
  63. END;
  64. BEGIN
  65. retrIEve_employee_info;
  66. partition_by_eligibility;
  67. add_to_history;
  68. give_the_raise;
  69. END give_raises_in_department;

掃一眼清單1 和清單2 就會清楚地認識到:改用集合和批量處理方法將增加代碼量和復雜性。但是,如果你需要大幅度提升性能,這還是值得的。下面,我們不看這些代碼,我們來看一看當使用FORALL時,用什麼來處理CURSORFOR循環內的條件邏輯。

定義集合類型與集合

在清單2中,聲明段的第一部分(第6行至第11行)定義了幾種不同的集合類型,與我將從員工表檢索出的列相對應。我更喜歡基於employee% ROWTYPE來聲明一個集合類型,但是FORALL還不支持對某些記錄集合的操作,在這樣的記錄中,我將引用個別字段。所以,我還必須為員工ID、薪金和雇用日期分別聲明其各自的集合。

接下來為每一列聲明所需的集合(第13行至第21行)。首先定義與所查詢列相對應的集合(第13行至第15行):

  1. employee_ids employee_aat;
  2. salarIEs salary_aat;
  3. hire_dates hire_date_aat;

然後我需要一個新的集合,用於存放已被批准加薪的員工的ID(第17行):approved_employee_ids employee_aat;

最後,我再為每一列聲明一個集合(第19行至第21行),用於記錄沒有加薪資格的員工:

  1. denIEd_employee_ids employee_aat;
  2. denied_salarIEs salary_aat;
  3. denIEd_hire_dates hire_date_aat;

關於Oracle數據庫的bulk collect用法之批量增刪改的相關操作就介紹到這裡了,希望本次的介紹能夠對您有所收獲!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved