PL/SQL Variables — Hard-Coded VARCHAR2 Overflows Batch
A column change from VARCHAR2(30) to VARCHAR2(60) caused a production ORA-06502 due to a hard-coded variable.
20+ years shipping high-throughput database systems. Drawn from code that ran under real load.
- PL/SQL Variables: named memory locations that hold values that can change during program execution
- Constants: fixed values declared with CONSTANT – must be initialized and cannot change
- Data Types: define the kind of data (scalar, composite, reference, LOB) and the operations allowed
- %TYPE anchors a variable to a column's data type – schema changes propagate automatically
- %ROWTYPE holds an entire row – reduces code maintenance when table structure changes
- Production trap: hard-coded VARCHAR2(20) breaks when the column expands to VARCHAR2(50) – ORA-06502
Think of PL/SQL Variables, Constants and Data Types as a powerful tool in your developer toolkit. Once you understand what it does and when to reach for it, everything clicks into place. Imagine you are running a professional kitchen. A 'Variable' is like a prep bowl where you store chopped onions temporarily before adding them to the pot. A 'Constant' is like a standardized measuring cup—its size never changes and everyone relies on it for accuracy. 'Data Types' are the labels on your storage containers (e.g., 'Liquid', 'Dry Goods', 'Perishable') that ensure you don't accidentally try to store a gallon of milk in a paper bag.
PL/SQL Variables, Constants and Data Types is a fundamental concept in Database development. In PL/SQL, every piece of information must be held in a named storage location with a specific format. By mastering these building blocks, you can create programs that are not only functional but also highly maintainable and less prone to data-related errors.
In this guide we'll break down exactly what PL/SQL Variables, Constants and Data Types is, why it was designed this way to ensure strict data integrity within the Oracle engine, and how to use it correctly in real projects.
By the end you'll have both the conceptual understanding and practical code examples to use PL/SQL Variables, Constants and Data Types with confidence.
What Is PL/SQL Variables, Constants and Data Types and Why Does It Exist?
PL/SQL Variables, Constants and Data Types is a core feature of PL/SQL. It was designed to solve the problem of temporary data storage during procedural execution. Unlike standard SQL, which processes sets of data, PL/SQL needs a way to hold individual values, calculation results, and database records in memory. Variables allow for dynamic changes, while Constants protect values that must remain fixed (like a tax rate or API version). Data types (Scalar, Large Object, Composite, and Reference) exist to tell the compiler how much memory to allocate and what operations are valid on that data.
This may seem obvious, but the real power lies in how PL/SQL enforces data integrity. A variable's type determines what you can do with it – you cannot accidentally store a date where a number is expected. The compiler catches these mismatches at compile time, not at 3 AM in production.
Common Mistakes and How to Avoid Them
When learning PL/SQL Variables, Constants and Data Types, most developers hit the same set of gotchas. A frequent mistake is hard-coding variable lengths (e.g., VARCHAR2(20)) instead of using %TYPE. If the database column later expands to 50 characters, your code will throw a 'Value Error'. Another common error is failing to initialize variables, leading to unexpected NULL logic in calculations.
Knowing these in advance saves hours of debugging production 'Buffer Overflow' and 'Constraint Violation' errors.
But there's a deeper one: using package-level global variables when local variables would suffice. These globals persist across calls within the same session, and if your code relies on their previous values, you're in for a debugging nightmare. A change in one procedure can affect another unintentionally.
Variables vs Constants – When to Use Each
Choosing between a variable and a constant is straightforward: use a constant when the value must never change. That sounds obvious, but developers often use variables for values that are logically fixed – like a maximum retry count, a cutoff date, or a conversion factor.
Constants improve readability and prevent accidental modification. They also help the compiler optimise memory usage. In PL/SQL, a constant must be declared with the CONSTANT keyword and initialized immediately. After that, any attempt to assign a new value raises a compilation error. That's a good thing – it's a contract your code makes with itself.
But there's a nuance: a constant can hold a value fetched from the database at declaration time, but that value becomes fixed for the duration of the block. If the underlying data changes, the constant still holds the old value. Use constants for truly static values, not for session-level configuration you might want to refresh.
Anchored Types: %TYPE and %ROWTYPE for Schema Resilience
Anchored types are PL/SQL's solution to the schema coupling problem. When you declare a variable with %TYPE, you tell the compiler: 'Find the data type of this column and use that.' If the column later changes, your code automatically adapts – no manual edits needed.
%ROWTYPE goes further: it declares a variable that holds an entire row. You can SELECT INTO a %ROWTYPE variable and access each column by name. This eliminates the need to list all columns individually. When you add a column to the table, your %ROWTYPE variables include it automatically (though you may need to adjust SELECT * to include the new column).
The performance cost is negligible – the compiler resolves the type at compile time, not runtime. The real cost saved is maintenance hours. A 200-line procedure that uses %ROWTYPE instead of 20 individual variable declarations is vastly easier to maintain and less error-prone.
But there's a catch: %ROWTYPE cannot be used for columns from joins – it only maps to a single table or cursor. For joins, you need to define a custom record type or use individual variables.
- %TYPE is a compile-time placeholder that resolves to the column's actual data type.
- If the column changes from VARCHAR2(30) to VARCHAR2(60), your variable changes too – no code change.
- %ROWTYPE bundles all columns into a single record – like a struct that auto-updates.
- Use %ROWTYPE for SELECT * INTO statements; never use individual variables for every column.
Data Type Categories and Their Memory Implications
PL/SQL data types fall into four categories: Scalar, Composite, Reference, and LOB (Large Object). Each has distinct memory and performance characteristics.
Scalar types (NUMBER, VARCHAR2, DATE, BOOLEAN) are the simplest – they hold a single value. A VARCHAR2 variable can hold up to 32,767 bytes in PL/SQL, but only 4,000 bytes in SQL. That's a common tripwire: you can build a large string in PL/SQL, but passing it to a SQL context truncates it silently. Use CLOB for larger text.
Composite types (records, collections) consume memory proportional to their elements. A collection of 100,000 NUMBER items can use hundreds of kilobytes. If you declare such a collection at the package level, it persists for the session and can lead to PGA memory exhaustion under high concurrency.
LOB types (CLOB, BLOB) are stored outside the PGA, with a locator in the variable. They're efficient for large data but have slower access than scalars. Reference types (REF CURSOR, REF) hold pointers to data – useful for passing result sets between programs.
Knowing these memory implications helps you avoid performance surprises. A naive variable choice can turn a fast procedure into a memory hog.
Scope, Lifetime, and Variable Visibility
A variable's scope determines where it can be accessed; its lifetime determines how long it exists. In PL/SQL, these depend on where you declare the variable.
Local variables inside a BEGIN-END block exist only during that block's execution. They are created when the block starts and destroyed when it ends. This is the safest scope – no unintended side effects.
Package-level variables declared in a package specification are visible to any caller. They live for the duration of the user session. If you modify a package variable in one procedure, any subsequent call to another procedure in the same session sees the modified value. This is both a feature (for session context like user ID) and a danger (for accidental state corruption).
Package body variables (declared in the body but not in the spec) are private – only procedures within the package can see them. That's a good middle ground: session-persistent but encapsulated.
The golden rule: declare variables at the smallest possible scope. Start with local. If you need to share state, use package body variables. Use package spec variables only when external callers need to read or set them.
Variable Declaration: Memory Allocation You Control
Declaring a variable isn't just syntax—it's a contract with the database engine. Every variable you declare in the declaration section carves out memory for its lifetime. The format is simple: variable_name [CONSTANT] datatype [NOT NULL] [:= | DEFAULT initial_value]. By default, every uninitialized variable is NULL. That seems harmless until you try to concatenate NULLs and get unexpected truncation. Use NOT NULL to enforce a value at compile time—catching logic errors before they hit production. Constrained declarations (like NUMBER(10,2)) use less memory than unconstrained ones (NUMBER). That matters when you declare a hundred variables in a bulk processing block. A real-world trap: mixing VARCHAR2 without a size limit. Oracle defaults to 4000 bytes, but upgrade to 12c+ and it can be 32767. Your code works in dev, but blows in production under heavy string loads. Always specify size.
Assigning SQL Query Results to PL/SQL Variables: The Silent Performance Killer
You can pump SQL query results straight into PL/SQL variables using SELECT INTO. It's elegant—until it's not. The SELECT INTO syntax SELECT column INTO variable FROM table WHERE ... works only when the query returns exactly one row. Zero rows throws NO_DATA_FOUND. More than one row throws TOO_MANY_ROWS. Both are unhandled exceptions that abort your block. That is why junior devs' batch jobs crater at midnight. Always wrap SELECT INTO in a BEGIN-EXCEPTION block. Better yet, use BULK COLLECT for multiple rows—it reduces context switches between SQL and PL/SQL engines from N to 1. A common incident: a WHERE clause on an unindexed column returns two rows instead of one in production, but passed all unit tests on a static test set. Your code must handle that. The pattern: log the exception, set a default, or raise a business exception. Never let it hit the user as ORA-01422.
SELECT INTO without exception handling is a live grenade. The moment data changes (e.g., a junior runs a merge that duplicates rows), your job breaks at 3 AM.The Silent VARCHAR2 Overflow That Took Down Month-End Processing
- Never hard-code variable lengths – use %TYPE to anchor to the source column.
- Always handle VALUE_ERROR in PL/SQL blocks that process data from external inputs.
- Schema migrations must include impact analysis on PL/SQL code, especially variable declarations.
SET SERVEROUTPUT ON;
EXEC DBMS_OUTPUT.PUT_LINE('Value: ' || v_some_variable);SELECT data_length FROM all_tab_columns WHERE table_name = 'SUPPLIERS' AND column_name = 'SUPPLIER_NAME';Key takeaways
Common mistakes to avoid
4 patternsHard-coding variable lengths instead of using %TYPE
Not initializing variables (assuming they default to 0 or empty string)
Using package-level variables when local variables suffice
Confusing PL/SQL VARCHAR2 max (32767) with SQL VARCHAR2 max (4000)
Interview Questions on This Topic
What is the difference between %TYPE and %ROWTYPE?
Frequently Asked Questions
20+ years shipping high-throughput database systems. Drawn from code that ran under real load.
That's PL/SQL. Mark it forged?
6 min read · try the examples if you haven't