ORA-12899 Value Too Large for Column - Data Size and Length Errors
ORA-12899: Value Too Large for Column
Section titled “ORA-12899: Value Too Large for Column”Error Overview
Section titled “Error Overview”Error Text: ORA-12899: value too large for column [schema].[table].[column] (actual: [actual_length], maximum: [max_length])
This error occurs when attempting to insert or update data that exceeds the defined column size. The actual data length is larger than the maximum allowed length for the column, which can happen with VARCHAR2, CHAR, or other size-limited data types.
Understanding Column Size Limits
Section titled “Understanding Column Size Limits”Oracle Data Type Limits
Section titled “Oracle Data Type Limits”-- Common data types and their limitsVARCHAR2(n) -- n bytes (1-4000 in table, 1-32767 in PL/SQL)CHAR(n) -- n bytes (1-2000)NVARCHAR2(n) -- n characters (1-2000)NCHAR(n) -- n characters (1-1000)RAW(n) -- n bytes (1-2000)
Character Set Considerations
Section titled “Character Set Considerations”-- Single-byte character sets (like US7ASCII)'ABC' = 3 bytes
-- Multi-byte character sets (like UTF8)'ABC' = 3 bytes (ASCII characters)'ABÇ' = 4 bytes (Ç takes 2 bytes in UTF8)'中文' = 6 bytes (each Chinese character takes 3 bytes)
Common Causes
Section titled “Common Causes”Data Length Exceeds Column Definition
Section titled “Data Length Exceeds Column Definition”-- Column defined as VARCHAR2(10)INSERT INTO users (username) VALUES ('this_is_too_long'); -- 16 characters, ERROR
-- Column defined as VARCHAR2(50) but using multi-byte charactersINSERT INTO users (description) VALUES ('这是一个很长的中文描述文本'); -- May exceed 50 bytes
Application Data Changes
Section titled “Application Data Changes”- Input validation removed or modified
- Data migration from systems with larger fields
- Concatenation operations producing longer strings
- User input exceeding expected limits
Character Set Issues
Section titled “Character Set Issues”-- Database character set affects byte calculationsSELECT LENGTHB('Hello World') as bytes, -- Byte length LENGTH('Hello World') as characters -- Character lengthFROM dual;
Diagnostic Queries
Section titled “Diagnostic Queries”Check Column Definition
Section titled “Check Column Definition”-- Get column informationSELECT column_name, data_type, data_length, char_length, char_usedFROM user_tab_columnsWHERE table_name = 'YOUR_TABLE'AND column_name = 'YOUR_COLUMN';
-- For all tablesSELECT table_name, column_name, data_type, data_length, data_precision, data_scaleFROM user_tab_columnsWHERE table_name = 'YOUR_TABLE'ORDER BY column_id;
Analyze Data Lengths
Section titled “Analyze Data Lengths”-- Check actual data lengths in tableSELECT column_name, LENGTH(column_name) as char_length, LENGTHB(column_name) as byte_length, column_name as sample_dataFROM your_tableWHERE LENGTH(column_name) > expected_lengthORDER BY byte_length DESC;
-- Check maximum data lengthsSELECT MAX(LENGTH(column_name)) as max_char_length, MAX(LENGTHB(column_name)) as max_byte_length, AVG(LENGTH(column_name)) as avg_char_lengthFROM your_table;
Find Problematic Records
Section titled “Find Problematic Records”-- Identify records that would cause ORA-12899SELECT *FROM staging_tableWHERE LENGTHB(description) > 100 -- Assuming target column is VARCHAR2(100)ORDER BY LENGTHB(description) DESC;
-- Count problematic recordsSELECT CASE WHEN LENGTHB(column_name) <= 50 THEN '0-50 bytes' WHEN LENGTHB(column_name) <= 100 THEN '51-100 bytes' WHEN LENGTHB(column_name) <= 200 THEN '101-200 bytes' ELSE 'Over 200 bytes' END as size_range, COUNT(*) as record_countFROM your_tableGROUP BY CASE WHEN LENGTHB(column_name) <= 50 THEN '0-50 bytes' WHEN LENGTHB(column_name) <= 100 THEN '51-100 bytes' WHEN LENGTHB(column_name) <= 200 THEN '101-200 bytes' ELSE 'Over 200 bytes' ENDORDER BY record_count DESC;
Resolution Strategies
Section titled “Resolution Strategies”1. Increase Column Size
Section titled “1. Increase Column Size”Expand Column Definition
Section titled “Expand Column Definition”-- Increase VARCHAR2 column sizeALTER TABLE your_table MODIFY column_name VARCHAR2(200);
-- Change from BYTE to CHAR semantics (for multi-byte characters)ALTER TABLE your_table MODIFY column_name VARCHAR2(100 CHAR);
-- Verify the changeSELECT column_name, data_length, char_length, char_usedFROM user_tab_columnsWHERE table_name = 'YOUR_TABLE'AND column_name = 'COLUMN_NAME';
Considerations for Column Expansion
Section titled “Considerations for Column Expansion”-- Check table size and impactSELECT table_name, num_rows, blocks, avg_row_lenFROM user_tablesWHERE table_name = 'YOUR_TABLE';
-- Check indexes that might be affectedSELECT index_name, column_name, column_positionFROM user_ind_columnsWHERE table_name = 'YOUR_TABLE'AND column_name = 'YOUR_COLUMN';
2. Data Truncation
Section titled “2. Data Truncation”Safe Truncation with Validation
Section titled “Safe Truncation with Validation”-- Create function for safe truncationCREATE OR REPLACE FUNCTION safe_truncate( p_input VARCHAR2, p_max_bytes NUMBER, p_suffix VARCHAR2 DEFAULT '...') RETURN VARCHAR2 AS l_result VARCHAR2(32767); l_suffix_len NUMBER := LENGTHB(p_suffix);BEGIN IF LENGTHB(p_input) <= p_max_bytes THEN RETURN p_input; ELSE -- Truncate to fit with suffix l_result := SUBSTRB(p_input, 1, p_max_bytes - l_suffix_len) || p_suffix; RETURN l_result; END IF;END;/
-- Use in INSERT/UPDATEINSERT INTO your_table (description)VALUES (safe_truncate(long_description, 100));
Batch Data Truncation
Section titled “Batch Data Truncation”-- Update existing data with truncationUPDATE your_tableSET description = SUBSTRB(description, 1, 100)WHERE LENGTHB(description) > 100;
-- Verify truncationSELECT COUNT(*) as total_records, COUNT(CASE WHEN LENGTHB(description) > 100 THEN 1 END) as oversized_recordsFROM your_table;
3. Data Validation and Cleansing
Section titled “3. Data Validation and Cleansing”Pre-Insert Validation
Section titled “Pre-Insert Validation”-- Create validation procedureCREATE OR REPLACE PROCEDURE validate_data_length( p_table_name VARCHAR2, p_column_name VARCHAR2, p_data VARCHAR2) AS l_max_length NUMBER; l_actual_length NUMBER := LENGTHB(p_data);BEGIN -- Get column max length SELECT data_length INTO l_max_length FROM user_tab_columns WHERE table_name = UPPER(p_table_name) AND column_name = UPPER(p_column_name);
IF l_actual_length > l_max_length THEN RAISE_APPLICATION_ERROR(-20001, 'Data too long: ' || l_actual_length || ' bytes, maximum: ' || l_max_length || ' bytes'); END IF;END;/
Data Cleaning Procedures
Section titled “Data Cleaning Procedures”-- Clean common data issuesCREATE OR REPLACE FUNCTION clean_data(p_input VARCHAR2)RETURN VARCHAR2 AS l_result VARCHAR2(32767) := p_input;BEGIN -- Remove leading/trailing spaces l_result := TRIM(l_result);
-- Replace multiple spaces with single space l_result := REGEXP_REPLACE(l_result, '[ ]{2,}', ' ');
-- Remove control characters l_result := REGEXP_REPLACE(l_result, '[[:cntrl:]]', '');
RETURN l_result;END;/
Advanced Solutions
Section titled “Advanced Solutions”Dynamic Column Sizing
Section titled “Dynamic Column Sizing”-- Analyze data and suggest optimal column sizesWITH data_analysis AS ( SELECT 'YOUR_COLUMN' as column_name, MAX(LENGTHB(your_column)) as max_bytes, AVG(LENGTHB(your_column)) as avg_bytes, PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY LENGTHB(your_column)) as p95_bytes FROM your_table)SELECT column_name, max_bytes, ROUND(avg_bytes, 2) as avg_bytes, ROUND(p95_bytes, 2) as p95_bytes, -- Suggested size with 20% buffer CEIL(max_bytes * 1.2) as suggested_sizeFROM data_analysis;
Migration Strategy for Large Tables
Section titled “Migration Strategy for Large Tables”-- Step 1: Create staging table with larger columnCREATE TABLE your_table_new ASSELECT * FROM your_table WHERE 1=0;
ALTER TABLE your_table_new MODIFY column_name VARCHAR2(500);
-- Step 2: Migrate data in batchesDECLARE CURSOR c_data IS SELECT rowid, your_column FROM your_table; TYPE t_batch IS TABLE OF c_data%ROWTYPE; l_batch t_batch; l_batch_size NUMBER := 10000;BEGIN OPEN c_data; LOOP FETCH c_data BULK COLLECT INTO l_batch LIMIT l_batch_size; EXIT WHEN l_batch.COUNT = 0;
FORALL i IN 1..l_batch.COUNT INSERT INTO your_table_new SELECT * FROM your_table WHERE rowid = l_batch(i).rowid;
COMMIT; END LOOP; CLOSE c_data;END;/
-- Step 3: Rename tablesDROP TABLE your_table;RENAME your_table_new TO your_table;
Character Set Conversion
Section titled “Character Set Conversion”-- Check character set informationSELECT parameter, valueFROM nls_database_parametersWHERE parameter IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
-- Convert data for different character setsSELECT original_text, CONVERT(original_text, 'UTF8', 'US7ASCII') as converted_text, LENGTHB(original_text) as original_bytes, LENGTHB(CONVERT(original_text, 'UTF8', 'US7ASCII')) as converted_bytesFROM your_table;
Prevention Strategies
Section titled “Prevention Strategies”Application-Level Validation
Section titled “Application-Level Validation”// Java example for input validationpublic void validateInputLength(String input, String fieldName, int maxBytes) { if (input != null && input.getBytes("UTF-8").length > maxBytes) { throw new ValidationException( fieldName + " exceeds maximum length of " + maxBytes + " bytes" ); }}
Database Constraints
Section titled “Database Constraints”-- Add check constraint for length validationALTER TABLE your_tableADD CONSTRAINT chk_description_lengthCHECK (LENGTHB(description) <= 500);
-- Add constraint with custom error messageALTER TABLE your_tableADD CONSTRAINT chk_name_lengthCHECK (LENGTH(name) <= 100);
Trigger-Based Validation
Section titled “Trigger-Based Validation”-- Create validation triggerCREATE OR REPLACE TRIGGER trg_validate_length BEFORE INSERT OR UPDATE ON your_table FOR EACH ROWBEGIN IF LENGTHB(:NEW.description) > 500 THEN RAISE_APPLICATION_ERROR(-20002, 'Description too long: ' || LENGTHB(:NEW.description) || ' bytes (max: 500 bytes)'); END IF;END;/
Monitoring and Reporting
Section titled “Monitoring and Reporting”Length Monitoring Query
Section titled “Length Monitoring Query”-- Regular monitoring of data lengthsSELECT table_name, column_name, data_length as defined_length, current_max_length, utilization_pct, CASE WHEN utilization_pct > 90 THEN 'HIGH RISK' WHEN utilization_pct > 75 THEN 'MEDIUM RISK' ELSE 'LOW RISK' END as risk_levelFROM ( SELECT 'YOUR_TABLE' as table_name, 'YOUR_COLUMN' as column_name, (SELECT data_length FROM user_tab_columns WHERE table_name = 'YOUR_TABLE' AND column_name = 'YOUR_COLUMN') as data_length, MAX(LENGTHB(your_column)) as current_max_length, ROUND((MAX(LENGTHB(your_column)) / (SELECT data_length FROM user_tab_columns WHERE table_name = 'YOUR_TABLE' AND column_name = 'YOUR_COLUMN')) * 100, 2) as utilization_pct FROM your_table);
Automated Alert System
Section titled “Automated Alert System”-- Create monitoring procedureCREATE OR REPLACE PROCEDURE check_column_utilization ASBEGIN FOR rec IN ( SELECT table_name, column_name, data_length FROM user_tab_columns WHERE data_type LIKE '%VARCHAR%' AND data_length > 0 ) LOOP DECLARE l_max_length NUMBER; l_utilization NUMBER; l_sql VARCHAR2(4000); BEGIN l_sql := 'SELECT NVL(MAX(LENGTHB(' || rec.column_name || ')), 0) FROM ' || rec.table_name; EXECUTE IMMEDIATE l_sql INTO l_max_length;
l_utilization := (l_max_length / rec.data_length) * 100;
IF l_utilization > 90 THEN DBMS_OUTPUT.PUT_LINE( 'WARNING: ' || rec.table_name || '.' || rec.column_name || ' utilization: ' || ROUND(l_utilization, 2) || '%' ); END IF; END; END LOOP;END;/
Emergency Procedures
Section titled “Emergency Procedures”Quick Fix for Production Issues
Section titled “Quick Fix for Production Issues”-- Emergency truncation procedureCREATE OR REPLACE PROCEDURE emergency_truncate( p_table_name VARCHAR2, p_column_name VARCHAR2) AS l_max_length NUMBER; l_sql VARCHAR2(4000);BEGIN -- Get column max length SELECT data_length INTO l_max_length FROM user_tab_columns WHERE table_name = UPPER(p_table_name) AND column_name = UPPER(p_column_name);
-- Truncate data that exceeds limit l_sql := 'UPDATE ' || p_table_name || ' SET ' || p_column_name || ' = SUBSTRB(' || p_column_name || ', 1, ' || l_max_length || ')' || ' WHERE LENGTHB(' || p_column_name || ') > ' || l_max_length;
EXECUTE IMMEDIATE l_sql;
DBMS_OUTPUT.PUT_LINE('Truncated ' || SQL%ROWCOUNT || ' records'); COMMIT;END;/
Rollback Strategy
Section titled “Rollback Strategy”-- Create backup before modificationsCREATE TABLE your_table_backup ASSELECT * FROM your_tableWHERE LENGTHB(problem_column) > current_limit;
-- After fixing, verify and drop backupSELECT COUNT(*) FROM your_table_backup;-- DROP TABLE your_table_backup;
Related Errors
Section titled “Related Errors”- ORA-01401: Inserted value too large for column
- ORA-06502: PL/SQL numeric or value error
- ORA-22835: Buffer too small for CLOB to CHAR conversion
Best Practices
Section titled “Best Practices”- Define appropriate column sizes during design
- Implement application-level validation
- Monitor column utilization regularly
- Use CHAR semantics for multi-byte character sets
- Test with realistic data volumes
- Document data length requirements
Troubleshooting Checklist
Section titled “Troubleshooting Checklist”- Identify the specific column and table
- Check current column definition
- Analyze actual data lengths
- Determine if column expansion is possible
- Consider data truncation options
- Implement validation procedures
- Test solution with sample data
- Monitor post-implementation