Struktur Kontrol PL/SQL
Pembahasan kali ini menunjukkan kepada kita bagaimana menyusun aliran kontrol melalui program PL/SQL. Kita akan mempelajari bagaimana perintah-perintah dihubungkan dengan struktur-struktur kontrol sederhana namun powerful, yang memiliki single entry dan exit point. Secara bersama-sama, struktur-struktur ini dapat menangani berbagai situasi. Penggunaan yang tepat akan menghasilkan program terstruktur yang baik.
Pembahasan kali ini mendiskusikan topik-topik berikut ini:
- Gambaran Struktur Kontrol PL/SQL
- Kontrol Kondisional: Perintah-perintah IF dan CASE
- Kontrol Perulangan: Perintah-perintah LOOP dan EXIT
- Kontrol Sekuensial: Perintah-perintah GOTO dan NULL
4.1. Gambaran Struktur Kontrol PL/SQL
Berdasarkan structure theorem, setiap program komputer dapat ditulis menggunakan struktur kontrol dasar yang ditunjukkan dalam Gambar 4-1. Mereka dapat dikombinasikan dalam suatu cara yang diperlukan untuk menjawab permasalahan-permasalahan yang ada.

Gambar 4-1 Struktur-struktur Kontrol
Struktur seleksi menguji kondisi, lalu mengeksekusi satu rangkaian perintah-perintah daripada lainnya, bergantung kepada kondisi mana yang true atau false. Suatu condition (kondisi) merupakan suatu variable atau ekpresi yang menghasilkan nilai Boolean (TRUE atau FALSE). Struktur perulangan mengeksekusi rangkaian perintah-perintah secara berulang-ulang selama suatu kondisi bernilai true. Struktur sekuensial (berurutan) secara sederhana mengeksekusi rangkaian perintah-perintah dalam urutan.
4.2. Struktur Kondisional: Perintah-perintah IF dan CASE
Seringkali, diperlukan untuk mengambil tindakan-tindakan alternatif bergantung kepada keadaan-keadaan yang ada. Perintah IF mengijinkan kita untuk mengeksekusi rangkaian perintah-perintah berdasarkan suatu kondisi. Dengan begitu, apakah suatu rangkaian perintah-perintah dieksekusi atau tidak, bergantung kepada nilai dari kondisi. Terdapat tiga bentuk perintah-perintah IF: IF-THEN, IF-THEN-ELSE, dan IF-THEN-ELSIF. Perintah CASE merupakan cara yang ringkas untuk mengevaluasi kondisi tunggal dan memiliki diantara banyak alternatif tindakan.
4.2.1. Perintah IF-THEN
Bentuk sederhana dari perintah IF menghubungkan kondisi dengan rangkaian perintah-perintah yang diapit oleh kata-kata kunci THEN dan END IF (bukan ENDIF), seperti berikut:
IF condition THEN sequence_of_statements END IF;
Rangkaian perintah-perintah dieksekusi hanya jika kondisi adalah true. Jika kondisi bernilai false atau null, perintah IF tidak melakukan apa-apa. Dalam salah satu kasus, kontrol berlalu kepada perintah selanjutnya. Contohnya:
IF sales > quota THEN compute_bonus(empid); UPDATE payroll SET pay = pay + bonus WHERE empno = emp_id; END IF;
Kita dapat meletakkan perintah-perintah IF yang pendek dalam satu baris tunggal, seperti
IF x > y THEN high := x; END IF;
4.2.2. Perintah IF-THEN-ELSE
Bentuk kedua dari perintah IF menambahkan kata kunci ELSE diikuti oleh rangkaian perintah-perintah alternatif, seperti berikut:
IF condition THEN sequence_of_statements1 ELSE sequence_of_statements2 END IF;
Rangkaian perintah-perintah dalam klausa ELSE dieksekusi hanya jika kondisi bernilai false atau null. Jadi, klausa ELSE memastikan bahwa rangkaian perintah-perintah tersebut dieksekusi. Dalam contoh berikut ini, perintah UPDATE pertama dieksekusi ketika kondisi bernilai true, namun perintah UPDATE kedua dieksekusi ketika kondisi bernilai false atau null:
IF trans_type = 'CR' THEN UPDATE accounts SET balance = balance + credit WHERE ... ELSE UPDATE accounts SET balance = balance - debit WHERE ... END IF;
Klausa-klausa THEN dan ELSE dapat mengandung perintah-perintah IF. Sehingga, perintah-perintah IF dapat bersarang, seperti ditunjukkan oleh contoh berikut ini:
IF trans_type = 'CR' THEN
UPDATE accounts SET balance = balance + credit WHERE ...
ELSE
IF new_balance >= minimum_balance THEN
UPDATE accounts SET balance = balance - debit WHERE ...
ELSE
RAISE insufficient_funds;
END IF;
END IF;
4.2.3. Perintah IF-THEN-ELSIF
Suatu ketika kita ingin memilih suatu tindakan dari beberapa alternatif yang terpisah satu sama lain. Bentuk ketiga dari perintah IF menggunakan kata kunci ELSIF (bukan ELSEIF) untuk memperkenalkan kondisi-kondisi tambahan, sebagai berikut:
IF condition1 THEN sequence_of_statements1 ELSIF condition2 THEN sequence_of_statements2 ELSE sequence_of_statements3 END IF;
Jika kondisi pertama bernilai false atau null, klausa ELSIF akan menguji kondisi lainnya. Perintah IF dapat memiliki sejumlah klausa ELSIF; klausa final ELSE bersifat opsional (bisa digunakan atau tidak). Kondisi-kondisi dievaluasi satu demi satu dari atas ke bawah. Jika suatu kondisi bernilai true, rangkaian perintah-perintah yang ada di dalamnya dieksekusi dan kontrol akan menuju ke perintah selanjutnya. Jika seluruh kondisi bernilai false atau null, maka rangkaian perintah-perintah di dalam klausa ELSE yang akan dieksekusi. Mari kita perhatikan contoh berikut ini:
BEGIN
...
IF sales > 50000 THEN
bonus := 1500;
ELSIF sales > 35000 THEN
bonus := 500;
ELSE
bonus := 100;
END IF;
INSERT INTO payroll VALUES (emp_id, bonus, ...);
END;
Jika nilai sales lebih besar dari 50000, kondisi pertama dan kedua bernilai true. Namun, bonus diberikan kepada nilai yang sesuai dari 1500 karena kondisi kedua tidak pernah diuji. Ketika kondisi pertama bernilai true, perintah-perintah di dalamnya dieksekusi dan kontrol menuju ke perintah INSERT.
4.2.4. Perintah CASE
Seperti halnya perintah IF, perintah CASE menyeleksi satu rangkaian perintah-perintah untuk dieksekusi. Namun, untuk menyeleksi rangkain perintah-perintah tersebut, perintah CASE menggunakan penyeleksi, bukannya menggunakan banyak ekspresi-ekspresi Boolean. (Pada pembahasan Dasar-dasar PL/SQL telah dijelaskan bahwa penyeleksi adalah ekspresi-ekspresi yang nilainya digunakan untuk menyeleksi satu dari beberapa alternatif yang ada). Untuk membandingkan perintah-perintah IF dan CASE, mari kita perhatikan kode program berikut ini yang menghasilkan deskripsi angka siswa sekolah:
IF grade = 'A' THEN
dbms_output.put_line('Excellent');
ELSIF grade = 'B' THEN
dbms_output.put_line('Very Good');
ELSIF grade = 'C' THEN
dbms_output.put_line('Good');
ELSIF grade = 'D' THEN
dbms_output. put_line('Fair');
ELSIF grade = 'F' THEN
dbms_output.put_line('Poor');
ELSE
dbms_output.put_line('No such grade');
END IF;
Perhatikan bahwa terdapat lima ekspresi Boolean. Dalam setiap langkah, kita menguji apakah variable yang sama, yaitu grade, sama dengan satu dari lima nilai: ‘A’, ‘B’, ‘C’, ‘D’, atau ‘E’. Mari kita tulis kembali kode diatas dengan menggunakan perintah IF, seperti berikut:
CASE grade
WHEN 'A' THEN dbms_output.put_line('Excellent');
WHEN 'B' THEN dbms_output.put_line('Very Good');
WHEN 'C' THEN dbms_output.put_line('Good');
WHEN 'D' THEN dbms_output.put_line('Fair');
WHEN 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
Perintah CASE lebih mudah dibaca dan lebih efisien. Jadi, jika memungkinkan, sebaiknya kita tulis ulang perintah-perintah IF-THEN-ELSIF dengan perintah-perintah CASE.
Perintah CASE diawali dengan kata kunci CASE. Kata kunci ini diikuti oleh penyeleksi, dimana pada contoh terakhir adalah variable grade. Ekspresi penyeleksi dapat menjadi kompleks. Sebagai contoh, ia dapat berisi pemanggilan function. Namun biasanya ia terdiri dari variable tunggal. Ekspresi penyeleksi dievaluasi hanya sekali. Nilai yan gdihasilkannya dapat merupakan tipe data PL/SQL selain BLOB, BFILE dan object type, PL/SQL record, index-by-table, varray, atau nested table.
Penyeleksi diikuti oleh satu atau lebih klausa WHEN, yang dicek secara berurutan. Nilai dari penyeleksi menentukan klausa mana yang akan dieksekusi. Jika nilai penyeleksi sama dengan nilai dari ekspresi klausa WHEN, maka klausa WHEN itulah yang akan dieksekusi. Pada contoh terakhir, jika grade sama dengan ‘C’, program menghasilkan ‘Good’. Jika suatu klausa WHEN dieksekusi, kontrol menuju ke perintah selanjutnya.
Klausa ELSE bekerja mirip dengan klausa ELSE di dalam perintah IF. Dalam contoh terakhir, jika grade tidak sama dengan nilai-nilai yang disediakan oleh klausa WHEN, maka klausa ELSE akan dipilih, dan outputnya adalah ‘No such grade’. Klausa ELSE bersifat opsional. Namun, jika kita mengabaikan klausa ELSE, PL/SQL menambahkan klausa ELSE implisit:
ELSE RAISE CASE_NOT_FOUND;
Jika perintah CASE memilih klausa ELSE implisit, maka PL/SQL menampilkan exception CASE_NOT_FOUND. Jadi, terdapat tindakan default, jika kita mengabaikan klausa ELSE.
Kata kunci END CASE menghentikan perintah CASE. Dua kata kunci ini harus dipisahkan oleh spasi. Perintah CASE memiliki bentuk sebagai berikut:
[<<label_name>>] CASE selector WHEN expression1 THEN sequence_of_statements1; WHEN expression2 THEN sequence_of_statements2; ... WHEN expressionN THEN sequence_of_statementsN; [ELSE sequence_of_statementsN+1;] END CASE [label_name];
Seperti halnya blok PL/SQL, perintah CASE dapat diberi label. Label, identifier tak terdeklarasi yang diapit oleh tanda ‘[<<’ dan ‘>>]’, harus berada pada awal perintah CASE. Dan secara opsional kita dapat juga menampilkan label pada akhir perintah CASE.
Berbagai exception yang muncul selama eksekusi perintah CASE ditangani dengan cara yang umum. Jadi, eksekusi normal dihentikan dan berpindah ke bagian exception-handling dari blok atau subprogram PL/SQL.
Alternatif terhadap perintah CASE adalah ekspresi CASE, dimana setiap klausa WHEN adalah merupakan ekspresi.
4.2.4.1. Searched CASE Statement
PL/SQL juga menyediakan perintah CASE searched, yang memiliki bentuk:
[<<label_name>>] CASE WHEN search_condition1 THEN sequence_of_statements1; WHEN search_condition2 THEN sequence_of_statements2; ... WHEN search_conditionN THEN sequence_of_statementsN; [ELSE sequence_of_statementsN+1;] END CASE [label_name];
Perintah CASE searched tidak memiliki penyeleksi. Juga, klausa WHEN-nya berisi kondisi pencarian yang menghasilkan nilai Boolean, bukan ekspresi-ekspresi yang dapat menghasilkan nilai dari suatu tipe. Contohnya sebagai berikut:
CASE
WHEN grade = 'A' THEN dbms_output.put_line('Excellent');
WHEN grade = 'B' THEN dbms_output.put_line('Very Good');
WHEN grade = 'C' THEN dbms_output.put_line('Good');
WHEN grade = 'D' THEN dbms_output.put_line('Fair');
WHEN grade = 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
Kondisi-kondisi pencarian dievaluasi secara berurutan. Nilai Boolean dari setiap kondisi pencarian menentukan klausa WHEN yang akan dieksekusi. Jika kondisi pencarian menghasilkan TRUE, klausa WHEN-nya dieksekusi. Jika suatu klausa WHEN dieksekusi, kontrol berjalan menuju perintah selanjutnya, sehingga kondisi-kondisi pencarian berikutnya tidak dievaluasi.
Jika tidak ada kondisi-kondisi pencarian yang menghasilkan TRUE, maka klausa ELSE dieksekusi. Klausa ELSE bersifat opsional. Namun, jika kita mengabaikan klausa ELSE, maka PL/SQL menambahkan klausa implisit ELSE berikut ini:
ELSE RAISE CASE_NOT_FOUND;
Exception yang muncul selama eksekusi perintah CASE searched ditangani dalam cara yang lazim. Sehingga, eksekusi normal dihentikan dan kontrol berpindah ke bagian exception-handling dari blok atau subprogram PL/SQL kita.
4.2.5. Pedoman untuk Perintah-perintah Kondisional PL/SQL
Hindari perintah-perintah IF yang janggal seperti contoh berikut ini:
IF new_balance < minimum_balance THEN overdrawn := TRUE; ELSE overdrawn := FALSE; END IF; ... IF overdrawn = TRUE THEN RAISE insufficient_funds; END IF;
Kode ini mengabaikan fakta-fakta yang berguna. Pertama, nilai ekspresi Boolean dapat langsung diberikan kepada variable Boolean. Jadi, kita dapat mengganti perintah IF pertama dengan pemberian nilai sederhana berikut ini:
overdrawn := new_balance < minimum_balance;
Kedua, variable Boolean itu sendiri bernilai true atau false. Jadi, kita dapat menyederhanakan kondisi dalam perintah IF yang kedua menjadi:
IF overdrawn THEN ...
Jika dimungkinkan, gunakan klausa ELSIF daripada perintah-perintah IF bersarang. Dengan demikian, kode program kit menjadi lebih mudah dibaca dan dimengerti. Bandingkan perintah-perintah IF berikut ini:
IF condition1 THEN | IF condition1 THEN
statement1; | statement1;
ELSE | ELSIF condition2 THEN
IF condition2 THEN | statement2;
statement2; | ELSIF condition3 THEN
ELSE | statement3;
IF condition3 THEN | END IF;
statement3; |
END IF; |
END IF; |
END IF; |
Perintah-perintah ini secara logikal sama, namun perintah pertama tidak jelas dalam hal aliran logikanya, dimana perintah kedua mengungkapnya.
Jika kita membandingkan ekspresi tunggal terhadap banyak nilai, kita dapat menyederhanakan logika dengan menggunakan perintah CASE tunggal daripada menggunakan IF dengan banyak klausa ELSIF.
4.3. Kontrol Perulangan: Perintah-perintah LOOP dan EXIT
Perintah-perintah LOOP mengijinkan kita untuk mengeksekusi rangkaian perintah-perintah beberapa kali. Terdapat tiga bentuk perintah-perintah LOOP: LOOP, WHILE-LOOP, dan FOR-LOOP.
4.3.1. LOOP
Bentuk paling sederhana dari perintah LOOP adalah loop dasar (tak berhingga), yang mengapit rangkaian perintah-perintah diantara kata-kata kunci LOOP dan END LOOP, seperti berikut ini:
LOOP sequence_of_statements END LOOP;
Dengan setiap perulangan dari setiap putaran, rangkaian perintah-perintah dieksekusi, lalu kontrol mulai lagi menuju ke awal putaran. Jika proses selanjutnya tidak diharapkan atau tidak dimungkinkan, kita dapat menggunakan perintah EXIT untuk mengakhiri putaran. Kita dapat meletakkan satu atau lebih perintah-perintah EXIT dimanapun di dalam putaran, namun tidak boleh di luar putaran. Terdapat dua bentuk perintah-perintah EXIT: EXIT dan EXIT-WHEN.
4.3.1.1. EXIT
Perintah EXIT memaksa loop berakhir danpa kondisi. Ketika perintah EXIT ditemukan, loop segera diakhiri dan kontrol berlanjut ke perintah selanjutnya. Contoh:
LOOP
...
IF credit_rating < 3 THEN
...
EXIT; -- exit loop immediately
END IF;
END LOOP;
-- kontrol mulai lagi dari sini
Contoh selanjutnya menunjukkan bahwa kita tidak dapat menggunakan perintah EXIT untuk mengakhiri blok PL/SQL:
BEGIN
...
IF credit_rating < 3 THEN
...
EXIT; -- tidak diperbolehkan
END IF;
END;
Perlu kita ingat, perintah EXIT harus diletakkan di dalam loop. Untuk menyelesaikan blok PL/SQL sebelum penyelesaian normal-nya terjadi, kita dapat menggunakan perintah RETURN.
4.3.1.2. EXIT-WHEN
Perintah EXIT-WHEN mengijinkan kita untuk menyelesaikan loop secara kondisional. Ketika perintah EXIT ditemukan, kondisi di dalam klausa WHEN dievaluasi. Jika kondisi bernilai true, loop diakhiri dan kontrol menuju ke perintah selanjutnya setelah loop. Contoh:
LOOP FETCH c1 INTO ... EXIT WHEN c1%NOTFOUND; -- keluar dari loop jika kondisi true ... END LOOP; CLOSE c1;
Sampai kondisi bernilai true, loop tidak dapat diselesaikan. Jadi, perintah di dalam loop harus mengubah nilai kondisi. Dalam contoh terakhir, jika perintah FETCH menghasilkan data, kondisi bernilai false. Ketika perintah FETCH gagal menghasilkan data, kondisi bernilai true, loop diselesaikan, dan kontrol menuju ke perintah CLOSE.
IF count > 100 THEN | EXIT WHEN count > 100; EXIT; | END IF; |
Perintah-perintah ini secara logikal adalah sama, namun perintah EXIT-WHEN lebih mudah dibaca dan dimengerti.
4.3.1.3. Label-label Loop
Seperti halnya blok PL/SQL, loop dapat diberi label. Label, identifier tidak terdeklarasi, yang diapit dengant tanda “<<” dan “>>”, harus muncul pada awal perintah LOOP, sebagai contoh:
<<label_name>> LOOP sequence_of_statements END LOOP;
Secara opsional, nama label juga dapat muncul pada akhir perintah LOOP, seperti ditunjukkan oleh contoh berikut:
<<my_loop>> LOOP ... END LOOP my_loop;
Ketika kita menyarangkan loop-loop berlabel, gunakan nama-nama label akhirnya untuk meningkatkan keterbacaan.
Dengan salah satu bentuk dari perintah EXIT, kita dapat menyelesaikan tidak hanya loop yang sedang berjalan, namun juga loop yang mengapitnya. Cukup berikan label pada loop yang mengapit yang ingin kita akhiri. Lalu, gunakan label di dalam perintah EXIT, seperti berikut ini:
<<outer>>
LOOP
...
LOOP
...
EXIT outer WHEN ... -- keluar dari kedua loop
END LOOP; ... END LOOP outer;
Setiap loop yang mengapit diatasnya dan terdapat di dialam loop berlabel akan diakhiri.
4.3.2. WHILE-LOOP
Perintah WHILE-LOOP menghubungkan kondisi dalam rangkaian perintah-perintah yang diapit oleh kata-kata kunci LOOP dan END LOOP, seperti berikut ini:
WHILE condition LOOP sequence_of_statements END LOOP;
Sebelum setiap perulangan dari loop, kondisi dievaluasi. Jika kondisi true, rangkaian perintah-perintah dieksekusi, kemudian kontrol kembali ke awal loop. Jika kondisi false atau null, loop diabaikan dan kontrol menuju ke perintah selanjutnya. Mari kita perhatikan contoh berikut ini:
WHILE total <= 25000 LOOP ... SELECT sal INTO salary FROM emp WHERE ... total := total + salary; END LOOP;
Jumlah perulangan bergantung pada kondisi dan jumlah tersebut tidak diketahui hingga loop selesai. Kondisi diuji pada awal loop, sehingga rangkaian perintah bisa saja dieksekusi sebanyak nol kali. Pada contoh terakhir, jika nilai awal total lebih besar dari 25000, kondisi bernilai false dan loop diabaikan.
Beberapa bahasa pemrograman memiliki struktur LOOP UNTIL atau REPEAT UNTIL, yang menguji kondisi pada dasar akhir loop, bukannya pada awal loop. Dengan demikian, rangkaian perintah-perintah akan dieksekusi paling tidak satu kali. PL/SQL tidak memiliki struktur seperti itu, namun kita dapat secara mudah membangunnya, seperti contoh berikut:
LOOP sequence_of_statements EXIT WHEN boolean_expression; END LOOP;
Untuk meyakinkan loop WHILE dieksekusi paling tidak satu kali, gunakan variable inisialisasi Boolean dalam kondisi, seperti berikut ini:
done := FALSE; WHILE NOT done LOOP sequence_of_statements done := boolean_expression; END LOOP;
Perintah di dalam loop harus memberikan nilai baru kepada variable Boolean. Jika tidak, maka kita akan memiliki loop tak berhingga (tiada akhir). Sebagai contoh, perintah-perintah LOOP berikut ini secara logikal adalah sama:
WHILE TRUE LOOP | LOOP ... | ... END LOOP; | END LOOP;
4.3.3. FOR-LOOP
Seperti telah kita pelajari sebelumnya, jumlah perulangan melalui loop WHILE tidak diketahui sampai loop berakhir, namun jumlah perulangan melalui loop FOR telah diketahui sebelum loop dijalankan. Loop-loop FOR berulang sejumlah jangkauan integer tertentu. Jangkauan ini adalah bagian dari iteration scheme, yang diapit oleh kata-kata kunci FOR dan LOOP. Tanda titik dua (..) bertindak sebagai operator jangkauan. Sintaksnya adalah sebagai berikut:
FOR counter IN [REVERSE] lower_bound..higher_bound LOOP sequence_of_statements END LOOP;
Jangkauan dievaluasi ketika loop FOR pertama kali dijalankan dan tidak pernah dievaluasi ulang.
Seperti ditunjukkan oleh contoh selanjutnya, rangkaian perintah-perintah dieksekusi sekali untuk integer dalam jangkauan tersebut. Setelah setiap perulangan, loop counter akan ditambah.
FOR i IN 1..3 LOOP –- memberikan nilai 1,2,3 kepada i sequence_of_statements –- dieksekusi tiga kali END LOOP;
Contoh berikut ini menunjukkan bahwa jika batas bawah sama dengan batas atas, rangkaian perintah-perintah dieksekusi satu kali:
FOR i IN 3..3 LOOP -- memberikan nilai 3 kepada i sequence_of_statements -- eksekusi satu kali END LOOP;
Secara default, perulangan diproses secara menaik mulai dari batas atas hingga batas bawah. Namun, seperti ditunjukkan oleh contoh dibawah ini, jika kita menggunakan kata kunci REVERSE, perulangan diproses secara menurun mulai dari batas atas menuju ke batas bawah. Setelah setiap perulangan (iterasi), loop counter dikurangi. Namun demikian, sebaiknya kita menulis jangkauan secara urutan menaik (bukan menurun).
FOR i IN REVERSE 1..3 LOOP -- memberikan nilai 3,2,1 kepada i sequence_of_statements -- eksekusi tiga kali END LOOP;
Dalam loop FOR, loop counter dapat direferensi seperti konstanta namun tidak dapat diberikan nilai, seperti ditunjukkan oleh contoh berikut ini:
FOR ctr IN 1..10 LOOP
IF NOT finished THEN
INSERT INTO ... VALUES (ctr, ...); -- diperbolehkan
factor := ctr * 2; -- diperbolehkan
ELSE
ctr := 10; -- tidak diperbolehkan
END IF;
END LOOP;
4.3.3.1. Skema Iterasi
Jangkauan batas dari loop dapat berupa literal-literal, variable-variable, atau ekspresi-ekspresi tetapi harus mengevaluasi angka. Jika tidak, PL/SQL memunculkan pesan kesalahan (exception) VALUE_ERROR. Batas atas perlu diawali dengan 1, seperti ditunjukkan oleh contoh berikut ini. Akan tetapi, penambahan (atau pengurangan)
loop counter harus 1.
j IN -5..5 k IN REVERSE first..last step IN 0..TRUNC(high/low) * 2
Secara internal, PL/SQL memberikan nilai-nilai dari batas tersebut ke variable-variable sementara PLS_INTEGER, dan, jika perlu, membulatkan nilai-nilai tersebut ke integer terdekat. Besarnya jangkauan dari PLS_INTEGER adalah -2**31..2**31. Sehingga, jika batas mengevaluasi angka diluar jangkauan tersebut, kita akan mendapatkan pesan kesalahan numeric overflow ketika PL/SQL mengusahakan pemberikan nilai, seperti ditunjukkan oleh contoh berikut ini:
DECLARE
hi NUMBER := 2**32;
BEGIN
FOR j IN 1..hi LOOP -- menyebabkan error 'numeric overflow'
...
END LOOP;
END;
Beberapa bahasa pemrograman menyediakan klausa STEP, yang mengijinkan kita untuk menentukan penambahan yang berbeda (misalnya 5, bukan 1). PL/SQL tidak memiliki struktur seperti itu, namun kita dapat dengan mudah membuatnya. Di dalam loop FOR, cukup kalikan setiap referensi terhadap loop counter tersebut dengan penambahan yang baru. Dalam contoh berikut ini, kita memberikan tanggal hari ini kepada elemen 5, 10, dan 15 dari index-by table:
DECLARE
TYPE DateList IS TABLE OF DATE INDEX BY BINARY_INTEGER;
dates DateList;
k CONSTANT INTEGER := 5; -- mengatur penambahan baru
BEGIN
FOR j IN 1..3 LOOP
dates(j*k) := SYSDATE; -- mengalikan loop counter dengan nilai penambahan
END LOOP;
...
END;
4.3.3.2. Jangkauan Dinamis
PL/SQL mengijinkan kita untuk menentukan jangkauan loop secara dinamis saat runtime, seperti contoh berikut ini:
SELECT COUNT(empno) INTO emp_count FROM emp; FOR i IN 1..emp_count LOOP ... END LOOP;
Nilai emp_count tidak diketahui saat kompilasi; perintah SELECT menampilkan nilainya saat runtime.
Apa yang terjadi jika batas bawah perulangan lebih besar dari batas atasnya? Seperti pada contoh berikut ini, rangkaian perintah-perintah didalam loop tidak dieksekusi dan kontrol menuju ke perintah berikutnya:
-- nilai limit menjadi 1 FOR i IN 2..limit LOOP sequence_of_statements -- eksekusi nol kali END LOOP; -- kontrol berjalan disini
4.3.3.3. Aturan-aturan Jangkauan
Loop counter didefinisikan hanya untuk loop. Kita tidak dapat mereferensinya di luar loop. Setelah loop selesai, loop counter menjadi tidak terdefinisi, seperti contoh berikut ini:
FOR ctr IN 1..10 LOOP ... END LOOP; sum := ctr - 1; -- tidak diperbolehkan
Kita tidak perlu secara eksplisit mendeklarasikan loop counter karena ia secara implisit dideklarasikan sebagai variable lokal bertipe INTEGER. Contoh berikut menunjukkan bahwa deklarasi menyembunyikan deklarasi global:
DECLARE
ctr INTEGER;
BEGIN
...
FOR ctr IN 1..25 LOOP
...
IF ctr > 10 THEN ... -- mereferensi kepada loop counter
END LOOP;
END;
Untuk mereferensi variable global dalam contoh ini, kita harus menggunakan label dan notasi titik, seperti berikut ini:
<<main>>
DECLARE
ctr INTEGER;
...
BEGIN
...
FOR ctr IN 1..25 LOOP
...
IF main.ctr > 10 THEN –- mereferensi kepada to variable global
...
END IF;
END LOOP;
END main;
Aturan-aturan jangkauan yang sama diaplikasikan kepada loop-loop FOR bersarang. Mari kita perhatikan contoh dibawah ini. Kedua loop counter memiliki nama yang sama. Jadi, untuk mereferensi kepada loop counter yang berada diluar dari dalam loop, kita harus menggunakan label dan notasi titik, seperti contoh berikut:
<<outer>>
FOR step IN 1..25 LOOP
FOR step IN 1..10 LOOP
...
IF outer.step > 15 THEN ...
END LOOP;
END LOOP outer;
4.3.3.4. Menggunakan Perintah EXIT
Perintah EXIT mengijinkan loop FOR diselesaikan sebelum waktunya. Sebagai contoh, loop berikut ini secara normal dieksekusi sepuluh kali, namun segera setelah perintah FETCH gagal menghasilkan data, loop diakhiri tidak peduli berapa kali ia telah dieksekusi:
FOR j IN 1..10 LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; ... END LOOP;
Umpamakan kita harus keluar dari loop FOR bersarang sebelum waktunya. Kita dapat mengakhiri tidak hanya loop yang sedang berjalan, namun juga loop yang mengapitnya. Cukup berikan label terhadap loop yang diapit yang ingin kita akhiri. Kemudian, gunakan label dalam perintah EXIT untuk menentukan loop FOR yang mana yang akan diselesaikan, seperti contoh berikut ini:
<<outer>>
FOR i IN 1..5 LOOP
...
FOR j IN 1..10 LOOP
FETCH c1 INTO emp_rec;
EXIT outer WHEN c1%NOTFOUND; -- keluar dari kedua loop FOR
...
END LOOP;
END LOOP outer;
-- kontrol berjalan disini
4.4.1. Kontrol Sekuensial: Perintah-perintah GOTO dan NULL
Tidak seperti perintah-perintah IF dan LOOP, perintah-perintah GOTO dan NULL tidak begitu penting untuk pemrograman PL/SQL. Struktur PL/SQL telah berbentuk seperti yang dibutuhkan oleh perintah GOTO. Terkadang, ia dapat cukup menyederhanakan logika untuk menjamin penggunaannya. Perintah NULL dapat meningkatkan keterbacaan dengan membuat arti dan aksi dari perintah-perintah berkondisi menjadi lebih jelas.
Penggunaan perintah-perintah GOTO yang berlebihan dapat menghasilkan kode yang kompleks dan tidak terstruktur (kadangkala disebut spaghetti code) yang sulit dimengerti dan dipelihara. Jadi, gunakan perintah-perintah GOTO dengan hemat. Sebagai contoh, untuk bercabang dari struktur bersarang yang dalam kepada rutin error-handling, munculkan exception dibandingkan menggunakan perintah GOTO.
4.4.1.1. Perintah GOTO
Perintah GOTO mencabang label tanpa adanya kondisi tertentu. Label ahrus unik di dalam jangkauannya dan harus mendahului perintah executable atau blok PL/SQL. Ketika dieksekusi, perintah GOTO mentransfer kontrol ke perintah label atau blok. Dalam contoh berikut ini, kita menuju ke perintah executable turun lebih jauh di dalam rangkaian perintah-perintah:
BEGIN ... GOTO insert_row; ... <<insert_row>> INSERT INTO emp VALUES ... END;
Dalam contoh selanjutnya, kita menuju ke blok PL/SQL jauh diatas rangkaian perintah-perintah:
BEGIN
...
<<update_row>>
BEGIN
UPDATE emp SET ...
...
END;
...
GOTO update_row;
...
END;
Label end_loop dalam contoh selanjutnya tidak diperbolehkan karena ia tidak mendahului perintah executable:
DECLARE
done BOOLEAN;
BEGIN
...
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
...
<<end_loop>> -- tidak diperbolehkan
END LOOP; -- bukan perintah executable
END;
Untuk men-debug contoh terakhir, cukup tambahkan perintah NULL, seperti berikut ini:
FOR i IN 1..50 LOOP
IF done THEN
GOTO end_loop;
END IF;
...
<<end_loop>>
NULL; -- perintah executable
END LOOP;
Seperti contoh selanjutnya, perintah GOTO dapat mencabang ke blok yang mengapitnya dari blok yang memanggilnya:
DECLARE
my_ename CHAR(10);
BEGIN
<<get_name>>
SELECT ename INTO my_ename FROM emp WHERE ...
BEGIN
...
GOTO get_name; -- mencabang ke blok yang mengapitnya
END;
END;
Perintah GOTO mencabang ke blok pertama yang mengapitnya dimana label yang direferensinya berada.
4.4.1.2. Batasan-batasan
Beberapa maksud dari perintah GOTO tidak diperbolehkan. Secara spesifik, perintah GOTO tidak dapat mencabang ke dalam perintah IF, perintah CASE, perintah LOOP, dan sub-blok. Sebagai contoh, perintah GOTO berikut ini tidak diperbolehkan:
BEGIN
...
GOTO update_row; -- tidak dapat mencabang ke perintah IF
...
IF valid THEN
...
<<update_row>>
UPDATE emp SET ...
END IF;
END;
Seperti ditunjukkan oleh contoh di bawah ini, perintah GOTO tidak dapat mencabang dari satu klausa perintah IF ke lainnya. Demikian juga, perintah GOTO tidak dapat mencabang dari satu perintah CASE klausa WHEN ke lainnya.
BEGIN
...
IF valid THEN
...
GOTO update_row; -- tidak dapat mencabang ke klausa ELSE
ELSE
...
<<update_row>>
UPDATE emp SET ...
END IF;
END;
Contoh berikutnya menunjukkan bahwa perintah GOTO tidak dapat mencabang dari blok yang mengapitnya ke sub-blok:
BEGIN
...
IF status = 'OBSOLETE' THEN
GOTO delete_part; -- tidak dapat mencabang ke sub-blok
END IF;
...
BEGIN
...
<<delete_part>>
DELETE FROM parts WHERE ...
END;
END;
Juga, perintah GOTO tidak dapat mencabang keluar dari subprogram, seperti ditunjukkan oleh contoh dibawah ini:
DECLARE ... PROCEDURE compute_bonus (emp_id NUMBER) IS BEGIN ... GOTO update_row; -- tidak dapat mencabang keluar dari subprogram END; BEGIN ... <<update_row>> UPDATE emp SET ... END;
Akhirnya, perintah GOTO tidak dapat mencabang dari satu penanganan kesalahan (exception handler) ke blok terkini. Sebagai contoh, perintah GOTO berikut ini tidak diperbolehkan:
DECLARE ... pe_ratio REAL; BEGIN ... SELECT price / NVL(earnings, 0) INTO pe_ratio FROM ... <<insert_row>> INSERT INTO stats VALUES (pe_ratio, ...); EXCEPTION WHEN ZERO_DIVIDE THEN pe_ratio := 0; GOTO insert_row; -- tidak dapat mencabang ke blok terkini END;
Namun, perintah GOTO dapat mencabang dari exception handler ke blok yang mengapitnya.
4.4.2. Perintah NULL
Perintah NULL tidak melakukan apapun kecuali hanya melewatkan kontrol ke perintah selanjutnya. Dalam konstruksi kondisional, perintah NULL memberitahu pembaca bahwa sebuah kemungkinan telah dipertimbangkan, namun tidak ada tindakan yang perlu dilakukan. Dalam contoh berikut ini, perintah NULL menunjukkan bahwa tidak ada tindakan yang diambil untuk exception-exception tak bernama:
EXCEPTION WHEN ZERO_DIVIDE THEN ROLLBACK; WHEN VALUE_ERROR THEN INSERT INTO errors VALUES ... COMMIT; WHEN OTHERS THEN NULL; END;
Dalam perintah-perintah IF atau tempat-tempat lain yang membutuhkan paling tidak satu perintah executable, perintah NULL digunakan untuk memenuhi sintaks. Dalam contoh berikut ini, perintah NULL menekankan bahwa hanya karyawan yang memiliki rate tinggi sajalah yang mendapatkan bonus:
IF rating > 90 THEN compute_bonus(emp_id); ELSE NULL; END IF;
Juga, perintah NULL merupakan jalan yang berguna untuk menciptakan potongan-potongan ketika mendesain aplikasi-aplikasi dari atas ke bawah. Stub (potongan) adalah subprogram kosong yang mengijinkan kita untuk menunda definisi procedure atau function-nya sampai kita menguji dan men-debug program utama. Dalam contoh berikut ini, perintah NULL memenuhi kebutuhan bahwa paling tidak terdapat satu perintah yang harus muncul dalam bagian executable dari subprogram:
PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS BEGIN NULL; END debit_account;
Silahkan melanjutkan membaca pembahasan Collections dan Records PL/SQL
Find Related articles
Beranda
Berbagi Cerita
Langgam
Ngomong
Obsesi
Serasi






































Comments
No comments yet.
Leave a comment