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.
Our SQL Transactions guide covers COMMIT, ROLLBACK, SAVEPOINT, and isolation levels in depth — all concepts that surface in scenario-based interview questions.
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;
Final Thoughts
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 the official Oracle documentation on PL/SQL Language Reference and our SQL Injection tutorial — SQL injection through dynamic PL/SQL is a common senior-level interview topic for an in-depth guide and best practices on PL/SQL. The interactive Oracle FreeSQL 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 recommend our Introduction to Oracle SQL course as the most structured path from SQL fundamentals to expert-level Oracle programming. 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. Teams get structured curricula, hands-on projects, and progress tracking in one place. Connect with our’s confidence and credibility, so connect with our team today.
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 that 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.
What is the difference between BULK COLLECT and FORALL in PL/SQL?
BULK COLLECT retrieves multiple rows from SQL into a PL/SQL collection in a single round trip, replacing row-by-row SELECT INTO statements. FORALL performs bulk DML (insert, update, delete) by sending a collection of values to the SQL engine in one batch. Use BULK COLLECT for reading data and FORALL for writing it. Both reduce context switches between the SQL and PL/SQL engines, which is the main source of performance overhead in data-intensive PL/SQL blocks.
How do you prevent SQL injection in PL/SQL?
The main risk in PL/SQL is dynamic SQL built by concatenating user input into a string for EXECUTE IMMEDIATE. Use bind variables instead: EXECUTE IMMEDIATE 'SELECT ... WHERE id = :id' USING p_id. Bind variables are never interpreted as SQL syntax, so injected code cannot execute. For cases where the statement structure must vary dynamically (table names, column names), use a whitelist of allowed values validated before concatenation, and apply least-privilege database grants to limit what an attacker could do even if injection occurred.



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:
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 covers how SQL and PL/SQL interact within Oracle’s execution engine.
TL;DR
DECLARE,BEGIN,EXCEPTION,END), scalar/composite/reference data types, and control structures%NOTFOUNDBULK COLLECT,FORALL), dynamic SQL withEXECUTE IMMEDIATE, compound triggers, andPRAGMAdirectivesNOWAIT/SKIP LOCKEDBeginner 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.
For a direct comparison of how the two languages differ in practice, see our Difference Between SQL and PL/SQL tutorial.
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 CURSORfor 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
IFandCASEstatements, 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 PL/SQL interview questions. Interviewers at this stage test your technical proficiency in implementing and using PL/SQL concepts in practice.
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
salaryof the employee with the givenemployee_idby adding the specifiedp_incrementsuch that the salary updates dynamically based on input parameters,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.
EXEC procedure_name(...)SELECT function_name(...) FROM DUALOur User Defined Stored Procedures in SQL tutorial covers naming conventions, parameter modes, and real-world patterns in more depth.
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:
For example, the query below creates a package
employee_pkgthat 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.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:
The query below creates an
AFTER UPDATEtriggertrg_salary_auditon theemployeestable that logs salary changes into thesalary_audit table, capturing the employee ID, old and new salary, and the update timestamp.Study our tutorial on SQL Triggers so that you will be ready in the event that triggers are brought up in the interview.
One trigger type not yet mentioned is the INSTEAD OF trigger. Unlike row- and statement-level triggers, INSTEAD OF triggers fire in place of a DML statement on a view, letting you write logic that handles the operation on the underlying base tables. They are the standard way to make non-updatable views accept
INSERT,UPDATE, andDELETEoperations.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
EXCEPTIONand 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.
The best practices for exception handling include the following:
9. How can you verify whether an UPDATE statement is executed or not?
The SQL
%NOTFOUNDattribute can be used to determine whether or not theUPDATEstatement successfully changed any records. If the last SQL statement run did not affect any rows, this variable returnsTRUE.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
UPDATEstatement using the%NOTFOUNDattribute. 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 theSQL%ROWCOUNTattribute.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?
PL/SQL performance improvement centres on five techniques.
BULK COLLECT/FORALL): Fetch or modify rows in batches instead of one at a time. Q11 shows both in practice.LIMITclause withBULK COLLECT: UseBULK COLLECT INTO v_coll LIMIT 500to cap memory per batch. Without it, Oracle loads the full result set into memory at once, which causes problems on large tables.NOCOPYhint: Pass largeIN OUTparameters by reference rather than by value. A deep copy of a 50 MB collection on every call is expensive;NOCOPYskips it.PLSQL_CODE_TYPE = NATIVEat the session or system level. Oracle compiles PL/SQL to machine code instead of bytecode, which cuts CPU time on computation-heavy routines.RESULT_CACHE: Add this pragma to deterministic functions that are called repeatedly with the same inputs. Oracle returns the cached result without re-executing the function body.For a broader look at the SQL performance techniques that complement PL/SQL tuning, see our SQL Query Optimization guide covering 15 techniques including index design and query rewriting.
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 COLLECTinto a collection and iterates through it to print each employee's name, improving performance by minimizing context switches between SQL and PL/SQL.Also, this PL/SQL block uses
FORALLfor bulk updating, increasing the salary by 10% for employees with IDs 101, 102, and 103.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
employeestable and prints the result. I like this kind of approach because it allows flexibility.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 CURSORto fetch and print employee names from department 20. The cursor is opened dynamically, iterated through using a loop, and closed after processing.For a dedicated deep dive including parameterised queries and the
DBMS_SQLpackage, see our Dynamic SQL tutorial.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, andFOR EACH ROWto prevent direct modification issues.The compound trigger below logs salary changes in the
salary_audittable efficiently by collecting data before each row update and performing a bulk insert after the statement, reducing context switches and improving performance.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 theemployeestable accordingly.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_salarymapped to error code-20001. If the exception is raised, it prints an error message.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_TRACEto track execution flow,DBMS_APPLICATION_INFOto monitor session activity, andDBMS_SESSIONto 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 withDBMS_APPLICATION_INFO.set_client_info()for monitoring. The PL/SQL block is executed, which simulates anUPDATEoperation. Finally, it disables the tracing after the execution usingDBMS_SESSION.set_sql_trace(FALSE).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.