course
Top 20 PL/SQL Interview Questions and Answers in 2025
PL/SQL, which is the Oracle’s procedural language extension of SQL, is something you have to know if you are going to be working with Oracle databases. This is true for major roles such as database administrator, developer, and data analyst, all of whom need to handle complex business logic within the database.
I believe interviewers assess candidates on three key aspects:
- Conceptual Understanding: Core principles, data types, control structures, and exception handling.
- Practical Coding Ability: Writing efficient stored procedures, functions, triggers, and packages.
- Performance Considerations: Optimizing PL/SQL code to improve execution time, minimize resource consumption, and avoid mistakes such as excessive context switching between SQL and PL/SQL.
To help, I am providing a guide on PL/SQL interview questions. I will begin with the fundamental concepts and move forward to more advanced topics like bulk processing, dynamic SQL, and performance tuning.
If you want to understand the foundational concepts of the Oracle database, I recommend taking our Introduction to Oracle SQL course, which is a really important resource to learn how to interact with the Oracle database and how the database uses PL/SQL to process queries.
Beginner PL/SQL Interview Questions
In the initial interview phase, the interviewer can ask foundational questions to assess your knowledge of basic database and PL/SQL concepts. Try studying these questions and answer to prepare for the initial phase of the interview.
1. What is PL/SQL?
PL/SQL is Oracle’s procedural extension to SQL. Unlike SQL, which is declarative and focuses on data retrieval and manipulation, PL/SQL allows developers to implement procedural logic, making it a powerful tool for writing complex business rules within the database. It supports variables, loops, conditionals, exception handling, and modular programming through procedures, functions, and packages.
2. What is the basic structure of a PL/SQL block?
A PL/SQL block is the fundamental unit of execution in PL/SQL, and it consists of four main sections:
-
DECLARE
(Optional): Used to define variables, constants, cursors, and user-defined types. -
BEGIN
: The executable section where SQL queries and procedural statements are written. -
EXCEPTION
(Optional): Handles runtime errors and exceptions to ensure graceful error recovery. -
END;
: Marks the end of the block.
3. What are the essential PL/SQL data types?
PL/SQL supports various data types, categorized as follows:
-
Scalar Types: Single-value types like
NUMBER
,VARCHAR2
,DATE
,BOOLEAN
. -
Composite Types: Collections such as
RECORD
(custom structures) andTABLE
/VARRAY
(arrays). -
Reference Types: Pointers to database objects, like
REF CURSOR
for dynamic query processing.
4. What are the basic control structures in PL/SQL?
PL/SQL includes several control structures that help manage the flow of a program:
-
Loops: These include
LOOP
,FOR LOOP
, andWHILE LOOP
, allowing repetitive execution of statements. -
Conditional Statements: These include
IF
andCASE
statements, which execute different blocks of code based on conditions. The DECODE() function is another good example of a conditional that is worth studying.
Intermediate PL/SQL Interview Questions
Having covered the basic questions, now let's move on to some intermediate-level data structure interview questions. After testing your basic knowledge, interviewers are going to now test your technical proficiency in implementing and using PL/SQL concepts.
5. What is the difference between stored procedures and functions?
Stored procedures and functions are both reusable PL/SQL code blocks, but they serve rather different purposes.
Stored procedures are used to perform operations that do not return a value, such as inserting, updating, or deleting data. They are used for tasks that modify data or perform complex operations without returning a result.
For example, the procedure below updates the salary
of the employee with the given employee_id
by adding the specified p_increment
such that the salary updates dynamically based on input parameters,
CREATE PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER) AS
BEGIN
-- Update the salary of the employee with the given ID
UPDATE employees
SET salary = salary + p_increment
WHERE employee_id = p_emp_id;
END;
Functions, on the other hand, return a value after performing operations. They are suitable for calculations or data retrieval that need to return a result.
The function below simplifies fetching an employee's salary, making it reusable in SQL queries or other procedures.
CREATE FUNCTION get_employee_salary(p_emp_id NUMBER) RETURN NUMBER AS
v_salary NUMBER;
BEGIN
-- Retrieve the salary for the given employee ID
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = p_emp_id;
-- Return the retrieved salary
RETURN v_salary;
END;
6. What are PL/SQL packages?
PL/SQL packages are collections of related procedures, functions, and variables that encapsulate code for better organization and reusability. They consist of two parts:
- Package Specification: Declares public elements (procedures, functions, variables).
- Package Body: Contains the implementation details of the procedures and functions declared in the specification.
For example, the query below creates a package employee_pkg
that defines a procedure to raise an employee's salary and a function to retrieve the total number of employees, with their implementations to be provided in the package body.
-- Create a package named 'employee_pkg'
CREATE PACKAGE employee_pkg AS
-- Procedure to increase an employee's salary by a percentage
PROCEDURE raise_salary(p_emp_id NUMBER, p_percent NUMBER);
-- Function to return the total number of employees
FUNCTION get_total_employees RETURN NUMBER;
END employee_pkg;
7. What are PL/SQL triggers?
Triggers are PL/SQL blocks that execute automatically in response to specific database events, such as insertions, updates, or deletions. Triggers are used to enforce business rules. They also are commonly used to perform auditing. They are categorized into:
- Row-Level Triggers: Execute once for each affected row.
- Statement-Level Triggers: Execute once per SQL statement, regardless of the number of rows affected.
The query below creates an AFTER UPDATE
trigger trg_salary_audit
on the employees
table that logs salary changes into the salary_audit table
, capturing the employee ID, old and new salary, and the update timestamp.
-- Create or replace a trigger 'trg_salary_audit'
CREATE OR REPLACE TRIGGER trg_salary_audit
AFTER UPDATE OF salary ON employees -- Fires after salary updates in 'employees' table
FOR EACH ROW -- Executes for each updated row
BEGIN
-- Inserts old and new salary details into 'salary_audit' table
INSERT INTO salary_audit (employee_id, old_salary, new_salary, change_date)
VALUES (:OLD.employee_id, :OLD.salary, :NEW.salary, SYSDATE);
END;
Study our tutorial on SQL Triggers so that you will be ready in the event that triggers are brought up in the interview.
8. What are the methods of exception handling in PL/SQL?
PL/SQL provides error-handling mechanisms for stability and to prevent crashes. The types of exceptions include the following:
-
Predefined Exceptions: Built-in exceptions like
NO_DATA_FOUND
,TOO_MANY_ROWS
, andZERO_DIVIDE
. -
User-Defined Exceptions: Custom exceptions are declared using
EXCEPTION
and raised usingRAISE
.
For example, the following PL/SQL block retrieves the salary of an employee with ID 100, raises a custom exception if the salary is below 1000, and handles possible errors, including missing employee records and unexpected exceptions.
DECLARE
v_salary NUMBER; -- Variable to store the employee's salary
e_low_salary EXCEPTION; -- Custom exception for low salary
BEGIN
-- Retrieve salary of employee with ID 100
SELECT salary INTO v_salary FROM employees WHERE employee_id = 100;
-- Raise exception if salary is below 1000
IF v_salary < 1000 THEN
RAISE e_low_salary;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN -- Handle case where employee ID is not found
DBMS_OUTPUT.PUT_LINE('Employee not found.');
WHEN e_low_salary THEN -- Handle custom low salary exception
DBMS_OUTPUT.PUT_LINE('Salary is below the allowed threshold.');
WHEN OTHERS THEN -- Handle any other unexpected errors
DBMS_OUTPUT.PUT_LINE('An unexpected error occurred: ' || SQLERRM);
END;
The best practices for exception handling include the following:
- Always anticipate potential exceptions.
- Use meaningful error messages.
- Log exceptions for auditing.
- Keep exception handling code concise and focused on recovery.
9. How can you verify whether an UPDATE statement is executed or not?
The SQL %NOTFOUND
attribute can be used to determine whether or not the UPDATE
statement successfully changed any records. If the last SQL statement run did not affect any rows, this variable returns TRUE
.
For example, the query below updates the salary of employees in department 10 by increasing it by 10%, and then checks whether any rows were affected by the UPDATE
statement using the %NOTFOUND
attribute. If no rows were updated, it outputs a message saying, "No rows were updated." If rows were updated, it outputs the number of rows that were affected using the SQL%ROWCOUNT
attribute.
DECLARE
-- Declare a variable to store the number of rows updated
rows_updated INTEGER;
BEGIN
-- Perform an UPDATE statement on the 'employees' table
UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 10;
-- Check if any rows were updated by using %NOTFOUND
IF SQL%NOTFOUND THEN
-- If no rows were updated, print a message
DBMS_OUTPUT.PUT_LINE('No rows were updated.');
ELSE
-- If rows were updated, print how many rows were affected
rows_updated := SQL%ROWCOUNT; -- Store the number of rows updated
DBMS_OUTPUT.PUT_LINE(rows_updated || ' rows were updated.');
END IF;
END;
/
Advanced PL/SQL Interview Questions
Let's now explore some advanced interview questions in case you are applying for a more senior role that requires more experience.
10. What are the methods of optimizing performance in PL/SQL?
Minimizing context switches between SQL and PL/SQL is crucial for optimizing performance. Each switch incurs overhead, which can slow down execution times, especially in situations involving frequent transitions between the two.
11. How do you use bulk operations to minimize context switches?
PL/SQL provides bulk processing techniques to optimize SQL-to-PL/SQL interaction by fetching or modifying multiple rows at once.
For example, the PL/SQL block below retrieves all employees from department 10 using BULK COLLECT
into a collection and iterates through it to print each employee's name, improving performance by minimizing context switches between SQL and PL/SQL.
DECLARE
-- Define a table-type collection based on the 'employees' table structure
TYPE emp_table IS TABLE OF employees%ROWTYPE;
v_emps emp_table; -- Declare a variable of this type
BEGIN
-- Bulk fetch employees from department 10 into the collection
SELECT * BULK COLLECT INTO v_emps FROM employees WHERE department_id = 10;
-- Loop through the collection and print employee names
FOR i IN 1..v_emps.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Employee: ' || v_emps(i).employee_name);
END LOOP;
END;
Also, this PL/SQL block uses FORALL
for bulk updating, increasing the salary by 10% for employees with IDs 101, 102, and 103.
DECLARE
-- Define a table-type collection for employee IDs
TYPE t_emp_ids IS TABLE OF employees.employee_id%TYPE;
-- Initialize collection with specific employee IDs
v_emp_ids t_emp_ids := t_emp_ids(101, 102, 103);
BEGIN
-- Bulk update salaries by 10% for specified employee IDs
FORALL i IN 1..v_emp_ids.COUNT
UPDATE employees SET salary = salary * 1.10 WHERE employee_id = v_emp_ids(i);
END;
12. What are dynamic SQL and Ref Cursors?
Dynamic SQL allows executing SQL statements dynamically at runtime, which is useful when dealing with variable table names, columns, or query structures.
The following PL/SQL block uses dynamic SQL to count the number of rows in the employees
table and prints the result. I like this kind of approach because it allows flexibility.
DECLARE
v_table_name VARCHAR2(50) := 'employees'; -- Store table name
v_count NUMBER; -- Variable to hold row count
BEGIN
-- Dynamically count rows in the specified table
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || v_table_name INTO v_count;
-- Print the total count
DBMS_OUTPUT.PUT_LINE('Total Employees: ' || v_count);
END;
Ref Cursors are dynamic cursors that can be opened, fetched, and closed at runtime, enabling the passing of query results between program units.
The PL/SQL block below uses a REF CURSOR
to fetch and print employee names from department 20. The cursor is opened dynamically, iterated through using a loop, and closed after processing.
DECLARE
-- Define a REF CURSOR type
TYPE emp_ref_cursor IS REF CURSOR;
v_cursor emp_ref_cursor; -- Declare a cursor variable
v_name employees.employee_name%TYPE; -- Variable to store employee name
BEGIN
-- Open the cursor for employees in department 20
OPEN v_cursor FOR SELECT employee_name FROM employees WHERE department_id = 20;
-- Fetch and print employee names in a loop
LOOP
FETCH v_cursor INTO v_name;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Employee: ' || v_name);
END LOOP;
-- Close the cursor
CLOSE v_cursor;
END;
13. How do you handle mutating table errors?
Mutating table errors occur when a trigger attempts to modify the table it is triggered on. A compound trigger allows breaking trigger execution into multiple phases such as BEFORE
, AFTER
, and FOR EACH ROW
to prevent direct modification issues.
The compound trigger below logs salary changes in the salary_audit
table efficiently by collecting data before each row update and performing a bulk insert after the statement, reducing context switches and improving performance.
CREATE OR REPLACE TRIGGER trg_salary_audit
FOR UPDATE OF salary ON employees -- Trigger fires on salary updates
COMPOUND TRIGGER
-- Declare an associative array to store audit records
TYPE t_salary_audit IS TABLE OF salary_audit%ROWTYPE INDEX BY PLS_INTEGER;
v_audit_data t_salary_audit;
v_idx PLS_INTEGER := 0;
-- Before updating each row, store old and new salary details
BEFORE EACH ROW IS
BEGIN
v_idx := v_idx + 1;
v_audit_data(v_idx).employee_id := :OLD.employee_id;
v_audit_data(v_idx).old_salary := :OLD.salary;
v_audit_data(v_idx).new_salary := :NEW.salary;
v_audit_data(v_idx).change_date := SYSDATE;
END BEFORE EACH ROW;
-- After the statement, insert all audit records in bulk
AFTER STATEMENT IS
BEGIN
FORALL i IN 1..v_idx
INSERT INTO salary_audit VALUES v_audit_data(i);
END AFTER STATEMENT;
END trg_salary_audit;
14. What is overloading in PL/SQL?
Overloading allows multiple procedures or functions with the same name but different parameters to be defined within a package. This enhances code readability and maintainability by providing multiple ways to perform similar operations.
In the query below, the package body implements two overloaded procedures named update_salary
: One increases an employee's salary by a specified amount, while the other sets a new salary with an effective date, updating the employees
table accordingly.
-- Create the package specification
CREATE OR REPLACE PACKAGE emp_pkg AS
-- Procedure to increment salary by a specified amount
PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER);
-- Overloaded procedure to set a new salary with an effective date
PROCEDURE update_salary(p_emp_id NUMBER, p_new_salary NUMBER, p_effective_date DATE);
END emp_pkg;
/
-- Create the package body
CREATE OR REPLACE PACKAGE BODY emp_pkg AS
-- Procedure to increment salary by a specified amount
PROCEDURE update_salary(p_emp_id NUMBER, p_increment NUMBER) AS
BEGIN
UPDATE employees
SET salary = salary + p_increment
WHERE employee_id = p_emp_id;
END update_salary;
-- Overloaded procedure to set a new salary with an effective date
PROCEDURE update_salary(p_emp_id NUMBER, p_new_salary NUMBER, p_effective_date DATE) AS
BEGIN
UPDATE employees
SET salary = p_new_salary, last_update = p_effective_date
WHERE employee_id = p_emp_id;
END update_salary;
END emp_pkg;
/
15. What are the compiler directives and pragmas in PL/SQL?
PL/SQL provides compiler directives (PRAGMA
) to optimize code and handle exceptions. The common pragmas include:
-
PRAGMA EXCEPTION_INIT
: Associates a user-defined exception with an Oracle error code. -
PRAGMA SERIALLY_REUSABLE
: Optimizes package memory usage for scalability.
The PL/SQL block below handles the insertion of an employee with an invalid salary by using a custom exception e_invalid_salary
mapped to error code -20001
. If the exception is raised, it prints an error message.
DECLARE
e_invalid_salary EXCEPTION; -- Declare custom exception for invalid salary
PRAGMA EXCEPTION_INIT(e_invalid_salary, -20001); -- Associate exception with error code -20001
BEGIN
-- Attempt to insert an employee with invalid salary
INSERT INTO employees (employee_id, salary) VALUES (999, -1000);
EXCEPTION
-- Handle the custom exception and print a message
WHEN e_invalid_salary THEN
DBMS_OUTPUT.PUT_LINE('Invalid salary detected!');
END;
16. What are the different methods to trace and debug PL/SQL code?
PL/SQL provides several built-in packages for tracing and debugging code performance. Common methods include using DBMS_TRACE
to track execution flow, DBMS_APPLICATION_INFO
to monitor session activity, and DBMS_SESSION
to gather session-level diagnostic information.
The query below first enables SQL tracing using DBMS_SESSION.set_sql_trace(TRUE)
, then sets client-specific session information with DBMS_APPLICATION_INFO.set_client_info()
for monitoring. The PL/SQL block is executed, which simulates an UPDATE
operation. Finally, it disables the tracing after the execution using DBMS_SESSION.set_sql_trace(FALSE)
.
-- Enable tracing for the current session using DBMS_SESSION
BEGIN
-- Start session-level tracing
DBMS_SESSION.set_sql_trace(TRUE);
END;
/
-- Set application information using DBMS_APPLICATION_INFO
BEGIN
-- Set the application name and action for session monitoring
DBMS_APPLICATION_INFO.set_client_info('Trace Debug Session');
DBMS_APPLICATION_INFO.set_action('Debugging PL/SQL Code');
END;
/
-- Example PL/SQL block that simulates a process for debugging
DECLARE
v_employee_id NUMBER := 100;
BEGIN
-- Example query to fetch employee details
FOR rec IN (SELECT first_name, last_name FROM employees WHERE employee_id = v_employee_id) LOOP
DBMS_OUTPUT.put_line('Employee: ' || rec.first_name || ' ' || rec.last_name);
END LOOP;
-- Simulate some logic that could be traced
IF v_employee_id = 100 THEN
DBMS_OUTPUT.put_line('Employee ID is 100');
END IF;
END;
/
-- Disable tracing after the session is complete
BEGIN
-- Stop session-level tracing
DBMS_SESSION.set_sql_trace(FALSE);
END;
/
Scenario-Based PL/SQL Interview Questions
In real-world enterprise applications, PL/SQL is used to handle complex business logic. Interviewers often assess a candidate’s ability to apply PL/SQL in practical scenarios. Below are some key challenges, along with strategies to solve them.
For this next and final section, if you are interviewing, I would encourage you to think about how to answer questions following this template, but try, of course, to re-orient your answer for your specific industry or to reflect your own experience. The same goes if you are interviewing a candidate and want ideas on how to ask a question and judge how thorough the answer is, in which case I hope this section serves as inspiration for both your questions and evaluation criteria.
Boost Your Team's SQL Proficiency
Train your team in SQL with DataCamp for Business. Comprehensive training, hands-on projects, and detailed performance metrics for your organization.

17. How do you design triggers in heavily concurrent environments?
Assume a scenario where you need to enforce a business rule where an employee’s salary cannot be updated more than once per day. However, the database experiences high transaction concurrency, and a simple trigger could lead to contention or performance issues.
Instead of using a row-level trigger that fires for every update and can slow down performance, use a statement-level trigger with a log table to prevent multiple salary updates within the same day.
For example, the trigger below prevents multiple salary updates for an employee on the same day by checking the salary_update_log
table before allowing an update. If the salary has already been updated today, an error is raised; otherwise, the update date is logged.
-- Create a table to log the last salary update for each employee
CREATE TABLE salary_update_log (
employee_id NUMBER PRIMARY KEY, -- Employee ID as primary key
last_update DATE -- Date of the last salary update
);
-- Create or replace a compound trigger to prevent multiple salary updates on the same day
CREATE OR REPLACE TRIGGER trg_prevent_multiple_salary_update
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
-- Declare a variable to store last salary update date per row
TYPE emp_log_type IS TABLE OF DATE INDEX BY PLS_INTEGER;
emp_log emp_log_type;
BEFORE STATEMENT IS
BEGIN
-- Load existing salary update logs into memory for reference
FOR rec IN (SELECT employee_id, last_update FROM salary_update_log) LOOP
emp_log(rec.employee_id) := rec.last_update;
END LOOP;
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
-- Check if an entry exists for this employee
IF emp_log.EXISTS(:NEW.employee_id) THEN
-- Validate if salary was updated today
IF emp_log(:NEW.employee_id) = TRUNC(SYSDATE) THEN
RAISE_APPLICATION_ERROR(-20010, 'Salary can only be updated once per day');
ELSE
-- Update log in memory
emp_log(:NEW.employee_id) := TRUNC(SYSDATE);
END IF;
ELSE
-- Insert a new log entry into memory
emp_log(:NEW.employee_id) := TRUNC(SYSDATE);
END IF;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
-- Apply changes to the log table after all row updates
FOR i IN emp_log.FIRST .. emp_log.LAST LOOP
MERGE INTO salary_update_log l
USING (SELECT i AS employee_id, emp_log(i) AS last_update FROM DUAL) s
ON (l.employee_id = s.employee_id)
WHEN MATCHED THEN
UPDATE SET l.last_update = s.last_update
WHEN NOT MATCHED THEN
INSERT (employee_id, last_update) VALUES (s.employee_id, s.last_update);
END LOOP;
END AFTER STATEMENT;
END trg_prevent_multiple_salary_update;
The above example would reduce row-level locking and minimize transaction conflicts during bulk updates.
18. How do you split large transactions into smaller chunks for reliability?
Imagine a scenario where a banking system requires a bulk update to adjust interest rates for millions of customer accounts. Executing a single large transaction might lock tables for too long or lead to rollback failures.
For this case, use bulk processing with COMMIT
in batches to process the data incrementally and avoid contention.
The PL/SQL block below implements the solution by using BULK COLLECT
to fetch account IDs and then iterates through them to update the interest rate by 5%. It commits the changes after every 1000 updates to improve performance and reduce resource usage. A final commit ensures any remaining updates are saved.
DECLARE
-- Define a collection type for account IDs
TYPE t_accounts IS TABLE OF NUMBER; -- Use NUMBER instead of referencing accounts.account_id
v_account_ids t_accounts; -- Variable to store account IDs
v_batch_size CONSTANT NUMBER := 1000; -- Batch size for commits
BEGIN
-- Bulk collect all account IDs into the collection
SELECT account_id BULK COLLECT INTO v_account_ids FROM accounts;
-- Loop through each account ID to update the interest rate
FOR i IN 1 .. v_account_ids.COUNT LOOP
UPDATE accounts
SET interest_rate = interest_rate * 1.05 -- Increase interest rate by 5%
WHERE account_id = v_account_ids(i);
-- Commit after every 1000 updates
IF MOD(i, v_batch_size) = 0 THEN
COMMIT;
END IF;
END LOOP;
COMMIT; -- Final commit for any remaining updates
END;
/
This solution would ensure reliability in financial systems like payroll by preventing transaction failures.
19. How do you organize complex logic into packages for maintainability?
A retail company needs to implement an order processing system that involves multiple steps: Checking inventory availability, reserving stock, calculating discounts, and logging transaction history.
Instead of writing separate standalone procedures, you need to organize this logic in a structured, maintainable way. Therefore, use PL/SQL packages to encapsulate related procedures and functions, improving code reusability and maintainability
The package specification below defines functions and procedures for order processing, including checking inventory, reserving stock, calculating discounts, and logging transaction statuses. It provides a modular approach to handling order-related tasks.
-- Create a package specification for order processing functions and procedures
CREATE PACKAGE order_processing_pkg AS
-- Function to check if enough inventory is available for the product
FUNCTION check_inventory(p_product_id NUMBER, p_quantity NUMBER) RETURN BOOLEAN;
-- Procedure to reserve stock for a specific order
PROCEDURE reserve_stock(p_order_id NUMBER, p_product_id NUMBER, p_quantity NUMBER);
-- Function to calculate discount based on customer and total amount
FUNCTION calculate_discount(p_customer_id NUMBER, p_total_amount NUMBER) RETURN NUMBER;
-- Procedure to log the status of a transaction
PROCEDURE log_transaction(p_order_id NUMBER, p_status VARCHAR2);
END order_processing_pkg;
We then use the following package body to implement the functions and procedures defined in the package specification. It handles key tasks like checking inventory availability, reserving stock, calculating discounts, and logging transaction statuses, ensuring smooth order processing operations.
-- Create the package body implementing the functions and procedures for order processing
CREATE PACKAGE BODY order_processing_pkg AS
-- Function to check if sufficient inventory is available for the product
FUNCTION check_inventory(p_product_id NUMBER, p_quantity NUMBER) RETURN BOOLEAN AS
v_available_qty NUMBER; -- Variable to store available quantity
BEGIN
-- Retrieve the available quantity from inventory
SELECT stock_quantity INTO v_available_qty FROM inventory WHERE product_id = p_product_id;
-- Return true if enough stock is available, otherwise false
RETURN v_available_qty >= p_quantity;
END check_inventory;
-- Procedure to reserve stock for a specific order
PROCEDURE reserve_stock(p_order_id NUMBER, p_product_id NUMBER, p_quantity NUMBER) AS
BEGIN
-- Deduct the ordered quantity from the inventory
UPDATE inventory SET stock_quantity = stock_quantity - p_quantity WHERE product_id = p_product_id;
END reserve_stock;
-- Function to calculate a discount based on the total order amount
FUNCTION calculate_discount(p_customer_id NUMBER, p_total_amount NUMBER) RETURN NUMBER AS
v_discount NUMBER := 0; -- Initialize discount to 0
BEGIN
-- Apply 10% discount if the total amount is greater than 500
IF p_total_amount > 500 THEN
v_discount := p_total_amount * 0.10;
END IF;
-- Return the calculated discount
RETURN v_discount;
END calculate_discount;
-- Procedure to log the transaction status
PROCEDURE log_transaction(p_order_id NUMBER, p_status VARCHAR2) AS
BEGIN
-- Insert a log entry for the order status
INSERT INTO order_log (order_id, status, log_date) VALUES (p_order_id, p_status, SYSDATE);
END log_transaction;
END order_processing_pkg;
20. How do you handle deadlocks in high-transaction systems?
Assume a scenario where a financial system frequently updates multiple related tables simultaneously. Deadlocks occur when two transactions wait on each other’s locked resources, causing performance bottlenecks.
To solve this problem, always lock rows in a consistent order across transactions. Also, use the NOWAIT
or SKIP LOCKED
clause to prevent waiting indefinitely.
For example, the PL/SQL block below attempts to lock a specific row in the accounts table for the update using the FOR UPDATE NOWAIT
clause, which causes the transaction to fail immediately if another session already locks the row. After locking, it updates the transaction status and commits the changes. If an error occurs, it catches the exception and prints an error message.
DECLARE
v_balance NUMBER(15,2); -- Declare variable to store the account balance
BEGIN
-- Lock the account row for update to prevent other sessions from modifying it
SELECT balance INTO v_balance FROM accounts
WHERE account_id = 101 FOR UPDATE NOWAIT;
-- Update the transaction status to 'Processed' for all transactions related to this account
UPDATE transactions
SET status = 'Processed'
WHERE account_id = 101 AND status = 'Pending'; -- Update only pending transactions
COMMIT; -- Commit the changes
EXCEPTION
-- Handle errors, such as locking issues or unexpected exceptions
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Transaction failed: ' || SQLERRM);
ROLLBACK; -- Ensure rollback in case of failure
END;
/
Similarly, this PL/SQL block processes pending transactions by locking them for update with FOR UPDATE SKIP LOCKED
, allowing it to skip over rows that are already locked by other sessions. It updates each transaction's status to 'Processing' and commits the changes at the end.
DECLARE
-- Declare a cursor to select pending transactions and lock rows for update
CURSOR c_pending_txns IS
SELECT transaction_id FROM transactions WHERE status = 'Pending' FOR UPDATE SKIP LOCKED; -- Skip locked rows
BEGIN
-- Loop through the pending transactions
FOR txn IN c_pending_txns LOOP
-- Update the status of each transaction to 'Processing'
UPDATE transactions SET status = 'Processing' WHERE transaction_id = txn.transaction_id;
END LOOP;
COMMIT; -- Commit the changes to finalize the transaction updates
END;
Conclusion and Further Resources
For Oracle-centric roles, employers look for candidates who can write correct PL/SQL code and handle real-life challenges, and optimize database performance. I recommend checking out official Oracle documentation on PL/SQL Language Reference for an in-depth guide and best practices on PL/SQL. The Oracle Live SQL (Interactive PL/SQL Playground) provides access to Oracle’s cloud-based platform for running PL/SQL queries online. I also encourage you to join the Oracle Community to engage with Oracle experts and developers and the Stack Overflow forum to find solutions to PL/SQL-related questions.
However, nothing beats a structured learning path. For this reason, I highly, highly recommend taking our Introduction to Oracle SQL course, which is really, in my opinion, the best way to really become an expert. Also, if you want to advance your knowledge of working with databases, I also recommend taking our Database Design course, where you will learn to create and manage databases and select the appropriate DBMS for your needs. Finally, try our Associate Data Engineer in SQL career track to learn the fundamentals of data engineering and data warehousing.
Last but not least, if you are interviewing candidates and you want to also upskill others, connect with our DataCamp for Business team. DataCamp can upskill entire teams with hands-on projects and custom learning paths. This is a great way for employers to bridge skill gaps and boost a team’s confidence and credibility, so connect with our team today.
Data Upskilling For Your Business
Enhance your team's data literacy and decision-making capabilities with DataCamp for Business. Access diverse courses, hands-on projects, and centralized insights for teams of 2 or more.

PL SQL FAQs
What is PL/SQL, and how does it differ from SQL?
PL/SQL is a procedural language developed by Oracle, allowing control structures and error handling, while SQL is declarative and handles data retrieval/manipulation.
What is a PL/SQL package?
A collection of related procedures, functions, variables, and cursors for modular programming and reusability.
How is a procedure different from a function?
Procedures perform an action that doesn’t return a value, while functions return a value the can be used in queries.
What are triggers in PL/SQL?
Automated actions executed before or after INSERT
, UPDATE
, DELETE
operations.
What is bulk processing in PL/SQL?
Bulk processing (BULK COLLECT
, FORALL
) enhances performance by reducing context switches between PL/SQL and SQL engines when handling large datasets.
Learn SQL with DataCamp
course
Data Manipulation in SQL
course
Applying SQL to Real-World Problems
blog
Top 30 SQL Server Interview Questions (2025)
Kevin Babitz
14 min

blog
Top 85 SQL Interview Questions and Answers for 2025
blog
The 36 Top Python Interview Questions & Answers For 2025
blog
Top 30 Database Administrator Interview Questions for 2025
blog
Top 34 MySQL Interview Questions and Answers For 2025

Laiba Siddiqui
35 min

blog