Các vòng lặp FOR và FORALL cũng không giao cho bạn nhiệm vụ quan ly index vòng lặp bởi vì index vòng lặp được thực thi một cách ngầm định và nằm bên ngoài phạm vi lập trình có thể truy c
Trang 1Bạn không thể giảm lượng các giá trị index bằng cách sử dụng các vòng lặp EFOR và FORALL Các vòng lặp FOR và FORALL cũng không giao cho bạn nhiệm vụ quan ly index vòng lặp bởi vì index vòng lặp được thực thi một cách ngầm định và nằm bên ngoài phạm vi lập trình có thể truy cập
€ó thể bỏ qua một giá trị index trong Oracle 11g bằng cách sử dụng câu lệnh CONTINUE mới Câu lệnh CONTINUE: báo hiệu một sự kết thúc tức thì một sự lặp lại vòng lặp và quay trở về câu lệnh đầu tiên trong vòng lặp
Các cau lạnh CONTINUE và CONTINUE WEEH là những tính năng Khối nặc danh sau đây minh hoạ cách thực thi một câu lệnh CON- TINUE trong một vòng lặp đơn giản:
Trang 2CONTINUE WHEN counter = 2;
dbms_output.put_line(‘index [' 11 counter 11‘) ‘);
Cau lénh print truéc d6 nim trong khéi ELSE CONTINUE WHEN loại bổ nhu cầu về khối IF
Trang 3Chương trình in kết quả này sang console sau hai bước đi qua vòng lặp:
iteration [1]
Vòng lặp đơn giản trở nên mạnh hơn khi được kết hợp với các thuộc tính cursor Điều này được thảo luận trong phần sau "Các cấu trúc cur- sor” trong chuong này
Vong lặp FOR là một vòng lặp ưa thích của nhiều nhà phát triển bởi
vì nó mạnh và đễ sử dụng Một vòng lặp FOR quan lý index vòng lặp và việc thoát cho bạn bởi vì nó là một phần của định nghĩa câu lệnh Có hai loại câu lệnh vòng lặp FOR Một là câu lệnh vòng lặp FOR dãy và một là câu lénh vong lap FOR cursor
Các câu lệnh vòng lặp FOR dãy
Một câu lệnh vòng lặp FOR dãy lý tưởng khi bạn biết điểm bắt đầu và điểm kết thúc và dãy (range) có thể được tượng trưng trong các số nguyên Bạn cũng có thể sử dụng một câu lệnh vòng lặp FOR để định hướng nội đung của một mảng kết hợp (associative array) bing cách truyền số phần tử trong đó Một ví dụ về việc định hướng một mảng kết hợp sử dụng một index chuỗi được cung cấp trong chương 7
Nguyên mẫu cho một câu lệnh vòng lặp FOR,dấy là
FOR range_index IN range_bottom range_top LOOP
BEGIN
FOR ¢ IN 1 3 LOOP
dbms_output.put_line(‘Iteration [' 11141 ']');
END LOOP;
Trang 4Không có câu lệnh exit trong ví dụ bởi vì một câu lệnh này không được bắt buộc Câu lệnh exit được đặt một cách ngầm định tại phần trên cùng của vòng lặp Logic điều kiện kiểm tra xem index dãy có lớn hơn phần trên cùng của dãy hay không và nó thoát khi điểu kiện đó không được đáp ứng Điều này có nghĩa nếu bạn đảo ngược đáy và đỉnh của dãy, vòng lặp sẽ thoát trước khi xử lý bất kỳ câu lệnh bởi vì nó tìm thấy 3 không nhỏ hơn 1 Do đó, một câu lệnh vòng lặp FOR day là một câu lệnh vòng lặp guard on entry
Các câu lệnh vòng lặp FOR cursor
Một câu lệnh vòng lặp FOR cursor lý tưởng khi bạn truy vấn một table hoặc view cơ sở đữ liệu Bạn không thực sự biết nó sẽ trả về bao nhiêu hàng
Phân này sử dụng một cursor ngâm định va phan sau "Các cấu trúc cursor" trình bày các vòng lặp FOR cursor với các cursor tường minh Một cursor ngầm định là một câu lệnh SELECT được định nghĩa là một phần của câu lệnh vòng lặp FOR cursor Một cursor tường minh được định nghĩa trong khối khai báo
Nguyên mẫu cho một câu lệnh vòng lặp FOR cursor là
FOR cursor_index IN {cursor_name[(actual_parameters)] | (select_statement)} LOOP repeating_statements,
END LOOP;
Cursor index có thể là bất kỳ định danh mà bạn thích hơn Như khi viết cho các vòng lặp trong những ngôn ngữ khác, nhiều nhà phát triển
sử dụng ¡ làm một tên biến, Sau đó, họ sử dụng j, k, L làm các tên biến
để xếp lễng các vòng lặp Index cursor cho một vòng lặp FOR cursor là một pointer dẫn sang một tập hợp kết quả trong một vùng làm việc query Một vùng làm việc query là một vùng bộ nhớ (được gọi là một ving ngif canh) trong Oracle 11g Database Process Global Area (PGA) Vùng làm việc query chứa thông tin vé query Ban sé tìm thấy các hàm được trả về bởi một query, số hàng được xử lý bởi query, và một pointer
Trang 5dẫn sang query được phân tích cú pháp trong vùng làm việc query Vùng làm việc query thường trú trong Oracle Shared Pool
Mã mẫu trình bày cách thực thi một vòng lặp PƠR cursor ngắm định
Nó phụ thuộc vào việc bạn đã chạy mã seeding hay chưa và trả về tên của các bộ phim Harry Potter trong cơ sở dữ liệu mẫu cửa hàng cho thuê video Sau day 1a vi du:
BEGIN
FOR i IN (SELECT COUNT(*) AS on_hand
item_title : item_rating FROM ¡em WHERE item_title LIKE ‘Harry Potter%' AND item_rating_agency = ‘MPAA’
GROUP BY item_title
item_rating) LOOP dbms_output.put(’(' | | ion_hand | 1’) *);
Harry Potter and the Sorcerer's Stone [PG]
(3) Harry Potter and the Goblet of Fire [PG-13]
(3) Harry Potter and the Chamber of Secrets [PG]
(2) Harry Potter and the Prisoner of Azkaban [PG]
(1) Harry Potter and the Order of the Phoenix [PG-13]
Cũng không có câu lệnh exit trong ví dụ, bởi vì một câu lệnh như vậy không được yêu cầu Câu lệnh exit được đặt một cách ngâm định ở phần trên cùng của vòng lặp Điều kiện exit kiểm tra xem tất cá hàng đã được đọc hay chưa Nó thoát khi không có thêm hàng nào để đọc
Các cursor tường minh có một số điểm khác biệt rõ rệt và một số điểm khác biệt tỉnh vi Phần sau "Các cấu trúc cursor" đễ cập đến các cursor tường minh trong các câu lệnh vòng lặp FOR cursor
Trang 6tác pâu lậnh vàng lặp WHILE
Các vòng lặp WHILE là các cấu trúc khối tường minh như các vòng lặp đơn gián (simple loop) Một vòng lặp WHILE bắt đầu và kết thúc bằng một từ dành riêng LOOP Như các vòng lặp đơn giản, các vòng lặp 'WHILE đồi hồi bạn quần lý cả index vòng lặp và tiêu chuẩn exit Vòng lặp WHILE là một vòng lặp guard on entry và có thể loại trừ một index vòng lặp bởi vì điêu kiện vào kiểm tra một biểu thức hoặc biến Boolean
khác một cách tường minh
Nguyên mẫu cho vòng lặp WHILE là
WHILE entry_condition LOOP
bỗ bởi vì bạn không còn cần bảo vệ việc khởi tạo bộ đếm nữa.
Trang 7Bạn nên chú ý rằng việc quần lý index vòng lặp là câu lệnh cuối cùng trong một vòng lặp WHILE Nó cuối cùng bởi vì tiêu chuẩn exit là thứ đầu tiên được lượng giá ở phần trên cùng của vòng lặp Điều này tạo ra một thách thức logic cho việc sử dụng một câu lệnh CONTINUE trong một vòng lặp WHILE bởi vì nó có thể bỏ qua logic index tăng lượng hoặc giảm lượng và tạo một vòng lặp vô hạn Một câu lệnh GOTO và một nhãn khối thực sự là một giải pháp tốt nhất cho vấn đề này được trình bày bởi câu lệnh CONTINUE
Đoạn mã sau đây hướng dẫn bạn cách sử dụng điều khiển có trình tự với câu lệnh GOTO và một nhãn khối:
ELSE dbms_output put_line(‘index {' 1! counter 1 '].’);
END IF;
<< loapindex >>
IF counter >= 1 THEN counter := counter + 1;
Các cấu trúc Cursor
Các cấu trúc cursor là các kết quả trả về từ những câu lệnh SQL SELECT Trong PL/8QL, bạn có thể xử lý các câu lệnh SELECT theo từng hàng hoặc dưới đạng các câu lệnh hàng loạt Phần này để cập cách làm việc với các cursor xử lý câu lệnh theo từng hàng Có hai loại cursor: ngắm định và tường minh Bạn tạo một cursor tường minh khi bạn định
Trang 8nghĩa một cursor bên trong một khối khai báo Các câu lệnh DML bên trong bất kỳ khối thực thi hoặc khối ngoại lệ là các cursor ngầm định Những câu lệnh này bao gồm các câu lệnh INSERT, UPDATE, và DE- LETE Bạn cũng tạo các cursor rigầm định bất cứ khi nào bạn sử dụng một câu lệnh SELECT với các mệnh do INTO hoặc BULK COLLECT INTO, hoặc bạn nhúng một câu lệnh SELECT bên trong một câu lệnh vòng lặp FOR cursor
Sự cân bằng của phần này thảo luận các cursor ngầm định và tường mỉnh một cách riêng biệt Các cursor ngầm định đứng trước tiên sau đó
là các cursor tường minh Việc xử lý hàng loạt được để cập trong phần tiếp theo
Ví dụ sau đây minh hoạ một thuộc tính cursor ROWCOUNT bằng cách sử dụng một cursor ngầm định một hàng trên giả table DUAL:
Có ba loại cursor ngầm định Một là cursor tập hợp hàng loạt ngầm định được để cập trong phần sau "các câu lệnh bulk" của chương Hai cursor ngầm định còn lại là chủ đê của phân này Chúng là các cursor ngầm định một hàng và nhiều hàng sử dụng các câu lệnh SELECT.
Trang 9Bang 4.6 Các thuậc tính cursor ngầm định
FOUND -Thudc tinh nay tra vé TRUE khi một câu
lệnh DML da thay déi một hàng, hoặc DQL
đã truy cập một hàng
ISOPEN Thuộc tính này luôn trả về EFALSE cho
bất kỳ cursor ngầm định
NOTFOUND “Thuộc tính này trả về TRUE khi một câu
lệnh DML đã thay đổi một hàng, hoặc một DQL không thể truy cập một hàng khác ROWCOUNT "Thuộc tính này trả về một số hàng thay
đổi bđi một câu lệnh DML hoặc số hàng được trả về bởi một câu lệnh SELECT TNTO
Các cursor ngầm định một hàng
Câu lệnh SELECT TNTO hiện điện trong tất cá cursor ngầm định vốn truy vấn dữ liệu Nó làm việc chỉ khi một hàng đơn được trả về bởi một câu lệnh select Bạn có thể chọn một cột hoặc danh sách cột trong mệnh
dé SELECT va gán các cột hàng vào các biến riêng lẻ hoặc chung vào một kiểu đữ liệu record
Nguyên mẫu cho một cursor ngầm định một hàng không có các mệnh
đề SQL WHERE, HAVING, GROUP BY, và ORDER BY chuẩn là
SELEGT colưmn1 [, column2 [, column(n+t ]]
INTO variable? [, variable2 {, variable(n+1) ]]
title item.item _title%TYPE;
subtitle item.item_ subtitle%TYPE;
BEGIN
SELECT item_id, item_title, item_subtitle
INTO id, title, subtitle
WHERE ROWNUM <2;
dbms_output.put_line(‘Selected [‘ | | title.1 1 ']);
Trang 10END;
/
Chương trình mẫu neo (anchor) tất cả biến vào bảng đích và giới hạn query chỉ trong một hàng bằng cách sử dụng giả cột Oracle SQL ROWNUM Nó in một hàng:
Selected [Around the World in 80 Days]
Việc gán một đối một làm cho việc gõ nhập trở nên rất phiền phức sau một khoảng thời gian Chúng cũng làm cho mã bảo trì tốn kém hơn theo thời gian Quy ước phổ biến hơn là gán các cột dưới dạng một nhóm vào các kiểu dữ liệu record
Ví dụ thứ hai gán các cột vào một kiểu đữ liệu record:
Các cursor ngầm định một hàng là những cách sửa chữa nhanh tuyệt vời nhưng có một điểm yếu Nó là một điểm yếu mà nhiều nhà phát triển
cố gắng triển khai bằng cách sử dụng nó để đưa ra các ngoại lệ khi các cursor trả về nhiều hàng Họ làm điều này bởi vì các cursor ngầm định một hàng đưa ra một lỗi "exact fetch returned too many rows" (ORA- 01422) khi trả về nhiễu hàng Có sẵn các giải pháp tốt hơn để phát hiện các lỗi trước khi truy tìm đữ liệu Bạn nên tự khám phá những lựa chọn khác khi triển khai mã và nơi có thể xử lý các lỗi một cách cụ thể, Các cursor tường minh thường là những giải pháp tốt hơn mỗi lần.
Trang 11Các cursor ngầm định nhiều hàng
Có hai cách để tạo các cursor ngầm định nhiều hàng Cách thứ nhất được thực hiện bằng cách viết bất kỳ câu lệnh DML trong một khối PL/ SQL Các câu lệnh DML được xem là các cursor ngầm định nhiều hàng mặc dù bạn có thể giới hạn chúng chỉ trong một hàng đơn Cách thứ hai
là viết một query nhúng trong một vòng lặp FOR cursor được định nghĩa trong một khối khai báo
Query sau đây mình hoạ một cursor ngầm định được tạo bởi một câu lệnh DML:
Updated [5]
Bạn cũng có thể định nghĩa các cursor ngẫm định nhiều hàng bên trong các câu lệnh vòng lặp FOR cursor Đây là những câu lệnh select có những tính năng tuyệt vời: tất cả biến được cung cấp ngầm định trong phạm vi của vòng lặp FOR cursor
Dòng mã sau đây minh hoạ một cursor ngầm định nhiều hàng trong một vòng lặp EOR cursor:
BEGIN
FOR i IN (SELECT item_id, item_titie FROM item) LOOP
dbms_output.put_line(‘Item #{' ! | Litemtd tl ‘J U1 iitem_title |! END LOOP;
Trang 12Cursor ngâm định này có sẵn trong phạm vi của index vòng lặp EOR cursor Cursor index cho vòng lặp FƠR cursor là một pointer dẫn sang một tập hợp kết quả trong một vùng làm việc query Vùng làm việc query là một vùng bộ nhớ (được gọi là vùng ngữ cảnh) trong Oracle 11g Database Process Global Area (PGA)
Bạn mở các cursor ngầm định tĩnh và động một cách khác nhau miễn
là chúng được định nghĩa bằng các tham số hình thức Khi chúng không
có các tham số hình thức, bạn mở chúng với cùng một cú pháp Các tham
số thực sự được ánh xạ bởi việc thay thế biến cục bộ
Các cursor tường mình đòi hỏi bạn mở, truy tìm (fetch), và đóng chúng cho dù bạn sử dụng vòng lặp đơn giản hoặc vòng lặp WHILE hoặc các câu lệnh vòng lặp FOR cursor Bạn sử dụng câu lệnh OPEN để mở các cursor, câu lệnh FETCH để truy tìm các record từ các cursor và câu lénh CLOSE để đóng và giải phóng các nguôn tài nguyên của các cursor Những câu lệnh này làm việc với các cursor động và tĩnh bên trong hoặc bên ngoài một cấu trúc vòng lặp Các câu lệnh vòng lặp FOR cursor mG, truy tìm và đóng các cursor cho bạn một cách ngắm định Các câu lệnh OPEN, FETCH, và CLOSE là những phần tử chính trong các mục "C/ - cursor tường minh tĩnh" và "Các cursor tường minh động"
Nguyên mẫu cho câu lệnh OPEN là
OPEN cursor_name [ (parameter1 [, parameter2 [, parameter (n+1) ] ] ) ] ;
Có hai nguyên mẫu cho câu lệnh FETCH Một nguyên mẫu gán các cột riêng lễ và các biến và các mẫu kia gán một hàng vào một cấu trúc record.
Trang 13Nguyên mẫu để gán các cột riêng lẻ vào các biến tương hợp là
FETCH cursor_name
INTO variabie1 [, variable? {, variable (n+1) J} ;
Nguyên mẫu để gán các hàng vào các biến cấu tric record Ja
gọi tham chiếu với các câu lệnh OPEN, FETCH, và CLOSE
Thuộc tính cursor FOUND báo hiệu rằng các hàng có sẵn để truy tìm
từ cursor và thuộc tính NOTFOUND báo hiệu rằng tất cả hàng đã được truy tìm từ cursor Thuộc tính ISOPEN cho biết cursor đã mở và là một điều nào đó mà bạn nên xem xét chạy trước khi cố mở một cursor Như các cursor ngầm định, thuộc tính ROWCOUNT cho biết bao nhiêu hàng
mà bạn đã truy tìm vào bất cứ thời điểm nào Chỉ thuộc tính cursor ISOPEN làm việc bất cứ lúc nào mà không gặp lỗi gì cả Ba thuộc tính kia đưa ra các lỗi khi cursor không mở Bảng 4.7 thu thập những hành
vi thay đổi này
Các ví dụ sử dụng các câu lệnh vòng lặp đơn giản, nhưng bạn cũng có thể sử dụng các cursor tường minh trong các câu lệnh vòng lặp WHILE hoặc được xếp lỗổng bên trong các vòng lặp FOR dãy và cursor Các cursor tĩnh và động được để cập trong các mục khác nhau để tổ chức các
ví dụ và làm nổi bật các điểm khác biệt
Các cursor tường mình fĩnh
Một cursor tường minh tĩnh là một câu lệnh SQL SELECT vốn không thay đổi hành vi của nó Một cursor tường minh có bốn thành phần: bạn định nghĩa, mở, truy tìm từ, và đóng một cursor Chương trình mẫu định nghĩa một cursor làm một câu lệnh SELECT truy vấn bảng ITEM Bảng
và đữ liệu được seed được phân phối trong mã có thể download
Trang 14Bảng 4.7 Các thuật tính cursor tường minh
Câu lệnh Trạng thái ‘FOUND $NOTFOUND %ISOPEN %ROWCOUNT OPEN Before Exception Exception FALSE Exception
1$ FETCH Before NULL NULL TRUE 9
After Exception Exception FALSE Exception Chương trình sau đây định nghĩa, mở, truy tìm từ, và đóng một cursor tĩnh vào một loạt các biến vô hướng:
BEGIN
OPEN c;
LOOP
FETCH ¢ INTO id, title;
EXIT WHEN c%NOTFOUND;
Trang 15nhưng ví dụ này muốn hiển thị cả hai Chương trình thoát khi không còn các record để truy tìm nữa và nó đã in các tiêu để sang console Chương trình này có thể được viết bằng cách sử dụng một câu lệnh vòng lặp FOR cursor Vong lap FOR tạo một cách ngâm định các biến
mà bạn có thể truy cập qua cursor index Điều này loại bỏ nhu câu bạn tạo chúng như được yêu cầu bởi câu lệnh vòng lặp đơn giản hoặc vòng lặp WHILE Câu lệnh vòng lặp FOR cursor cũng mở, truy tìm và đóng cur- sor một cách ngầm định như sau:
DEGLARE
CURSOR c IS
SELECT item_id AS id : item_title AS title FROM item;
có ngữ cảnh bên ngoài câu lệnh vòng lặp FOR Hạn chế này giới hạn cách bạn sử dụng các giá trị trả về từ một vòng lặp FOR cursor Cau lénh vòng lặp đơn giản hoặc vòng lặp WHILE là những giải pháp hiệu quả hơn khi bạn muốn gán những giá trị trả về và các biến vốn được trao đổi với các đơn vị chương trình khác Chương 6 thảo luận một số ưu điểm này trong khi để cập các hàm và thủ tục lưu trữ
Cú pháp câu lệnh FETCH thay thế để gán một hàng đữ liệu vào một kiểu đữ liệu record được minh hoạ trong chương trình tiếp theo Mọi thứ khác vẫn không đổi
Chương trình sau đây định nghĩa, mớ, truy tìm từ, và đóng một cursor tĩnh vào một cấu trúc record:
Trang 16
CURSOR c IS
SELECT item_id : item_title FROM item;
BEGIN
OPEN c;
LOOP
FETCH c INTO item;
EXIT WHEN c%NOTFOUND;
Dòng mã sau đây minh hoa việc gán một cấu trúc record từ cursor index:
Trang 17Trong cá hai ví dụ này, có thể cursor không tìm thấy bất kỳ record nào Khi một cursor ngầm định hoặc tường minh chạy và không có dữ liệu nào được tìm thấy, lỗi không được đưa ra Bạn cân xác định xem có record được tìm thấy hay không Điều này được thực hiện bằng cách sử dụng một câu lénh IF va cdc thuée tinh cursor NOTFOUND và ROWCOUNT
Chương trình sau đây in mét théng bdo no data found khi cursor không thể tìm thấy bất kỳ record nào bằng cách sử dụng một giá trị
TTEM_ID âm mà lẽ ra không nằm trong dữ liệu: