Flow Control in PL
Flow Control in PL
Flow Control in PL
DECLARE
numerator NUMBER;
denominator NUMBER;
the_ratio NUMBER;
lower_limit CONSTANT NUMBER := 0.72;
samp_num CONSTANT NUMBER := 132;
BEGIN
SELECT x, y INTO numerator, denominator FROM result_table
WHERE sample_id = samp_num;
the_ratio := numerator/denominator;
IF the_ratio > lower_limit THEN
INSERT INTO ratio VALUES (samp_num, the_ratio);
ELSE
INSERT INTO ratio VALUES (samp_num, -1);
END IF;
COMMIT;
EXCEPTION
WHEN ZERO_DIVIDE THEN
INSERT INTO ratio VALUES (samp_num, 0);
COMMIT;
WHEN OTHERS THEN
ROLLBACK;
END;
DECLARE
x NUMBER := 100;
BEGIN
FOR i IN 1..10 LOOP
IF MOD(i,2) = 0 THEN -- i is even
INSERT INTO temp VALUES (i, x, 'i is even');
ELSE
INSERT INTO temp VALUES (i, x, 'i is odd');
END IF;
x := x + 100;
END LOOP;
COMMIT;
END;
SQL> SELECT ename, empno, sal FROM emp ORDER BY sal DESC;
PL/SQL Block
-- available online in file 'sample2'
DECLARE
CURSOR c1 is
SELECT ename, empno, sal FROM emp
ORDER BY sal DESC; -- start with highest paid employee
my_ename VARCHAR2(10);
my_empno NUMBER(4);
my_sal NUMBER(7,2);
BEGIN
OPEN c1;
FOR i IN 1..5 LOOP
FETCH c1 INTO my_ename, my_empno, my_sal;
EXIT WHEN c1%NOTFOUND; /* in case the number requested */
/* is more than the total */
/* number of employees */
INSERT INTO temp VALUES (my_sal, my_empno, my_ename);
COMMIT;
END LOOP;
CLOSE c1;
END;
Output Table
SQL> SELECT * FROM temp ORDER BY col1 DESC;
NUM_COL1 NUM_COL2 CHAR_COL
-------- -------- --------
5000 7839 KING
3000 7902 FORD
3000 7788 SCOTT
2975 7566 JONES
2850 7698 BLAKE
3) DECLARE
x NUMBER := 0;
counter NUMBER := 0;
BEGIN
FOR i IN 1..4 LOOP
x := x + 1000;
counter := counter + 1;
INSERT INTO temp VALUES (x, counter, 'in OUTER loop');
/* start an inner block */
DECLARE
x NUMBER := 0; -- this is a local version of x
BEGIN
FOR i IN 1..4 LOOP
x := x + 1; -- this increments the local x
counter := counter + 1;
INSERT INTO temp VALUES (x, counter, 'inner loop');
END LOOP;
END;
END LOOP;
COMMIT;
END;
Output Table
SQL> SELECT * FROM temp ORDER BY col2;
4) DECLARE
qty_on_hand NUMBER(5);
BEGIN
SELECT quantity INTO qty_on_hand FROM inventory
WHERE product = 'TENNIS RACKET'
FOR UPDATE OF quantity;
IF qty_on_hand > 0 THEN -- check quantity
UPDATE inventory SET quantity = quantity - 1
WHERE product = 'TENNIS RACKET';
INSERT INTO purchase_record
VALUES ('Tennis racket purchased', SYSDATE);
ELSE
INSERT INTO purchase_record
VALUES ('Out of tennis rackets', SYSDATE);
END IF;
COMMIT;
END;
5)DECLARE
my_sal REAL(7,2);
PROCEDURE adjust_salary (emp_id INT, salary IN OUT REAL) IS ...
BEGIN
SELECT AVG(sal) INTO my_sal FROM emp;
adjust_salary(7788, my_sal); -- assigns a new value to my_sal
5) DECLARE
qty_on_hand NUMBER(5);
BEGIN
SELECT quantity INTO qty_on_hand FROM inventory
WHERE product = 'TENNIS RACKET'
FOR UPDATE OF quantity;
IF qty_on_hand > 0 THEN -- check quantity
UPDATE inventory SET quantity = quantity - 1
WHERE product = 'TENNIS RACKET';
INSERT INTO purchase_record
VALUES ('Tennis racket purchased', SYSDATE);
ELSE
INSERT INTO purchase_record
VALUES ('Out of tennis rackets', SYSDATE);
END IF;
COMMIT;
END;
6) SET SERVEROUTPUT ON
DECLARE
l_day VARCHAR2(10);
BEGIN
l_day := TRIM(TO_CHAR(SYSDATE, 'DAY'));
7)
SET SERVEROUTPUT ON
DECLARE
l_day VARCHAR2(10);
BEGIN
l_day := TRIM(TO_CHAR(SYSDATE, 'DAY'));
8) SET SERVEROUTPUT ON
DECLARE
l_day VARCHAR2(10);
BEGIN
l_day := TRIM(TO_CHAR(SYSDATE, 'DAY'));
CASE l_day
WHEN 'SATURDAY' THEN
DBMS_OUTPUT.put_line('The weekend has just started!');
WHEN 'SUNDAY' THEN
DBMS_OUTPUT.put_line('The weekend is nearly over!');
ELSE
DBMS_OUTPUT.put_line('It''s not the weekend yet!');
END CASE;
END;
/
9) SET SERVEROUTPUT ON
DECLARE
l_day VARCHAR2(10);
BEGIN
l_day := TRIM(TO_CHAR(SYSDATE, 'DAY'));
CASE
WHEN l_day = 'SATURDAY' THEN
DBMS_OUTPUT.put_line('The weekend has just started!');
WHEN l_day = 'SUNDAY' THEN
DBMS_OUTPUT.put_line('The weekend is nearly over!');
ELSE
DBMS_OUTPUT.put_line('It''s not the weekend yet!');
END CASE;
END;
/
10) SET SERVEROUTPUT ON
DECLARE
i NUMBER := 1;
BEGIN
LOOP
EXIT WHEN i > 5;
DBMS_OUTPUT.put_line(i);
i := i + 1;
END LOOP;
END;
/
11) SET SERVEROUTPUT ON
BEGIN
FOR i IN 1 .. 5 LOOP
DBMS_OUTPUT.put_line(i);
END LOOP;
END;
/
12) SET SERVEROUTPUT ON
DECLARE
i NUMBER := 1;
BEGIN
WHILE i <= 5 LOOP
DBMS_OUTPUT.put_line(i);
i := i + 1;
END LOOP;
END;
/
GOTO
The GOTO statement allows a program to branch unconditionally to a predefined label. The
following example uses the GOTO statement to repeat the functionality of the examples in the
previous section.
SET SERVEROUTPUT ON
DECLARE
i NUMBER := 1;
BEGIN
LOOP
IF i > 5 THEN
GOTO exit_from_loop;
END IF;
DBMS_OUTPUT.put_line(i);
i := i + 1;
END LOOP;
Inside a procedure, an IN parameter acts like a constant. So, you cannot assign it a
value. An OUT parameter acts like a local variable. So, you can change its value and
reference the value in any way. An IN OUT parameter acts like an initialized variable.
So, you can assign it a value, which can be assigned to another variable. For summary
information about the parameter modes, see Table 8-1.
Unlike OUT and IN OUT parameters, IN parameters can be initialized to default values.
For more information, see "Using Default Values for Subprogram Parameters".
Before exiting a procedure, explicitly assign values to all OUT formal parameters.
An OUT actual parameter can have a value before the subprogram is called. However,
when you call the subprogram, the value is lost unless you specify the compiler
hint NOCOPY or the subprogram exits with an unhandled exception.
You can write the procedure spec and body as a unit. Or, you can separate the
procedure spec from its body. That way, you can hide implementation details by
placing the procedure in a package. You can define procedures in a package body
without declaring their specs in the package spec. However, such procedures can be
called only from inside the package.
Examples
The following procedure debits a bank account:
PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS
old_balance REAL;
new_balance REAL;
overdrawn EXCEPTION;
BEGIN
SELECT bal INTO old_balance FROM accts WHERE acctno = acct_id;
new_balance := old_balance - amount;
IF new_balance < 0 THEN
RAISE overdrawn;
ELSE
UPDATE accts SET bal = new_balance WHERE acctno = acct_id;
END IF;
EXCEPTION
WHEN overdrawn THEN
...
END debit_account;
In the following example, you call the procedure using named notation:
debit_account(amount => 500, acct_id => 10261);
SET SERVEROUTPUT ON
EXECUTE display_numbers(2, 6);
2
3
4
5
6
Functions
DETERMINISTIC
This hint helps the optimizer avoid redundant function calls.
IN, OUT, IN OUT
These parameter modes define the behavior of formal parameters. An IN parameter
lets you pass values to the subprogram being called. An OUT parameter lets you return
values to the caller of the subprogram. An IN OUT parameter lets you pass initial values
to the subprogram being called and return updated values to the caller.
The following function returns the balance of a specified bank account:
FUNCTION balance (acct_id INTEGER) RETURN REAL IS
acct_bal REAL;
BEGIN
SELECT bal INTO acct_bal FROM accts WHERE acctno = acct_id;
RETURN acct_bal;
END balance;
FUNCTION compound (
years NUMBER,
amount NUMBER,
rate NUMBER) RETURN NUMBER IS
BEGIN
RETURN amount * POWER((rate / 100) + 1, years);
END compound;
p_lower IN NUMBER,
p_upper IN NUMBER)
RETURN NUMBER
AS
BEGIN
RETURN p_upper - p_lower;
END;
/
PRINT l_result
L_RESULT
----------
4