Oracle PL/SQL

Complete PL/SQL Exercises: From Beginner to Advanced

complete plsql
Avatar of shohal
Written by shohal

The goal of this post is to help students, job seekers, and professionals practice PL/SQL coding in a structured way and master problem-solving skills for exams, interviews, and real-world database projects.

Exercise 1 – Hello PL/SQL

Write a block that prints ‘Hello, PL/SQL!’ using DBMS_OUTPUT.

BEGIN

   DBMS_OUTPUT.PUT_LINE('Hello, PL/SQL!');

END;

/

Exercise 2 – Simple Addition

Declare two variables a and b, assign values, and print their sum.

DECLARE

   a NUMBER := 10;   -- first variable

   b NUMBER := 20;   -- second variable

   sum_result NUMBER;  -- variable to hold the sum

BEGIN

   sum_result := a + b;  -- perform addition

   DBMS_OUTPUT.PUT_LINE('The sum of ' || a || ' and ' || b || ' is: ' || sum_result);

END;

/

Exercise 3 – IF Statement

Check if a number is positive or negative and print result.

DECLARE

   num NUMBER := -15;   -- change the value to test

BEGIN

   IF num > 0 THEN

      DBMS_OUTPUT.PUT_LINE(num || ' is Positive');

   ELSIF num < 0 THEN

      DBMS_OUTPUT.PUT_LINE(num || ' is Negative');

   ELSE

      DBMS_OUTPUT.PUT_LINE('The number is Zero');

   END IF;

END;

/

Exercise 4 – Multiplication Table

Print the multiplication table of 5 using a LOOP.

DECLARE

   num NUMBER := 5;  -- number for which table is printed

BEGIN

   FOR i IN 1..7 LOOP

      DBMS_OUTPUT.PUT_LINE(num || ' x ' || i || ' = ' || (num * i));

   END LOOP;

END;

/

Exercise 5 – Factorial

Write a PL/SQL program to calculate factorial of 5.

DECLARE

   n NUMBER := 5;          -- number to calculate factorial

   factorial NUMBER := 1;  -- variable to store factorial

BEGIN

   FOR i IN 1..n LOOP

      factorial := factorial * i;

   END LOOP;

   DBMS_OUTPUT.PUT_LINE('Factorial of ' || n || ' is: ' || factorial);

END;

/

Exercise 6 – Prime Check

Check if a given number is prime.

DECLARE

   num NUMBER := 17;      -- number to check

   is_prime BOOLEAN := TRUE;

BEGIN

   IF num <= 1 THEN

      is_prime := FALSE;

   ELSE

      FOR i IN 2..TRUNC(SQRT(num)) LOOP

         IF MOD(num, i) = 0 THEN

            is_prime := FALSE;

            EXIT;  -- exit loop if a divisor is found

         END IF;

      END LOOP;

   END IF;

   IF is_prime THEN

      DBMS_OUTPUT.PUT_LINE(num || ' is a Prime number');

   ELSE

      DBMS_OUTPUT.PUT_LINE(num || ' is not a Prime number');

   END IF;

END;

/

Exercise 7 – Procedures

Write a procedure that accepts a number and prints whether it is odd or even.

SET SERVEROUTPUT ON;

— Create the procedure

CREATE OR REPLACE PROCEDURE check_odd_even(p_num IN NUMBER) IS

BEGIN

   IF MOD(p_num, 2) = 0 THEN

      DBMS_OUTPUT.PUT_LINE(p_num || ' is Even');

   ELSE

      DBMS_OUTPUT.PUT_LINE(p_num || ' is Odd');

   END IF;

END;

/

— Call the procedure

BEGIN

   check_odd_even(15);  -- You can change this number to test

   check_odd_even(24);

END;

/

Exercise 8 – Functions

Write a function that returns the square of a number.

CREATE OR REPLACE FUNCTION square_num(p_number IN NUMBER)

RETURN NUMBER

AS

BEGIN

    RETURN p_number * p_number;

END;

/
DECLARE

    v_result NUMBER;

BEGIN

    v_result := square_num(7);

    DBMS_OUTPUT.PUT_LINE('Square of 7 is: ' || v_result);

END;

/

Exercise 9 – Cursor Example

Use an explicit cursor to fetch and print employee names.

CREATE TABLE employees (

    emp_id   NUMBER PRIMARY KEY,

    name     VARCHAR2(100) NOT NULL,

    salary   NUMBER(10,2),

    dept_id  NUMBER

);

INSERT INTO employees (emp_id, name, salary, dept_id)

VALUES (1, 'Alice Johnson', 75000, 10);

INSERT INTO employees (emp_id, name, salary, dept_id)

VALUES (2, 'Bob Smith', 60000, 20);

INSERT INTO employees (emp_id, name, salary, dept_id)

VALUES (3, 'Charlie Brown', 55000, 20);

INSERT INTO employees (emp_id, name, salary, dept_id)

VALUES (4, 'Diana Prince', 50000, 30);

INSERT INTO employees (emp_id, name, salary, dept_id)

VALUES (5, 'Ethan Hunt', 80000, 10);

DECLARE

    -- Declare cursor

    CURSOR emp_cur IS

        SELECT name

        FROM employees;

    -- Variable to hold fetched value

    v_name employees.name%TYPE;

BEGIN

    -- Open the cursor

    OPEN emp_cur;

    LOOP

        -- Fetch into variable

        FETCH emp_cur INTO v_name;

        EXIT WHEN emp_cur%NOTFOUND;

        -- Print employee name

        DBMS_OUTPUT.PUT_LINE('Employee: ' || v_name);

    END LOOP;

    -- Close the cursor

    CLOSE emp_cur;

END;

/

Exercise 10 – Exception Handling

Write code that divides two numbers and handles divide-by-zero.

DECLARE

    num1 NUMBER := 20;

    num2 NUMBER := 0;  -- change to a nonzero number to test normal case

    result NUMBER;

BEGIN

    BEGIN

        result := num1 / num2;

        DBMS_OUTPUT.PUT_LINE('Result: ' || result);

    EXCEPTION

        WHEN ZERO_DIVIDE THEN

            DBMS_OUTPUT.PUT_LINE('Error: Cannot divide by zero!');

    END;

END;

/

Exercise 11 – User-defined Exception

Raise and handle a custom exception when salary < 1000.

DECLARE

    v_salary employees.salary%TYPE := 800;  -- test with less than 1000

    -- Declare custom exception

    ex_low_salary EXCEPTION;

BEGIN

    -- Check condition

    IF v_salary < 1000 THEN

        RAISE ex_low_salary;  -- raise exception

    END IF;

    DBMS_OUTPUT.PUT_LINE('Salary is acceptable: ' || v_salary);

EXCEPTION

    WHEN ex_low_salary THEN

        DBMS_OUTPUT.PUT_LINE('Error: Salary cannot be less than 1000! Entered salary = ' || v_salary);

END;

/

Exercise 12 – Nested Blocks

Create a block inside another, handling exceptions separately.

DECLARE

    num1 NUMBER := 10;

    num2 NUMBER := 0;

    result NUMBER;

BEGIN

    DBMS_OUTPUT.PUT_LINE('--- Outer Block Started ---');

    -- Inner block

    BEGIN

        DBMS_OUTPUT.PUT_LINE('--- Inner Block Started ---');

        -- This will cause divide by zero

        result := num1 / num2;

        DBMS_OUTPUT.PUT_LINE('Result: ' || result);

        DBMS_OUTPUT.PUT_LINE('--- Inner Block Ended ---');

    EXCEPTION

        WHEN ZERO_DIVIDE THEN

            DBMS_OUTPUT.PUT_LINE('Inner Block Error: Cannot divide by zero!');

    END;

    -- Continue outer block

    DBMS_OUTPUT.PUT_LINE('--- Outer Block Continues ---');

    -- Simulate another error

    RAISE VALUE_ERROR; -- for demonstration

EXCEPTION

    WHEN VALUE_ERROR THEN

        DBMS_OUTPUT.PUT_LINE('Outer Block Error: Invalid numeric operation!');

    WHEN OTHERS THEN

        DBMS_OUTPUT.PUT_LINE('Outer Block Error: ' || SQLERRM);

END;

/

Exercise 13 – Packages

Create a package with a procedure and function for arithmetic operations.

————Package Specification———————-

CREATE OR REPLACE PACKAGE arithmetic_pkg AS

    -- Function to add two numbers

    FUNCTION add_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER;

    -- Function to subtract two numbers

    FUNCTION subtract_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER;

    -- Function to multiply two numbers

    FUNCTION multiply_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER;

    -- Procedure to divide two numbers and print result

    PROCEDURE divide_numbers(p_num1 NUMBER, p_num2 NUMBER);

END arithmetic_pkg;

/

—————-Package Body—————

CREATE OR REPLACE PACKAGE BODY arithmetic_pkg AS

    FUNCTION add_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER IS

    BEGIN

        RETURN p_num1 + p_num2;

    END add_numbers;

    FUNCTION subtract_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER IS

    BEGIN

        RETURN p_num1 - p_num2;

    END subtract_numbers;

    FUNCTION multiply_numbers(p_num1 NUMBER, p_num2 NUMBER) RETURN NUMBER IS

    BEGIN

        RETURN p_num1 * p_num2;

    END multiply_numbers;

    PROCEDURE divide_numbers(p_num1 NUMBER, p_num2 NUMBER) IS

        v_result NUMBER;

    BEGIN

        v_result := p_num1 / p_num2;

        DBMS_OUTPUT.PUT_LINE('Division Result: ' || v_result);

    EXCEPTION

        WHEN ZERO_DIVIDE THEN

            DBMS_OUTPUT.PUT_LINE('Error: Cannot divide by zero!');

    END divide_numbers;

END arithmetic_pkg;

/

——————Test the Package———————-

SET SERVEROUTPUT ON;
DECLARE

    v_result NUMBER;

BEGIN

    -- Using functions

    v_result := arithmetic_pkg.add_numbers(10, 5);

    DBMS_OUTPUT.PUT_LINE('Addition: ' || v_result);

    v_result := arithmetic_pkg.subtract_numbers(10, 5);

    DBMS_OUTPUT.PUT_LINE('Subtraction: ' || v_result);

    v_result := arithmetic_pkg.multiply_numbers(10, 5);

    DBMS_OUTPUT.PUT_LINE('Multiplication: ' || v_result);

    -- Using procedure

    arithmetic_pkg.divide_numbers(10, 0);  -- will trigger exception

END;

/

Exercise 14 – Overloading

Overload a function to calculate area of circle and rectangle.

—————-Create a Package Specification———————

CREATE OR REPLACE PACKAGE area_pkg AS

    -- Area of circle (radius)

    FUNCTION calc_area(p_radius NUMBER) RETURN NUMBER;

    -- Area of rectangle (length, width)

    FUNCTION calc_area(p_length NUMBER, p_width NUMBER) RETURN NUMBER;

END area_pkg;

/

———————Package Body————————

CREATE OR REPLACE PACKAGE BODY area_pkg AS

    -- Area of circle

    FUNCTION calc_area(p_radius NUMBER) RETURN NUMBER IS

    BEGIN

        RETURN 3.14159 * p_radius * p_radius;

    END calc_area;

    -- Area of rectangle

    FUNCTION calc_area(p_length NUMBER, p_width NUMBER) RETURN NUMBER IS

    BEGIN

        RETURN p_length * p_width;

    END calc_area;

END area_pkg;

/

—————–Test the Overloaded Functions——————–

SET SERVEROUTPUT ON;
DECLARE

    v_circle_area NUMBER;

    v_rectangle_area NUMBER;

BEGIN

    -- Circle with radius 5

    v_circle_area := area_pkg.calc_area(5);

    DBMS_OUTPUT.PUT_LINE('Area of Circle: ' || v_circle_area);

    -- Rectangle with length 10 and width 4

    v_rectangle_area := area_pkg.calc_area(10, 4);

    DBMS_OUTPUT.PUT_LINE('Area of Rectangle: ' || v_rectangle_area);

END;

/

Exercise 15 – Triggers

Create a trigger that logs inserts on the employee table.

————–Create a Log Table ———————–

CREATE TABLE employee_log (

    log_id      NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,

    emp_id      NUMBER,

    name        VARCHAR2(100),

    salary      NUMBER(10,2),

    dept_id     NUMBER,

    inserted_on DATE DEFAULT SYSDATE

);

——————Create the Trigger——————

CREATE OR REPLACE TRIGGER trg_employee_insert

AFTER INSERT ON employees

FOR EACH ROW

BEGIN

    INSERT INTO employee_log (emp_id, name, salary, dept_id)

    VALUES (:NEW.emp_id, :NEW.name, :NEW.salary, :NEW.dept_id);

END;

/

—————-Test the Trigger—————–

— Insert a new employee

INSERT INTO employees (emp_id, name, salary, dept_id)

VALUES (6, 'Frank Castle', 70000, 40);

COMMIT;

— Check the log table

SELECT * FROM employee_log;

Exercise 16 – Bulk Collect

Fetch multiple rows into a collection using BULK COLLECT.

SET SERVEROUTPUT ON;

DECLARE

    -- Declare a collection (nested table) to hold employee names

    TYPE emp_name_tbl IS TABLE OF employees.name%TYPE;

    v_emp_names emp_name_tbl;

BEGIN

    -- Fetch all employee names into the collection

    SELECT name

    BULK COLLECT INTO v_emp_names

    FROM employees

    WHERE salary > 50000;  -- example condition

    -- Loop through the collection and print names

    FOR i IN 1 .. v_emp_names.COUNT LOOP

        DBMS_OUTPUT.PUT_LINE('Employee: ' || v_emp_names(i));

    END LOOP;

END;

/

Exercise 17 – FORALL

Use FORALL to insert multiple records into a table efficiently.

CREATE TABLE employees_bulk (

    emp_id    NUMBER,

    name      VARCHAR2(50),

    salary    NUMBER

);
set serveroutput on
DECLARE

    -- Define PL/SQL collection types

    TYPE emp_id_tab IS TABLE OF NUMBER;

    TYPE name_tab IS TABLE OF VARCHAR2(50);

    TYPE salary_tab IS TABLE OF NUMBER;

    -- Declare and initialize collections

    v_emp_ids   emp_id_tab := emp_id_tab(201, 202, 203, 204,205);

    v_names     name_tab   := name_tab('Rahim', 'Karim', 'Jasim', 'Hasan', 'Kuddus');

    v_salaries  salary_tab := salary_tab(3000, 3500, 2800, 4000, 3200);

BEGIN

    -- Bulk insert using FORALL

    FORALL i IN v_emp_ids.FIRST .. v_emp_ids.LAST

        INSERT INTO employees_bulk (emp_id, name, salary)

        VALUES (v_emp_ids(i), v_names(i), v_salaries(i));

    DBMS_OUTPUT.PUT_LINE('Bulk insert completed successfully!');

END;

/

Exercise 18 – Record Types

Create and use a RECORD type for student info.

DECLARE

    -- Define a RECORD type for student info

    TYPE student_rec IS RECORD (

        student_id   NUMBER,

        student_name VARCHAR2(50),

        age          NUMBER,

        dept         VARCHAR2(30)

    );

    -- Declare a variable of that record type

    v_student student_rec;

BEGIN

    -- Assign values to the record fields

    v_student.student_id   := 101;

    v_student.student_name := 'Rahim';

    v_student.age          := 21;

    v_student.dept         := 'Computer Science';

    -- Print values using DBMS_OUTPUT

    DBMS_OUTPUT.PUT_LINE('Student ID   : ' || v_student.student_id);

    DBMS_OUTPUT.PUT_LINE('Name         : ' || v_student.student_name);

    DBMS_OUTPUT.PUT_LINE('Age          : ' || v_student.age);

    DBMS_OUTPUT.PUT_LINE('Department   : ' || v_student.dept);

END;

/

Exercise 19 – Object Types

Define an object type ‘Car’ with attributes and methods.

CREATE OR REPLACE TYPE Car AS OBJECT (

    -- Attributes

    car_id     NUMBER,

    brand      VARCHAR2(50),

    model      VARCHAR2(50),

    price      NUMBER,

    -- Member methods

    MEMBER PROCEDURE display_info,

    MEMBER FUNCTION apply_discount(p_percent NUMBER) RETURN NUMBER

);

/
CREATE OR REPLACE TYPE BODY Car AS

    -- Procedure to display car information

    MEMBER PROCEDURE display_info IS

    BEGIN

        DBMS_OUTPUT.PUT_LINE('Car ID   : ' || car_id);

        DBMS_OUTPUT.PUT_LINE('Brand    : ' || brand);

        DBMS_OUTPUT.PUT_LINE('Model    : ' || model);

        DBMS_OUTPUT.PUT_LINE('Price    : ' || price);

    END display_info;

    -- Function to calculate discounted price

    MEMBER FUNCTION apply_discount(p_percent NUMBER) RETURN NUMBER IS

    BEGIN

        RETURN price - (price * (p_percent / 100));

    END apply_discount;

END;

/
DECLARE

    v_car Car;

    v_new_price NUMBER;

BEGIN

    -- Initialize object

    v_car := Car(1, 'Toyota', 'Corolla', 20000);

    -- Call method to display info

    v_car.display_info;

    -- Apply discount

    v_new_price := v_car.apply_discount(10);

    DBMS_OUTPUT.PUT_LINE('Discounted Price : ' || v_new_price);

END;

/

Exercise 20 – Nested Tables

Store multiple phone numbers for an employee in a nested table.

— Create a collection type for phone numbers

CREATE OR REPLACE TYPE phone_list AS TABLE OF VARCHAR2(15);

/
CREATE TABLE employees_nt (

    emp_id     NUMBER PRIMARY KEY,

    emp_name   VARCHAR2(50),

    phones     phone_list

) NESTED TABLE phones STORE AS phones_nt_tab;

INSERT INTO employees_nt VALUES (

    1,

    'Rahim',

    phone_list('01711111111', '01822222222', '01933333333')

);

INSERT INTO employees_nt VALUES (

    2,

    'Karim',

    phone_list('01544444444')

);

— Normal query (phones not expanded)

SELECT * FROM employees_nt;

— Expand phone numbers using TABLE()

SELECT e.emp_id, e.emp_name, p.COLUMN_VALUE AS phone

FROM employees_nt e,

     TABLE(e.phones) p;

Exercise 21 – VARRAY

Create a VARRAY to hold top 5 scores of a student.

— Create a VARRAY type that can hold max 5 scores

CREATE OR REPLACE TYPE score_array AS VARRAY(5) OF NUMBER;

/
CREATE TABLE students_varray (

    student_id   NUMBER PRIMARY KEY,

    student_name VARCHAR2(50),

    scores       score_array

);

Exercise 22 – Autonomous Transactions

Write a procedure that logs audit records using PRAGMA AUTONOMOUS_TRANSACTION.

CREATE TABLE audit_log (

    log_id       NUMBER GENERATED BY DEFAULT AS IDENTITY,

    log_date     DATE DEFAULT SYSDATE,

    user_name    VARCHAR2(50),

    action       VARCHAR2(200),

    module       VARCHAR2(100),

    details      VARCHAR2(4000)

);

Exercise 23 – Materialized Views with PL/SQL

Refresh and query a materialized view inside PL/SQL.

— Insert some sample data

Exercise 24 – DBMS_SCHEDULER

Schedule a PL/SQL job that runs daily at midnight.

Exercise 25 – Dynamic SQL

Execute a dynamically built query using EXECUTE IMMEDIATE.

  1. Simple Dynamic SELECT INTO Variable

  • Dynamic DML (INSERT/UPDATE/DELETE)

  • Dynamic Query with Parameters (Using Bind Variables)

  • Returning Multiple Rows with BULK COLLECT

Exercise 26 – Bind Variables

Use bind variables with dynamic SQL for better performance.

1. Dynamic SELECT with a Single Bind

2. Dynamic SELECT with Multiple Binds

3. Dynamic DML with Binds

4. Fetch Multiple Rows with Binds (BULK COLLECT)

Exercise 27 – Native Compilation

Compile a PL/SQL unit natively for performance boost.

  1. One-off: compile a specific unit as native

— Procedure / Function / Package / Trigger

  • Session/system default (affects new compilations)

— Session scope (your current connection)

ALTER SESSION SET plsql_code_type      = NATIVE;

ALTER SESSION SET plsql_optimize_level = 3;

— Instance/system scope (needs privileges; affects future compilations)

ALTER SYSTEM SET plsql_code_type      = NATIVE  SCOPE=BOTH;

ALTER SYSTEM SET plsql_optimize_level = 3       SCOPE=BOTH;

  • Force recompilation so existing units pick up the setting

— Recompile one schema’s invalids

BEGIN

  UTL_RECOMP.recomp_serial(schema => USER);   — or a specific schema name

END;

/

— Or recompile a specific object after changing the session setting

ALTER PROCEDURE my_proc COMPILE;  — will use current session’s plsql_code_type

  • Verify what actually happened

— Check per-object settings (your schema)

SELECT name, type, plsql_code_type, plsql_optimize_level

FROM   user_plsql_object_settings

ORDER  BY name;

— Check dictionary view that shows what’s in effect

SELECT object_name, object_type, plsql_code_type, plsql_optimize_level

FROM   user_plsql_object_settings

WHERE  name = ‘MY_PROC’;

  • Measure quickly

SET SERVEROUTPUT ON

DECLARE

  t1 TIMESTAMP; t2 TIMESTAMP;

BEGIN

  t1 := SYSTIMESTAMP;

  — call hot code many times

  FOR i IN 1..100000 LOOP

    my_proc; — or my_func(…)

  END LOOP;

  t2 := SYSTIMESTAMP;

  DBMS_OUTPUT.PUT_LINE(‘Elapsed: ‘ || TO_CHAR(t2 – t1));

END;

/

  • Notes & gotchas

·  Modern Oracle (11gR2+): native compilation is integrated—no external C compiler or native library directories required.

·  Native helps most when code is CPU-bound with tight loops and minimal SQL context switching. If your time is in SQL, optimize SQL first.

·  Keep PLSQL_WARNINGS on during development:

ALTER SESSION SET plsql_warnings = ‘ENABLE:ALL’;

·  If you toggle plsql_code_type at the session level, you must recompile units in that session to change them to native/interpreted.

Exercise 28 – Distributed Transactions

Write a block that updates two remote databases in a single transaction.

DECLARE

    v_errmsg VARCHAR2(4000);

BEGIN

    — Update remote database #1

    UPDATE employees@db_link1

       SET salary = salary * 1.10

     WHERE department_id = 50;

    — Update remote database #2

    UPDATE employees@db_link2

       SET salary = salary * 1.05

     WHERE department_id = 60;

    — If all updates succeed, commit both together

    COMMIT;

EXCEPTION

    WHEN OTHERS THEN

        — Rollback all updates if any error occurs

        ROLLBACK;

        v_errmsg := SQLERRM;

        DBMS_OUTPUT.PUT_LINE(‘Error occurred: ‘ || v_errmsg);

END;

/

Exercise 29 – Parallel Execution

Invoke DBMS_PARALLEL_EXECUTE to process large data in chunks.

Suppose we have a table:

CREATE TABLE big_sales (

    sale_id     NUMBER PRIMARY KEY,

    amount      NUMBER,

    processed   CHAR(1) DEFAULT ‘N’

);

We want to update millions of rows, setting processed = 'Y' — but in parallel chunks for performance.

1.Create a Task

BEGIN

    DBMS_PARALLEL_EXECUTE.CREATE_TASK(‘process_sales_task’);

END;

/

2.Divide Table into Chunks by Rowid

BEGIN

    DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_ROWID(

        task_name   => ‘process_sales_task’,

        table_owner => ‘HR’,

        table_name  => ‘BIG_SALES’,

        by_row      => TRUE,

        chunk_size  => 10000  — rows per chunk

    );

END;

/

3. Define SQL to Run on Each Chunk

BEGIN

    DBMS_PARALLEL_EXECUTE.RUN_TASK(

        task_name      => ‘process_sales_task’,

        sql_stmt       => ‘UPDATE big_sales

                           SET processed = ”Y”

                           WHERE ROWID BETWEEN :start_id AND :end_id’,

        language_flag  => DBMS_SQL.NATIVE,

        parallel_level => 4  — run 4 workers in parallel

    );

END;

/

4.Monitor Task Progress

SELECT task_name, status, start_time, end_time

FROM   user_parallel_execute_tasks

WHERE  task_name = ‘process_sales_task’;

5.Drop the Task After Completion

BEGIN

    DBMS_PARALLEL_EXECUTE.DROP_TASK(‘process_sales_task’);

END;

/

     Exercise 30 – Advanced Optimizations

Profile PL/SQL code using DBMS_PROFILER and optimize it.

🔹 Step 1: Setup for Profiling

— Create profiler tables (one-time setup)

@$ORACLE_HOME/rdbms/admin/profload.sql

🔹 Step 2: Example PL/SQL Code (Before Optimization)

CREATE OR REPLACE PROCEDURE slow_sum IS

    v_total NUMBER := 0;

BEGIN

    FOR i IN 1..100000 LOOP

        v_total := v_total + i;

    END LOOP;

    DBMS_OUTPUT.PUT_LINE(‘Total: ‘ || v_total);

END;

/

🔹 Step 3: Profile the Code

— Start profiling

EXEC DBMS_PROFILER.START_PROFILER(‘Profiling slow_sum procedure’);

— Run the procedure

EXEC slow_sum;

— Stop profiling

EXEC DBMS_PROFILER.STOP_PROFILER;

🔹 Step 4: Analyze Profile Data

— Find runs

SELECT runid, run_comment, run_date

FROM   plsql_profiler_runs

ORDER  BY runid DESC;

— Check time spent in each unit

SELECT u.unit_name, d.total_occur, d.total_time

FROM   plsql_profiler_units u

JOIN   plsql_profiler_data d ON u.unit_number = d.unit_number

WHERE  u.unit_name = ‘SLOW_SUM’;

🔹 Step 5: Optimize the Code

CREATE OR REPLACE PROCEDURE fast_sum IS

    v_total NUMBER := 0;

    v_n     NUMBER := 100000;

BEGIN

    — Formula for sum of first N natural numbers = n(n+1)/2

    v_total := v_n * (v_n + 1) / 2;

    DBMS_OUTPUT.PUT_LINE(‘Total: ‘ || v_total);

END;

/

🔹 Step 6: Re-Profile the Optimized Code

EXEC DBMS_PROFILER.START_PROFILER(‘Profiling fast_sum procedure’);

EXEC fast_sum;

EXEC DBMS_PROFILER.STOP_PROFILER;

Exercise 31 – Polymorphic Table Functions (PTFs)

Goal: Create functions that adapt to input table structures.

CREATE OR REPLACE PACKAGE ptf_pkg AS

    FUNCTION describe(tab IN OUT DBMS_TF.table_t) RETURN DBMS_TF.describe_t;

    PROCEDURE fetch_rows;

END ptf_pkg;

/

— Example is simplified — PTFs are advanced features for dynamic query shaping

Try:

Implement a PTF that masks sensitive columns dynamically.

Exercise 32 – Secure Coding with Code Signing

Goal: Sign PL/SQL code for tamper detection.

— Create wallet and certificate outside PL/SQL

— Example command (done in OS):

— orapki wallet create

— In PL/SQL:

ALTER PROCEDURE sensitive_proc SIGN;

Try:

  • Test signature verification after altering code.

Exercise 33 – Application Context for Multi-Tenant Security

Goal: Dynamically filter data per tenant/user.

BEGIN

    DBMS_SESSION.SET_CONTEXT(‘app_ctx’, ‘tenant_id’, ‘T1001’);

END;

/

CREATE OR REPLACE FUNCTION tenant_policy_fn (

    schema_name VARCHAR2,

    table_name  VARCHAR2

) RETURN VARCHAR2 IS

BEGIN

    RETURN ‘tenant_id = SYS_CONTEXT(”app_ctx”, ”tenant_id”)’;

END;

      Try:

  • Switch tenant context and see filtered results.

Exercise 34 – Real-Time Streaming via Advanced Queuing

Goal: Use AQ to send messages between sessions.

BEGIN

    DBMS_AQADM.CREATE_QUEUE_TABLE(

        queue_table => ‘message_queue_table’,

        queue_payload_type => ‘SYS.AQ$_JMS_TEXT_MESSAGE’

    );

    DBMS_AQADM.CREATE_QUEUE(

        queue_name => ‘message_queue’,

        queue_table => ‘message_queue_table’

    );

    DBMS_AQADM.START_QUEUE(queue_name => ‘message_queue’);

END;

Try:

  • Publish and consume messages in different sessions.

Exercise 35 – Sharded Database PL/SQL

Goal: Execute PL/SQL in a sharded environment.

— Requires Oracle Sharding setup

— Example: Run on specific shard

EXECUTE IMMEDIATE ‘ALTER SESSION SET CONTAINER = shard1’;

Try:

  • Implement cross-shard data aggregation procedure.

Exercise 36 – PL/SQL Code Generation

Goal: Write PL/SQL that writes PL/SQL.

DECLARE

    v_code VARCHAR2(4000);

BEGIN

    v_code := ‘BEGIN DBMS_OUTPUT.PUT_LINE(”Generated Code Execution”); END;’;

    EXECUTE IMMEDIATE v_code;

END;

      Try:

  • Generate a procedure dynamically from metadata tables.

About the author

Avatar of shohal

shohal

I have profession and personal attachment with custom ERP Software development, Business Analysis, Project Management and Implementation almost (36) ,also Oracle Apex is my all-time favorite platform to developed the software. Moreover i have some website development experience with WordPress. For hand on networking experience DevOps and CCNA, it create me a full package. Here are some core programming language with networking course i have been worked: Oracle SQL ,PL/SQL,Oracle 19c Database , Oracle Apex 20.1,WordPress,Asp.Net ,MS SQL ,CCNA ,Dev Ops, SAP SD

Leave a Comment