13.4. Basic Statements

This section begins the discussion of the programming statements that can be used in an EDB-SPL program.

13.4.1. NULL

The simplest statement is the NULL statement. This statement is an executable statement that does nothing.

NULL;

The following is the simplest, possible valid EDB-SPL program.

BEGIN
    NULL;
END;

The NULL statement can act as a placeholder where an executable statement is required such as in a branch of an IF-THEN-ELSE statement.

For example:

CREATE OR REPLACE PROCEDURE divide_it (
    p_numerator     IN  NUMBER,
    p_denominator   IN  NUMBER,
    p_result        OUT NUMBER
)
IS
BEGIN
    IF p_denominator = 0 THEN
        NULL;
    ELSE
        p_result := p_numerator / p_denominator;
    END IF;
END;

13.4.2. Assignment

The assignment statement sets a variable or a formal parameter of mode OUT or IN OUT specified on the left side of the assignment, :=, to the evaluated expression specified on the right side of the assignment.

variable := expression;

variable is an identifier for a previously declared variable, OUT formal parameter, or IN OUT formal parameter. expression is an expression that produces a single value. The value produced by the expression must have a compatible data type with that of variable. See Chapter 9 for a discussion of data type compatibility and conversion.

While the dept_salary_rpt example in Section 13.3 showed assignment statements used in variable declarations, a variation of this example shows the typical use of assignment statements in the executable section of the procedure.

CREATE OR REPLACE PROCEDURE dept_salary_rpt (
    p_deptno        NUMBER
)
IS
    todays_date     DATE;
    rpt_title       VARCHAR2(40);
    base_sal        INTEGER;
    base_comm_rate  NUMBER;
    base_annual     NUMBER;
BEGIN
    todays_date := SYSDATE;
    rpt_title := 'Report For Department # ' || p_deptno || ' on ' || todays_date;
    base_sal := 35525;
    base_comm_rate := 1.33333;
    base_annual := ROUND(base_sal * base_comm_rate, 2);

    DBMS_OUTPUT.PUT_LINE(rpt_title);
    DBMS_OUTPUT.PUT_LINE('Base Annual Salary: ' || base_annual);
END;

13.4.3. SELECT INTO

The SELECT INTO statement is an EDB-SPL variation of the SQL SELECT command, the differences being:

Other than the above, all of the clauses of the SELECT command such as WHERE, ORDER BY, GROUP BY, HAVING, etc. are valid for SELECT INTO. The following are the two variations of SELECT INTO.

SELECT select_expressions INTO target FROM ...;

target is a comma-separated list of simple variables. select_expressions and the remainder of the statement are the same as for the SQL SELECT command. See Chapter 6 for a discussion of the SELECT command. The selected values must exactly match in data type, number, and order the structure of the target or a run-time error occurs.

SELECT * INTO record FROM table ...;

record is a record that has previously been declared using table%ROWTYPE where table is the table referenced in the FROM table clause.

If the query returns zero rows, null values are assigned to the target(s). If the query returns multiple rows, the first row is assigned to the target(s) and the rest are discarded. (Note that "the first row" is not well-defined unless you've used ORDER BY.)

Note: In both cases, where no row is returned or more than one row is returned, EDB-SPL throws an exception.

You can use the WHEN NO_DATA_FOUND clause in an EXCEPTION block to determine whether the assignment was successful (that is, at least one row was returned by the query).

This version of the emp_sal_query procedure uses the variation of SELECT INTO that returns the result set into a record. Also note the addition of the EXCEPTION block containing the WHEN NO_DATA_FOUND conditional expression.

CREATE OR REPLACE PROCEDURE emp_sal_query (
    p_empno         IN emp.empno%TYPE
)
IS
    r_emp           emp%ROWTYPE;
    v_avgsal        emp.sal%TYPE;
BEGIN
    SELECT * INTO r_emp
        FROM emp WHERE empno = p_empno;
    DBMS_OUTPUT.PUT_LINE('Employee # : ' || p_empno);
    DBMS_OUTPUT.PUT_LINE('Name       : ' || r_emp.ename);
    DBMS_OUTPUT.PUT_LINE('Job        : ' || r_emp.job);
    DBMS_OUTPUT.PUT_LINE('Hire Date  : ' || r_emp.hiredate);
    DBMS_OUTPUT.PUT_LINE('Salary     : ' || r_emp.sal);
    DBMS_OUTPUT.PUT_LINE('Dept #     : ' || r_emp.deptno);

    SELECT AVG(sal) INTO v_avgsal
        FROM emp WHERE deptno = r_emp.deptno;
    IF r_emp.sal > v_avgsal THEN
        DBMS_OUTPUT.PUT_LINE('Employee''s salary is more than the department average of '
            || v_avgsal);
    ELSE
        DBMS_OUTPUT.PUT_LINE('Employee''s salary does not exceed the department average of '
            || v_avgsal);
    END IF;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Employee # ' || p_empno || ' not found');
END;

If the query is executed with a non-existent employee number the results appear as follows.

EXEC emp_sal_query(0);

Employee # 0 not found

Another conditional clause of use in the EXCEPTION section with SELECT INTO is the TOO_MANY_ROWS exception. If more than one row is selected by the SELECT INTO statement an exception is thrown by EDB-SPL.

When the following block is executed, the TOO_MANY_ROWS exception is thrown since there are many employees in the specified department.

DECLARE
    v_ename         emp.ename%TYPE;
BEGIN
    SELECT ename INTO v_ename FROM emp WHERE deptno = 20 ORDER BY ename;
EXCEPTION
    WHEN TOO_MANY_ROWS THEN
        DBMS_OUTPUT.PUT_LINE('More than one employee found');
        DBMS_OUTPUT.PUT_LINE('First employee returned is ' || v_ename);
END;

More than one employee found
First employee returned is ADAMS

Note: See Section 13.5.5 for more information on exception handling.

13.4.4. INSERT

The INSERT command available in the SQL language can also be used in EDB-SPL programs.

An expression in the EDB-SPL language can be used wherever an expression is allowed in the SQL INSERT command. Thus, EDB-SPL variables and parameters can be used to supply values to the insert operation.

The following is an example of a procedure that performs an insert of a new employee using data passed from a calling program.

CREATE OR REPLACE PROCEDURE emp_insert (
    p_empno         IN emp.empno%TYPE,
    p_ename         IN emp.ename%TYPE,
    p_job           IN emp.job%TYPE,
    p_mgr           IN emp.mgr%TYPE,
    p_hiredate      IN emp.hiredate%TYPE,
    p_sal           IN emp.sal%TYPE,
    p_comm          IN emp.comm%TYPE,
    p_deptno        IN emp.deptno%TYPE
)
IS
BEGIN
    INSERT INTO emp VALUES (
        p_empno,
        p_ename,
        p_job,
        p_mgr,
        p_hiredate,
        p_sal,
        p_comm,
        p_deptno);

    DBMS_OUTPUT.PUT_LINE('Added employee...');
    DBMS_OUTPUT.PUT_LINE('Employee # : ' || p_empno);
    DBMS_OUTPUT.PUT_LINE('Name       : ' || p_ename);
    DBMS_OUTPUT.PUT_LINE('Job        : ' || p_job);
    DBMS_OUTPUT.PUT_LINE('Manager    : ' || p_mgr);
    DBMS_OUTPUT.PUT_LINE('Hire Date  : ' || p_hiredate);
    DBMS_OUTPUT.PUT_LINE('Salary     : ' || p_sal);
    DBMS_OUTPUT.PUT_LINE('Commission : ' || p_comm);
    DBMS_OUTPUT.PUT_LINE('Dept #     : ' || p_deptno);
    DBMS_OUTPUT.PUT_LINE('----------------------');
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('OTHERS exception on INSERT of employee # ' || p_empno);
        DBMS_OUTPUT.PUT_LINE('SQLCODE : ' || SQLCODE);
        DBMS_OUTPUT.PUT_LINE('SQLERRM : ' || SQLERRM);
END;

If an exception occurs all database changes made in the procedure are automatically rolled back. In this example the EXCEPTION section with the WHEN OTHERS clause catches all exceptions. Two variables are displayed. SQLCODE is a number that identifies the specific exception that occurred. SQLERRM is a text message explaining the error. See Section 13.5.5 for more information on exception handling.

The following shows the output when this procedure is executed.

EXEC emp_insert(9503,'PETERSON','ANALYST',7902,'31-MAR-05',5000,NULL,40);

Added employee...
Employee # : 9503
Name       : PETERSON
Job        : ANALYST
Manager    : 7902
Hire Date  : 31-MAR-05
Salary     : 5000
Commission :
Dept #     : 40
----------------------

SELECT * FROM emp WHERE empno = 9503;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      9503 PETERSON   ANALYST         7902 31-MAR-05       5000                    40

See Section 5.1 and INSERT for more information on the SQL INSERT command.

13.4.5. UPDATE

The UPDATE command available in the SQL language can also be used in EDB-SPL programs.

An expression in the EDB-SPL language can be used wherever an expression is allowed in the SQL UPDATE command. Thus, EDB-SPL variables and parameters can be used to supply values to the update operation.

CREATE OR REPLACE PROCEDURE emp_comp_update (
    p_empno         IN emp.empno%TYPE,
    p_sal           IN emp.sal%TYPE,
    p_comm          IN emp.comm%TYPE
)
IS
BEGIN
    UPDATE emp SET sal = p_sal, comm = p_comm WHERE empno = p_empno;

    IF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Updated Employee # : ' || p_empno);
        DBMS_OUTPUT.PUT_LINE('New Salary         : ' || p_sal);
        DBMS_OUTPUT.PUT_LINE('New Commission     : ' || p_comm);
    ELSE
        DBMS_OUTPUT.PUT_LINE('Employee # ' || p_empno || ' not found');
    END IF;
END;

The SQL%FOUND conditional expression returns "true" if a row is updated, "false" otherwise. See Section 13.4.7 for a discussion of SQL%FOUND and other similar expressions.

The following shows the update on the employee using this procedure.

EXEC emp_comp_update(9503, 6540, 1200);

Updated Employee # : 9503
New Salary         : 6540
New Commission     : 1200

SELECT * FROM emp WHERE empno = 9503;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      9503 PETERSON   ANALYST         7902 31-MAR-05       6540       1200         40

See Section 5.2 and UPDATE for information on the SQL UPDATE command.

13.4.6. DELETE

The DELETE command available in the SQL language can also be used in EDB-SPL programs.

An expression in the EDB-SPL language can be used wherever an expression is allowed in the SQL DELETE command. Thus, EDB-SPL variables and parameters can be used to supply values to the delete operation.

CREATE OR REPLACE PROCEDURE emp_delete (
    p_empno         IN emp.empno%TYPE
)
IS
BEGIN
    DELETE FROM emp WHERE empno = p_empno;

    IF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Deleted Employee # : ' || p_empno);
    ELSE
        DBMS_OUTPUT.PUT_LINE('Employee # ' || p_empno || ' not found');
    END IF;
END;

The SQL%FOUND conditional expression returns "true" if a row is deleted, "false" otherwise. See Section 13.4.7 for a discussion of SQL%FOUND and other similar expressions.

The following shows the deletion of an employee using this procedure.

EXEC emp_delete(9503);

Deleted Employee # : 9503

SELECT * FROM emp WHERE empno = 9503;

no rows selected

See Section 5.3 and DELETE for information on the SQL DELETE command.

13.4.7. Obtaining the Result Status

There are several attributes that can be used to determine the effect of a command. SQL%FOUND is a boolean that returns true if at least one row was affected by an INSERT, UPDATE or DELETE command or a SELECT INTO command retrieved one or more rows.

BEGIN
    UPDATE emp SET mgr = '7900' WHERE job = 'CLERK';
    IF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('At least one row has been updated');
    END IF;
END;

SQL%NOTFOUND is the opposite of SQL%FOUND. SQL%NOTFOUND returns true if no rows were affected by an INSERT, UPDATE or DELETE command or a SELECT INTO command retrieved no rows.

BEGIN
    UPDATE emp SET job = 'CLERK' WHERE empno = 9000;
    IF SQL%NOTFOUND THEN
        DBMS_OUTPUT.PUT_LINE('No rows were updated');
    END IF;
END;

SQL%ROWCOUNT provides the number of rows affected by an INSERT, UPDATE or DELETE command.

BEGIN
    UPDATE emp SET mgr = 7900 WHERE deptno = 30;
    DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' rows were updated');
END;