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:

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


 

Anda dapat membantu situs ini dengan memberikan donasi kepada kami. Berapapun jumlahnya akan sangat berharga bagi kami. Salurkan donasi anda melalui halaman donasi. Terima kasih.

 

 

Find Related articles

 

 

Beranda

 

Berbagi Cerita

 

Langgam

 

Ngomong

 

Obsesi

 

Serasi

 

Comments

No comments yet.

Leave a comment

(required)

(required)


*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word