How to Create Packages in Oracle Database 23ai PL/SQL

Oracle Database 23ai offers powerful features for modern application development, and packages are at the center of it. A package in PL/SQL is a way to group related procedures, functions, constants, cursors, and variables into a single unit. Instead of scattering logic across many standalone objects, you can organize them under one umbrella, making your applications modular, secure, and easier to maintain.

This Oracle tutorial explains how to create packages, the difference between a specification and a body, how to use parameters in procedures and functions, how to handle exceptions, and how to integrate new JSON and vector data types available in Oracle Database 23ai.

Syntax of a Package in Oracle Database 23ai

Every PL/SQL package is divided into two main components: the package specification and the package body. The specification acts like an interface, listing the available functions, procedures, and public variables. The body contains the actual implementation. If you only need to declare global constants or types, you can create a package specification without a body.

Package Specification Syntax

CREATE [OR REPLACE] PACKAGE package_name IS
   -- Public type declarations
   -- Public variable declarations
   -- Public procedure and function specifications
   PROCEDURE procedure_name (parameter_list);
   FUNCTION function_name (parameter_list) RETURN datatype;
END package_name;
/

Package Body Syntax

CREATE [OR REPLACE] PACKAGE BODY package_name IS
   -- Private variables and helper functions (optional)

   PROCEDURE procedure_name (parameter_list) IS
   BEGIN
      -- procedure logic
   END;

   FUNCTION function_name (parameter_list) RETURN datatype IS
   BEGIN
      -- function logic
      RETURN value;
   END;

END package_name;
/

The specification ensures users know what they can call, while the body provides the logic. This separation allows you to update the internal implementation without breaking applications that depend on the package.

Creating a Simple Package

A package can encapsulate related operations. For example, in an HR system, you may need to both update employee salaries and calculate annual salary. Instead of writing two standalone procedures or functions, you can group them inside a single package for better organization and reuse.

Create a sample table

CREATE TABLE prc_emp_pkg (
    employee_id   NUMBER PRIMARY KEY,
    first_name    VARCHAR2(50),
    last_name     VARCHAR2(50),
    salary        NUMBER(10,2),
    department_id NUMBER
);

INSERT INTO prc_emp_pkg VALUES (301, 'Steve', 'Rogers', 7500, 10);
INSERT INTO prc_emp_pkg VALUES (302, 'Natasha', 'Romanoff', 9000, 20);
COMMIT;

Create the package specification

CREATE OR REPLACE PACKAGE prc_employee_pkg IS
   PROCEDURE raise_salary (
      p_emp_id   IN NUMBER,
      p_percent  IN NUMBER
   );

   FUNCTION get_annual_salary (
      p_emp_id IN NUMBER
   ) RETURN NUMBER;
END prc_employee_pkg;
/

This specification declares two public subprograms: raise_salary (a procedure) and get_annual_salary (a function). Applications can call them, but they cannot see how they are implemented inside the package body.

Create the package body

CREATE OR REPLACE PACKAGE BODY prc_employee_pkg IS

   PROCEDURE raise_salary (
      p_emp_id   IN NUMBER,
      p_percent  IN NUMBER
   ) IS
   BEGIN
      UPDATE prc_emp_pkg
      SET salary = salary + (salary * p_percent / 100)
      WHERE employee_id = p_emp_id;

      DBMS_OUTPUT.PUT_LINE('Salary updated for Employee ID: ' || p_emp_id);
   END;

   FUNCTION get_annual_salary (
      p_emp_id IN NUMBER
   ) RETURN NUMBER IS
      v_salary NUMBER;
   BEGIN
      SELECT salary * 12
      INTO v_salary
      FROM prc_emp_pkg
      WHERE employee_id = p_emp_id;

      RETURN v_salary;
   END;

END prc_employee_pkg;
/

Here, raise_salary uses two IN parameters—the employee ID and the percentage increase—to update the salary. The get_annual_salary function accepts an employee ID and returns a single value (annual salary).

Execute the package

SET SERVEROUTPUT ON;

BEGIN
   prc_employee_pkg.raise_salary(301, 20);  
END;
/

SELECT prc_employee_pkg.get_annual_salary(301) AS annual_salary
FROM dual;

Output:

Salary updated for Employee ID: 301

ANNUAL_SALARY
-------------
108000

This shows how a package consolidates related logic into one place, improving code organization and reusability.

Creating a Package with IN, OUT, and IN OUT Parameters

Packages often need to pass multiple values back to the caller. This is where OUT and IN OUT parameters are useful. A package can contain procedures that take multiple input values and return multiple outputs.

Create the package specification

CREATE OR REPLACE PACKAGE prc_employee_info_pkg IS
   PROCEDURE get_employee_details (
      p_emp_id   IN  NUMBER,
      p_fname    OUT VARCHAR2,
      p_lname    OUT VARCHAR2,
      p_salary   OUT NUMBER
   );
END prc_employee_info_pkg;
/

This declares a procedure get_employee_details with one input (p_emp_id) and three output parameters (p_fname, p_lname, p_salary).

Create the package body

CREATE OR REPLACE PACKAGE BODY prc_employee_info_pkg IS

   PROCEDURE get_employee_details (
      p_emp_id   IN  NUMBER,
      p_fname    OUT VARCHAR2,
      p_lname    OUT VARCHAR2,
      p_salary   OUT NUMBER
   ) IS
   BEGIN
      SELECT first_name, last_name, salary
      INTO   p_fname, p_lname, p_salary
      FROM   prc_emp_pkg
      WHERE  employee_id = p_emp_id;
   EXCEPTION
      WHEN NO_DATA_FOUND THEN
         p_fname  := NULL;
         p_lname  := NULL;
         p_salary := NULL;
   END;

END prc_employee_info_pkg;
/

The OUT parameters allow the procedure to return multiple pieces of information at once. If the employee ID does not exist, the procedure sets the outputs to NULL.

Execute the procedure

SET SERVEROUTPUT ON;

DECLARE
   v_fname  VARCHAR2(50);
   v_lname  VARCHAR2(50);
   v_salary NUMBER;
BEGIN
   prc_employee_info_pkg.get_employee_details(302, v_fname, v_lname, v_salary);
   DBMS_OUTPUT.PUT_LINE('Employee: ' || v_fname || ' ' || v_lname || ' | Salary = ' || v_salary);
END;
/

Output:

Employee: Natasha Romanoff | Salary = 9000

This makes it easy to return multiple related values from one call instead of executing multiple queries.

Creating a Package with JSON Functions in Oracle 23ai

Oracle Database 23ai has introduced a native JSON data type, making it faster and easier to work with semi-structured data. You can use packages to build reusable APIs for handling JSON documents, extracting values, or processing JSON content.

Create a JSON table

CREATE TABLE prc_customers_pkg (
    customer_id   NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    customer_data JSON
);

INSERT INTO prc_customers_pkg (customer_data) VALUES (
    JSON('{"name": "Tony", "email": "tony@stark.com", "tier": "Platinum"}')
);

INSERT INTO prc_customers_pkg (customer_data) VALUES (
    JSON('{"name": "Bruce", "email": "bruce@wayne.com", "tier": "Gold"}')
);

COMMIT;

Create the package

CREATE OR REPLACE PACKAGE prc_customer_pkg IS
   FUNCTION get_customer_email (
      p_customer_id IN NUMBER
   ) RETURN VARCHAR2;

   PROCEDURE print_customer_details (
      p_customer_id IN NUMBER
   );
END prc_customer_pkg;
/

CREATE OR REPLACE PACKAGE BODY prc_customer_pkg IS

   FUNCTION get_customer_email (
      p_customer_id IN NUMBER
   ) RETURN VARCHAR2 IS
      v_email VARCHAR2(100);
   BEGIN
      SELECT c.customer_data.email.string()
      INTO v_email
      FROM prc_customers_pkg c
      WHERE c.customer_id = p_customer_id;

      RETURN v_email;
   END;

   PROCEDURE print_customer_details (
      p_customer_id IN NUMBER
   ) IS
      v_name VARCHAR2(100);
      v_email VARCHAR2(100);
      v_tier VARCHAR2(50);
   BEGIN
      SELECT c.customer_data.name.string(),
             c.customer_data.email.string(),
             c.customer_data.tier.string()
      INTO v_name, v_email, v_tier
      FROM prc_customers_pkg c
      WHERE c.customer_id = p_customer_id;

      DBMS_OUTPUT.PUT_LINE('Customer: ' || v_name || ' | Email: ' || v_email || ' | Tier: ' || v_tier);
   END;

END prc_customer_pkg;
/

Execute the JSON functions

SET SERVEROUTPUT ON;

-- Call function in SQL
SELECT prc_customer_pkg.get_customer_email(1) AS customer_email
FROM dual;

-- Call procedure
BEGIN
   prc_customer_pkg.print_customer_details(2);
END;
/

Output:

CUSTOMER_EMAIL
-----------------
tony@stark.com

Customer: Bruce | Email: bruce@wayne.com | Tier: Gold

This shows how packages in Oracle 23ai can handle JSON efficiently using the new JSON type and direct field access methods like .string() and .number().

Creating a Package with Vector Functions in Oracle 23ai

Oracle 23ai introduces vector data types for storing and querying embeddings used in AI and machine learning. You can package vector search operations in functions and procedures to make them reusable across applications such as recommendation systems, similarity search, or semantic matching.

Create a vector table

CREATE TABLE prc_doc_pkg (
    doc_id    NUMBER PRIMARY KEY,
    content   CLOB,
    embedding VECTOR(4)
);

INSERT INTO prc_doc_pkg VALUES (
    1,
    'Oracle 23ai introduces AI vector search for modern data applications.',
    TO_VECTOR('[0.12, 0.95, -0.40, 0.60]')
);

INSERT INTO prc_doc_pkg VALUES (
    2,
    'PL/SQL packages in Oracle Database 23ai support functions and procedures.',
    TO_VECTOR('[0.13, 0.90, -0.38, 0.62]')
);

COMMIT;

Create the vector search package

CREATE OR REPLACE PACKAGE prc_vector_pkg IS
   FUNCTION get_similarity (
      p_doc_id    IN NUMBER,
      p_query_vec IN VECTOR
   ) RETURN BINARY_DOUBLE;

   PROCEDURE find_top_matches (
      p_query_vec IN VECTOR,
      p_limit     IN NUMBER
   );
END prc_vector_pkg;
/

CREATE OR REPLACE PACKAGE BODY prc_vector_pkg IS

   FUNCTION get_similarity (
      p_doc_id    IN NUMBER,
      p_query_vec IN VECTOR
   ) RETURN BINARY_DOUBLE IS
      v_score BINARY_DOUBLE;
   BEGIN
      SELECT embedding <-> p_query_vec
      INTO v_score
      FROM prc_doc_pkg
      WHERE doc_id = p_doc_id;

      RETURN v_score;
   END;

   PROCEDURE find_top_matches (
      p_query_vec IN VECTOR,
      p_limit     IN NUMBER
   ) IS
   BEGIN
      FOR rec IN (
         SELECT doc_id, content, embedding <-> p_query_vec AS distance
         FROM prc_doc_pkg
         ORDER BY embedding <-> p_query_vec
         FETCH FIRST p_limit ROWS ONLY
      ) LOOP
         DBMS_OUTPUT.PUT_LINE('Doc ID: ' || rec.doc_id ||
                              ' | Distance: ' || rec.distance ||
                              ' | Content: ' || DBMS_LOB.SUBSTR(rec.content, 60));
      END LOOP;
   END;

END prc_vector_pkg;
/

Execute the vector package

SET SERVEROUTPUT ON;

-- Call function inside SQL
SELECT prc_vector_pkg.get_similarity(2, TO_VECTOR('[0.14, 0.92, -0.39, 0.61]')) AS sim_score
FROM dual;

-- Call procedure
BEGIN
   prc_vector_pkg.find_top_matches(TO_VECTOR('[0.14, 0.92, -0.39, 0.61]'), 2);
END;
/

Sample Output:

SIM_SCORE
---------
0.02

Doc ID: 2 | Distance: 0.02 | Content: PL/SQL packages in Oracle Database 23ai support functions and procedures.
Doc ID: 1 | Distance: 0.04 | Content: Oracle 23ai introduces AI vector search for modern data applications.

This example demonstrates how you can encapsulate vector similarity logic in packages and reuse it across your applications, which is valuable for AI and recommendation workloads.

Managing Packages

After creating packages, you may need to recompile them when dependent objects change or drop them when they are no longer needed. Oracle Database 23ai provides flexible management options.

-- Recompile package specification
ALTER PACKAGE prc_employee_pkg COMPILE SPECIFICATION;

-- Recompile package body
ALTER PACKAGE prc_employee_pkg COMPILE BODY;

-- Drop the entire package
DROP PACKAGE prc_customer_pkg;

-- Drop only the package body
DROP PACKAGE BODY prc_vector_pkg;

Recompiling the body allows you to change internal logic without affecting applications that depend on the specification. If you modify the specification, dependent objects may need to be recompiled.

Conclusion

Packages in Oracle Database 23ai are essential for building robust, modular, and secure PL/SQL applications. They let you group related procedures and functions, manage business logic more effectively, and improve performance by storing compiled code in memory.

With Oracle 23ai’s support for JSON and vector data types, packages are more powerful than ever, enabling developers to integrate modern AI-driven features directly into the database. Mastering packages is a key step for any developer who wants to build enterprise-ready solutions on Oracle Database 23ai.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE and software veteran with 25+ years of experience, passionate about AI and IT innovation.

guest

0 Comments
Oldest
Newest Most Voted