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
✦ Definition~90s read
What is PL/SQL Variables, Constants and Data Types?
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.
★
Think of PL/SQL Variables, Constants and Data Types as a powerful tool in your developer toolkit.
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.
Plain-English First
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.
⚙ Browser compatibility
Latest versions — ✓ supported
Chrome
Firefox
Safari
Edge
✓
✓
✓
✓
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.
ForgeVariableBasics.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- io.thecodeforge: Standard Variable and Constant DeclarationDECLARE-- Scalar Variable
v_employee_name VARCHAR2(100) := 'TheCodeForge Developer';
-- Constant: Must be initialized and cannot be changed
gc_tax_rate CONSTANTNUMBER(3,2) := 0.05;
-- Anchored Type: Dynamically matches the column type in the DB
v_project_id forge_projects.project_id%TYPE;
-- Rowtype: Holds an entire row from a table
v_project_rec forge_projects%ROWTYPE;
BEGIN
v_project_id := 101;
-- Selective AssignmentSELECT * INTO v_project_rec
FROM forge_projects
WHERE project_id = v_project_id;
DBMS_OUTPUT.PUT_LINE('Processing: ' || v_project_rec.project_name);
END;
Output
Processing: Forge Core API
💡Key Insight:
The most important thing to understand about PL/SQL Variables, Constants and Data Types is the problem it was designed to solve. Always ask 'why does this exist?' before asking 'how do I use it?' Use anchored types (%TYPE) as much as possible to ensure your code remains 'linked' to your database schema changes.
📊 Production Insight
Hard-coded variable lengths are the #1 cause of production VALUE_ERROR bugs after schema migrations.
When a column expands from VARCHAR2(30) to VARCHAR2(60), every hard-coded VARCHAR2(30) variable in your codebase silently becomes a ticking bomb.
Rule: always use %TYPE for variables that read from database columns.
🎯 Key Takeaway
Variables store temporary data; constants protect fixed values.
Data types enforce compile-time integrity – let the compiler catch your mistakes.
Anchored types (%TYPE, %ROWTYPE) keep your code in sync with the schema automatically.
thecodeforge.io
Plsql Variables Datatypes
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.
CommonMistakes.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- io.thecodeforge: Avoiding hard-coded data types-- WRONG: Risk of ORA-06502 if the DB column expands-- v_name VARCHAR2(10);-- CORRECT: Using %TYPE to stay synchronizedDECLARE
v_forge_user_name forge_users.username%TYPE;
v_current_total NUMBER(10,2) := 0.00; -- Initialize to avoid NULL arithmeticBEGINSELECT username INTO v_forge_user_name
FROM forge_users
WHERE user_id = 1;
v_current_total := v_current_total + 150.50;
END;
Output
// Variable v_forge_user_name automatically adopts the DB column width.
⚠ Watch Out:
The most common mistake with PL/SQL Variables, Constants and Data Types is using it when a simpler alternative would work better. Avoid creating 'Global' variables in package specifications unless they truly need to persist across the session, as this can lead to memory bloat and difficult-to-trace state changes.
📊 Production Insight
Package-level variables that persist across calls are a frequent source of 'action at a distance' bugs.
A stored procedure called twice in the same session may behave differently if the global variable was mutated by another procedure.
Rule: prefer local variables inside procedures; use package globals only for true session-level state like a user context.
🎯 Key Takeaway
Hard-code variable sizes = schedule a production outage.
Uninitialized variables default to NULL – NULL + number = NULL.
Package globals are shared state; they're useful but dangerous – use sparingly.
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.
VariablesVsConstants.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- io.thecodeforge: Proper use of constants vs variablesDECLARE-- Constant: tax rate never changes mid-calculation
gc_tax_rate CONSTANTNUMBER(3,2) := 0.07;
-- Variable: running total changes
v_gross_amount NUMBER(10,2) := 0.00;
v_net_amount NUMBER(10,2) := 0.00;
BEGIN
v_gross_amount := 1150.00;
v_net_amount := v_gross_amount * (1 - gc_tax_rate);
DBMS_OUTPUT.PUT_LINE('Net: ' || v_net_amount);
-- This would fail to compile: gc_tax_rate := 0.06;END;
Output
Net: 1069.5
🔥Why Constants Matter:
Constants turn a 'don't change this' comment into a compiler-enforced rule. They also make code self-documenting – a reader immediately knows this value is a fixed reference.
📊 Production Insight
Using a variable for a logically constant value opens the door for accidental overwrites by future developers.
In one incident, a 'variable' that was supposed to hold the fiscal year start date was changed inadvertently by a batch process, causing all reports for the next month to use the wrong date.
Rule: if the value must never change, declare it CONSTANT.
🎯 Key Takeaway
If it should not change, make it CONSTANT – the compiler will enforce it.
Constants are documentation that cannot lie.
Initialise constants at declaration; they cannot be assigned later.
thecodeforge.io
Plsql Variables Datatypes
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.
AnchoredTypes.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- io.thecodeforge: Using %TYPE and %ROWTYPE for resilienceDECLARE-- %TYPE: single column
v_order_total forge_orders.total_amount%TYPE;
-- %ROWTYPE: entire row
v_customer_rec forge_customers%ROWTYPE;
BEGINSELECT total_amount INTO v_order_total
FROM forge_orders
WHERE order_id = 1001;
SELECT * INTO v_customer_rec
FROM forge_customers
WHERE customer_id = 202;
DBMS_OUTPUT.PUT_LINE('Customer: ' || v_customer_rec.name || ' - Order Total: ' || v_order_total);
END;
Output
Customer: Acme Corp - Order Total: 4500.75
Mental Model
Think of %TYPE as a Smart Pointer
Just as a smart pointer in C++ auto-adapts to the object type, %TYPE adapts to the column type.
%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.
📊 Production Insight
A schema migration that changes a column length will silently break every hard-coded variable that reads from it.
Without %TYPE, you're forced to grep through all PL/SQL code and manually update lengths – a slow and error-prone process.
Rule: if a variable holds data from a column, use %TYPE. Period.
🎯 Key Takeaway
%TYPE ties your variable to the column – schema changes propagate automatically.
%ROWTYPE captures an entire row – less code, fewer merge conflicts.
Anchored types do not hurt performance – they save maintenance hours.
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.
DataTypesMemory.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- io.thecodeforge: Understanding data type memoryDECLARE-- Scalar: small, fast
v_small VARCHAR2(100) := 'Hi';
-- Composite: collection can be largeTYPE num_table ISTABLEOFNUMBERINDEXBY PLS_INTEGER;
v_big num_table;
-- LOB: large data, uses locator
v_clob CLOB := 'This is large text...';
-- Reference: pointer to cursor
v_cursor SYS_REFCURSOR;
BEGIN-- Fill collection with 10000 numbersFOR i IN1..10000LOOPv_big(i) := i;
ENDLOOP;
DBMS_OUTPUT.PUT_LINE('Count: ' || v_big.COUNT);
-- Open ref cursorOPEN v_cursor FOR'SELECT * FROM dual';
END;
Output
Count: 10000
⚠ Memory Trap:
Package-level collections persist for the entire user session. If you load 100,000 rows into a PL/SQL table and the procedure is called 100 times concurrently, you can exhaust the shared pool or PGA. Always assess concurrency when choosing data structures.
📊 Production Insight
A VARCHAR2(32000) declared in a package body will consume heap space once, but if you use an associative array of 1000 strings each 1KB, that's ~1MB per session.
With 200 concurrent sessions, that's 200MB of PGA – enough to cause ORA-04030 (out of memory).
Rule: batch load collections with LIMIT clause; use LOBs for truly large text.
🎯 Key Takeaway
Scalar types are small; composite types can be huge – know your memory budget.
PL/SQL VARCHAR2 max is 32767, but SQL VARCHAR2 max is 4000 – silent truncation awaits.
LOB type variables use locators, not the full data – efficient for large content.
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.
ScopeLifetime.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- io.thecodeforge: Scope and lifetime examplesCREATEORREPLACEPACKAGE forge_session_ctx IS-- Package spec variable: visible to all callers, session lifetime
g_user_id NUMBER;
g_user_role VARCHAR2(30);
END forge_session_ctx;
/
CREATEORREPLACEPACKAGEBODY forge_session_ctx IS-- Private package body variable: only visible inside this package
g_last_access TIMESTAMP;
PROCEDUREset_context(p_user_id NUMBER, p_role VARCHAR2) IS-- Local variable: only visible within this procedure
v_local_counter NUMBER := 0;
BEGIN
g_user_id := p_user_id;
g_user_role := p_role;
g_last_access := SYSTIMESTAMP;
v_local_counter := v_local_counter + 1;
END;
END forge_session_ctx;
Output
// Package compiled successfully
🔥Visibility Rule:
A variable declared in a package spec is effectively a global variable. Use it sparingly and document the intended lifecycle. Prefer passing parameters over relying on global state.
📊 Production Insight
A package spec variable reset by one procedure but read by another creates hidden dependencies that are invisible in the calling code.
This caused a production incident where the 'g_user_role' variable was set by a login procedure but never reset on logout, leaking privileges across connections.
Rule: always clear package-level session variables at the start of each request if they represent user context.
🎯 Key Takeaway
Smallest scope wins – local over package body over package spec.
Package spec variables are global – treat them with care.
Session-persistent variables can cause privilege leaks if not reset properly.
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.
Unconstrained VARCHAR2 in a loop building dynamic SQL can silently grow past 32K, throwing ORA-06502. Always constrain or use CLOB for indeterminate lengths.
🎯 Key Takeaway
Declare every variable with explicit size and NOT NULL if it must carry a value. NULLs kill logic silently.
thecodeforge.io
Plsql Variables Datatypes
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.
SelectIntoGuard.sqlPLSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// io.thecodeforge
DECLARE
v_customer_name VARCHAR2(100);
v_customer_id NUMBER := 42;
BEGINBEGINSELECT customer_name
INTO v_customer_name
FROM customers
WHERE customer_id = v_customer_id;
EXCEPTIONWHEN NO_DATA_FOUND THEN
v_customer_name := 'UNKNOWN';
DBMS_OUTPUT.PUT_LINE('Customer ' || v_customer_id || ' not found');
WHEN TOO_MANY_ROWS THEN
-- Log the error, don't silently fail
DBMS_OUTPUT.PUT_LINE('Duplicate customer IDs: ' || v_customer_id);
RAISE_APPLICATION_ERROR(-20001, 'Data integrity failure');
END;
DBMS_OUTPUT.PUT_LINE('Name: ' || v_customer_name);
END;
/
Output
Name: UNKNOWN
⚠ Production Trap:
A 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.
🎯 Key Takeaway
Always handle NO_DATA_FOUND and TOO_MANY_ROWS in every SELECT INTO. Never trust production data to be exactly one row.
● Production incidentPOST-MORTEMseverity: high
The Silent VARCHAR2 Overflow That Took Down Month-End Processing
Symptom
Month-end batch processing started throwing ORA-06502 errors on a row that had always worked before. The error pointed to a variable in a stored procedure that had been unchanged for years.
Assumption
The developers assumed that since the variable declaration had not changed, the error must be due to bad input data from a new upstream system.
Root cause
A recent schema change altered the supplier_name column from VARCHAR2(30) to VARCHAR2(60). The PL/SQL block used a hard-coded VARCHAR2(30) variable instead of %TYPE. When a supplier name longer than 30 characters was inserted, the assignment overflowed the variable.
Fix
Changed the variable declaration to use %TYPE: v_supplier_name suppliers.supplier_name%TYPE. Also added an exception handler to log the actual value that caused the overflow.
Key lesson
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.
Production debug guideQuick symptom-to-action reference for common variable-related failures4 entries
Symptom · 01
ORA-06502: PL/SQL numeric or value error
→
Fix
Check the variable's declared size vs the value being assigned. Use %TYPE to match the source column. Enable detailed error logging with DBMS_UTILITY.FORMAT_ERROR_BACKTRACE.
Symptom · 02
Unexpected NULL in calculations or comparisons
→
Fix
Verify all variables are initialized explicitly. A variable declared without default is NULL. Use NVL or COALESCE to handle possible NULLs from database columns.
Symptom · 03
Memory usage grows unexpectedly in a session
→
Fix
Review package-level global variables. They persist for the session. Consider using local variables or session-specific collections. Monitor with V$MYSTAT for PGA memory.
Symptom · 04
Compilation error: PLS-00302: component must be declared
→
Fix
Check if the variable name conflicts with a column name in a SQL statement. Use a naming convention like v_ for variables to avoid ambiguity.
★ Quick Debug Cheat Sheet: PL/SQL Variable and Data Type ErrorsImmediate commands and fixes for the three most common variable-related production issues.
ORA-06502 on variable assignment−
Immediate action
Run the failing block with DBMS_OUTPUT turned on to see the exact value being assigned.
Commands
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';
Fix now
Replace hard-coded size with %TYPE: v_supplier_name suppliers.supplier_name%TYPE;
NULL result from arithmetic with uninitialized variable+
Immediate action
Check if any variable in the expression was declared without DEFAULT.
Commands
SELECT DUMP(v_counter) FROM DUAL; -- shows NULL if uninitialized
Check package body for package-level variable declarations.
Fix now
Add DEFAULT 0 or NVL(v_counter, 0) to the expression.
PLS-00302: variable name ambiguous with column+
Immediate action
Add procedure/function name prefix or rename variable.
Commands
SHOW ERRORS; -- displays exact line and column
SELECT text FROM user_source WHERE name = 'PROCEDURE_NAME' AND line = [error_line];
Fix now
Use a naming convention like v_variable_name to avoid clashes.
Feature
Variable
Constant
Anchored Type (%TYPE)
Value Modification
Can be updated anytime
Immutable once declared
Depends on usage
Initialization
Optional (Defaults to NULL)
Mandatory
Optional
Maintenance
Manual updates required
Single point of change
Automatic via Schema
Scope
Local or Global
Local or Global
Linked to DB Object
Best Use Case
Counters, flags, temp data
Configuration, Pi, Tax rates
Database data retrieval
⚙ Quick Reference
8 commands from this guide
File
Command / Code
Purpose
ForgeVariableBasics.sql
DECLARE
What Is PL/SQL Variables, Constants and Data Types and Why D
CommonMistakes.sql
DECLARE
Common Mistakes and How to Avoid Them
VariablesVsConstants.sql
DECLARE
Variables vs Constants – When to Use Each
AnchoredTypes.sql
DECLARE
Anchored Types
DataTypesMemory.sql
DECLARE
Data Type Categories and Their Memory Implications
ScopeLifetime.sql
CREATE OR REPLACE PACKAGE forge_session_ctx IS
Scope, Lifetime, and Variable Visibility
DeclareVariables.sql
DECLARE
Variable Declaration
SelectIntoGuard.sql
DECLARE
Assigning SQL Query Results to PL/SQL Variables
Key takeaways
1
Variables store temporary data; constants enforce immutability
use the right tool for the job.
2
Always use %TYPE for variables that hold column values
it prevents schema-migration-induced failures.
3
Uninitialized variables are NULL
explicitly initialize everything to avoid silent NULL propagation.
4
Package-level variables persist across calls
use them intentionally and clear session context between requests.
5
PL/SQL VARCHAR2 max (32767) != SQL VARCHAR2 max (4000)
mind the difference when mixing contexts.
Common mistakes to avoid
4 patterns
×
Hard-coding variable lengths instead of using %TYPE
Symptom
ORA-06502 occurs when a column expands beyond the hard-coded value, often during batch processing that passes long strings.
Fix
Replace hard-coded VARCHAR2(N) with column%TYPE. This automatically adapts to schema changes.
×
Not initializing variables (assuming they default to 0 or empty string)
Symptom
Calculations unexpectedly return NULL. For example, v_total := v_total + 100 returns NULL if v_total was never initialized.
Fix
Always initialize scalar variables: NUMBER := 0, VARCHAR2 := '', or use a sensible default. Document the default in comments.
×
Using package-level variables when local variables suffice
Symptom
Unexpected state changes across procedure calls within the same session. A variable set in one procedure affects another procedure that reads it.
Fix
Declare variables at the smallest possible scope. Use package body variables for shared session state, and pass values as parameters rather than relying on globals.
×
Confusing PL/SQL VARCHAR2 max (32767) with SQL VARCHAR2 max (4000)
Symptom
Silent truncation when passing a large PL/SQL string to a SQL statement or storing it in a table column.
Fix
Use CLOB for text larger than 4000 bytes that must interact with SQL. When possible, keep SQL constraints in mind by checking variable sizes.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01JUNIOR
What is the difference between %TYPE and %ROWTYPE?
Q02JUNIOR
Why should you use a CONSTANT instead of a regular variable for fixed va...
Q03JUNIOR
How does PL/SQL handle uninitialized variables by default?
Q04SENIOR
What are the benefits and trade-offs of using anchored data types (%TYPE...
Q05SENIOR
What is the maximum size of a VARCHAR2 variable in PL/SQL and how does i...
Q01 of 05JUNIOR
What is the difference between %TYPE and %ROWTYPE?
ANSWER
%TYPE anchors a single variable to the data type of a specific column in a table or cursor. %ROWTYPE declares a record variable that represents an entire row from a table, cursor, or view. %TYPE is for individual columns; %ROWTYPE is for complete rows. Use %ROWTYPE when you need to fetch all columns from a table, as it reduces maintenance when the table structure changes.
Q02 of 05JUNIOR
Why should you use a CONSTANT instead of a regular variable for fixed values?
ANSWER
Constants prevent accidental modification and make the code self-documenting. The compiler enforces immutability – any attempt to assign a new value to a constant raises a compilation error. This catches bugs at compile time rather than in production. Constants also signal intent to other developers: 'this value is a fixed reference, don't change it.'
Q03 of 05JUNIOR
How does PL/SQL handle uninitialized variables by default?
ANSWER
A variable declared without a DEFAULT clause is initialized to NULL. This includes all scalar types (NUMBER, VARCHAR2, DATE, etc.). NULL arithmetic always yields NULL – so an expression like v_total + v_increment where v_total is NULL returns NULL. The best practice is to explicitly initialize variables with a default value that makes sense for your logic.
Q04 of 05SENIOR
What are the benefits and trade-offs of using anchored data types (%TYPE, %ROWTYPE)?
ANSWER
Benefits: automatic schema synchronization – code adapts when columns change; reduced maintenance – no need to update variable declarations manually; improved readability – the variable's origin is clear. Trade-offs: minor compile-time overhead; cannot be used for columns from joins (use custom record types); %ROWTYPE includes all columns, which may be more than needed for some operations (but the memory cost is minimal). Overall, the benefits far outweigh the costs for production code.
Q05 of 05SENIOR
What is the maximum size of a VARCHAR2 variable in PL/SQL and how does it differ from SQL?
ANSWER
In PL/SQL, VARCHAR2 can hold up to 32,767 bytes. In SQL (table columns or SQL statements), VARCHAR2 is limited to 4,000 bytes. If you declare a VARCHAR2(32767) in PL/SQL and try to use it in a SQL query, you may get an error or silent truncation. For text larger than 4,000 bytes that needs to interact with SQL, use CLOB. This is a frequent gotcha for developers moving between PL/SQL and SQL contexts.
01
What is the difference between %TYPE and %ROWTYPE?
JUNIOR
02
Why should you use a CONSTANT instead of a regular variable for fixed values?
JUNIOR
03
How does PL/SQL handle uninitialized variables by default?
JUNIOR
04
What are the benefits and trade-offs of using anchored data types (%TYPE, %ROWTYPE)?
SENIOR
05
What is the maximum size of a VARCHAR2 variable in PL/SQL and how does it differ from SQL?
SENIOR
FAQ · 4 QUESTIONS
Frequently Asked Questions
01
Is it mandatory to initialize a constant in PL/SQL?
Yes. A constant must be initialized at declaration. You cannot assign a value later. Example: gc_rate CONSTANT NUMBER := 0.05; is required.
Was this helpful?
02
Can I change a variable declared with %TYPE after declaration?
Yes, %TYPE affects only the data type, not the mutability. You can assign new values to the variable as long as they are compatible with the anchored type.
Was this helpful?
03
What happens if I try to assign a value larger than the variable's size?
You get ORA-06502 (PL/SQL numeric or value error). The runtime raises this exception. It can be caught with an exception handler, but it's better to avoid it by using %TYPE or ensuring sizes are adequate.
Was this helpful?
04
How do I declare a variable that holds a row from a join?
You cannot use %ROWTYPE for joins directly. Instead, create a custom record type: TYPE join_rec IS RECORD (col1 table1.col1%TYPE, col2 table2.col2%TYPE); v_rec join_rec; Then SELECT into v_rec.