Oracle PL/SQL – Procedures and Functions

Subject: Information Technology – Database Systems
Grade: 12th Grade – HTL Computer Science
Prerequisites: PL/SQL Introduction, Control Structures, Cursors, Exceptions
Author: HTL Pinkafeld – IF/IT



1. Stored Subprograms – Overview

1.1 Advantages over Anonymous Blocks

Feature Anonymous Block Stored Procedure/Function
Storage Not stored Stored in database
Reusability No Yes – callable from anywhere
Compilation Every execution Once (pre-compiled)
Security No GRANT possible EXECUTE privilege grantable
Parameters Not possible IN, OUT, IN OUT
Return value Not possible Function returns a value

1.2 Procedure vs. Function

Feature Procedure Function
Keyword PROCEDURE FUNCTION
Return value None (only OUT params) Exactly one value (RETURN)
Called As a statement In expressions, SQL
Typical use Execute actions Compute and return a value

2. Creating Stored Procedures

2.1 Syntax

CREATE [OR REPLACE] PROCEDURE procedure_name
    [(parameter1 [mode] data_type, ...)]
IS  -- or AS
    -- Declaration section (no DECLARE keyword!)
BEGIN
    -- Execution section
EXCEPTION
    -- Error handling (optional)
END [procedure_name];
/

2.2 Simple Procedure without Parameters

CREATE OR REPLACE PROCEDURE show_departments IS
BEGIN
    FOR r IN (SELECT deptno, dname, loc FROM dept ORDER BY deptno) LOOP
        DBMS_OUTPUT.PUT_LINE(
            LPAD(r.deptno, 3) || ' | ' || RPAD(r.dname, 14) || ' | ' || r.loc
        );
    END LOOP;
END show_departments;
/

-- Call:
BEGIN
    show_departments;
END;
/

2.3 OR REPLACE

The OR REPLACE keyword overwrites an existing procedure without an error. Without it, Oracle throws an error if the procedure already exists.


3. Parameter Modes: IN, OUT, IN OUT

3.1 IN Parameter (Default)

IN is the default mode. The value is passed at call time and cannot be changed inside the procedure:

CREATE OR REPLACE PROCEDURE give_raise (
    p_empno   IN emp.empno%TYPE,
    p_percent IN NUMBER
) IS
    v_old_sal emp.sal%TYPE;
BEGIN
    SELECT sal INTO v_old_sal FROM emp WHERE empno = p_empno;

    UPDATE emp
    SET    sal = sal * (1 + p_percent / 100)
    WHERE  empno = p_empno;

    DBMS_OUTPUT.PUT_LINE(
        'Employee ' || p_empno || ': ' || v_old_sal ||
        ' → ' || ROUND(v_old_sal * (1 + p_percent/100), 2)
    );
    COMMIT;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Employee ' || p_empno || ' not found.');
END give_raise;
/

-- Call:
BEGIN
    give_raise(7369, 10);   -- 10% raise for employee 7369
END;
/

3.2 OUT Parameter

OUT parameters return values to the caller. They may not be read inside the procedure (only written):

CREATE OR REPLACE PROCEDURE get_emp_info (
    p_empno    IN  emp.empno%TYPE,
    p_name     OUT emp.ename%TYPE,
    p_salary   OUT emp.sal%TYPE,
    p_found    OUT BOOLEAN
) IS
BEGIN
    SELECT ename, sal
    INTO   p_name, p_salary
    FROM   emp
    WHERE  empno = p_empno;
    p_found := TRUE;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        p_name    := NULL;
        p_salary  := NULL;
        p_found   := FALSE;
END get_emp_info;
/

-- Call:
DECLARE
    v_name  VARCHAR2(10);
    v_sal   NUMBER;
    v_ok    BOOLEAN;
BEGIN
    get_emp_info(7839, v_name, v_sal, v_ok);
    IF v_ok THEN
        DBMS_OUTPUT.PUT_LINE(v_name || ' earns ' || v_sal);
    ELSE
        DBMS_OUTPUT.PUT_LINE('Not found.');
    END IF;
END;
/

3.3 IN OUT Parameter

IN OUT parameters can be both read and written:

CREATE OR REPLACE PROCEDURE normalize_name (
    p_name IN OUT VARCHAR2
) IS
BEGIN
    p_name := INITCAP(TRIM(p_name));
END normalize_name;
/

DECLARE
    v_n VARCHAR2(50) := '  JOHN DOE  ';
BEGIN
    DBMS_OUTPUT.PUT_LINE('Before: [' || v_n || ']');
    normalize_name(v_n);
    DBMS_OUTPUT.PUT_LINE('After:  [' || v_n || ']');
END;
/

4. Creating Stored Functions

4.1 Syntax

CREATE [OR REPLACE] FUNCTION function_name
    [(parameter [IN] data_type, ...)]
RETURN return_type
IS  -- or AS
    -- Declarations
BEGIN
    -- Logic
    RETURN expression;
EXCEPTION
    ...
END [function_name];
/

4.2 Simple Function

CREATE OR REPLACE FUNCTION annual_salary (
    p_sal  emp.sal%TYPE,
    p_comm emp.comm%TYPE DEFAULT 0
) RETURN NUMBER
IS
    v_comm NUMBER := NVL(p_comm, 0);
BEGIN
    RETURN (p_sal + v_comm) * 12;
END annual_salary;
/

-- Call in PL/SQL:
DECLARE
    v_ann NUMBER;
BEGIN
    v_ann := annual_salary(3000, 500);
    DBMS_OUTPUT.PUT_LINE('Annual salary: ' || v_ann);
END;
/

4.3 Function with Error Handling

CREATE OR REPLACE FUNCTION get_dept_name (
    p_deptno dept.deptno%TYPE
) RETURN VARCHAR2
IS
    v_name dept.dname%TYPE;
BEGIN
    SELECT dname INTO v_name FROM dept WHERE deptno = p_deptno;
    RETURN v_name;
EXCEPTION
    WHEN NO_DATA_FOUND THEN RETURN 'Unknown Department';
    WHEN OTHERS        THEN RETURN 'Error: ' || SQLERRM;
END get_dept_name;
/

5. Using Functions in SQL Queries

5.1 Function in SELECT

Custom functions can be used directly in SQL queries – just like built-in functions:

SELECT empno,
       ename,
       sal,
       NVL(comm, 0)              AS commission,
       annual_salary(sal, comm)  AS annual_pay,
       get_dept_name(deptno)     AS department
FROM   emp
ORDER  BY annual_pay DESC;

5.2 Functions in WHERE and ORDER BY

-- Function in WHERE:
SELECT ename, sal
FROM   emp
WHERE  annual_salary(sal, comm) > 40000;

-- Function in ORDER BY:
SELECT ename, deptno
FROM   emp
ORDER  BY get_dept_name(deptno), ename;

5.3 Restrictions for SQL Functions

For a function to be usable in SQL, it must follow these rules:


6. Local Subprograms

Procedures and functions can be defined locally within a block. They are only visible within that block:

DECLARE
    -- Local function (must come AFTER all variable declarations)
    FUNCTION tax (p_salary NUMBER) RETURN NUMBER IS
    BEGIN
        CASE
            WHEN p_salary <= 1800 THEN RETURN 0;
            WHEN p_salary <= 3000 THEN RETURN p_salary * 0.20;
            WHEN p_salary <= 5000 THEN RETURN p_salary * 0.32;
            ELSE                       RETURN p_salary * 0.42;
        END CASE;
    END tax;

    PROCEDURE show_tax (p_name VARCHAR2, p_sal NUMBER) IS
    BEGIN
        DBMS_OUTPUT.PUT_LINE(
            RPAD(p_name, 12) || 'Salary: ' || LPAD(p_sal, 6) ||
            ' | Tax: ' || LPAD(ROUND(tax(p_sal)), 6)
        );
    END show_tax;

BEGIN
    FOR r IN (SELECT ename, sal FROM emp WHERE deptno = 10) LOOP
        show_tax(r.ename, r.sal);
    END LOOP;
END;
/

7. Overloading

Within a package (or local declaration section), multiple subprograms with the same name but different parameters can coexist:

DECLARE
    FUNCTION format_val (p_value NUMBER) RETURN VARCHAR2 IS
    BEGIN RETURN TO_CHAR(p_value, 'FM999,990.00') || ' USD'; END;

    FUNCTION format_val (p_value DATE) RETURN VARCHAR2 IS
    BEGIN RETURN TO_CHAR(p_value, 'DD/MM/YYYY'); END;

    FUNCTION format_val (p_value VARCHAR2) RETURN VARCHAR2 IS
    BEGIN RETURN '"' || TRIM(p_value) || '"'; END;

BEGIN
    DBMS_OUTPUT.PUT_LINE(format_val(1234.5));
    DBMS_OUTPUT.PUT_LINE(format_val(SYSDATE));
    DBMS_OUTPUT.PUT_LINE(format_val('  Hello  '));
END;
/

8. Managing Subprograms

8.1 Data Dictionary

-- Show all own procedures/functions:
SELECT object_name, object_type, status, last_ddl_time
FROM   user_objects
WHERE  object_type IN ('PROCEDURE', 'FUNCTION')
ORDER  BY object_type, object_name;

-- Show source code:
SELECT line, text
FROM   user_source
WHERE  name = 'ANNUAL_SALARY'
AND    type = 'FUNCTION'
ORDER  BY line;

-- Show compile errors:
SELECT line, position, text
FROM   user_errors
WHERE  name = 'GIVE_RAISE'
ORDER  BY sequence;

8.2 Grant Privileges

GRANT EXECUTE ON annual_salary TO scott;
GRANT EXECUTE ON give_raise TO hr_app;
REVOKE EXECUTE ON annual_salary FROM scott;

9. Autonomous Transactions

9.1 PRAGMA AUTONOMOUS_TRANSACTION

An autonomous transaction runs independently of the calling transaction. Useful for logging (must survive even a ROLLBACK):

CREATE OR REPLACE PROCEDURE log_action (
    p_action  VARCHAR2,
    p_user    VARCHAR2 DEFAULT USER,
    p_info    VARCHAR2 DEFAULT NULL
) IS
    PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
    INSERT INTO audit_log (action, usr, ts, info)
    VALUES (p_action, p_user, SYSTIMESTAMP, p_info);
    COMMIT;  -- Commits only THIS transaction – main TX unaffected
END log_action;
/

BEGIN
    log_action('DELETE', USER, 'Attempt to delete dept 10');
    DELETE FROM dept WHERE deptno = 10;  -- Fails (FK violation)
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;  -- Main transaction rolled back
        -- But: log_action was COMMITTED and remains!
        DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END;
/

10. Summary and Outlook

10.1 Procedure vs. Function – Decision Guide

Question Recommendation
Is a value returned? Function
Should it be used in SQL? Function
Multiple output values needed? Procedure with OUT parameters
Execute actions (DML)? Procedure
Logging (independent)? Procedure with AUTONOMOUS_TRANSACTION

10.2 Best Practices

10.3 Outlook

The next chapter covers Packages – bundling related procedures, functions, types, and variables into a logical unit:

Previous TopicExceptions Next TopicPackages