Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ORA-12899 value too large for column despite of same length

I am running the following query. But getting ORA-12899. Altough the length of string am trying to insert is 30.

INSERT INTO TABLE1 SELECT * FROM temp_Table1 where LENGTH(column1)=30;

SQL Error: ORA-12899:value too large for column "TABLE1"."column1" (actual: 31, maximum: 30)


select column1 from temp_Table1 where LENGTH(column1)=30;

Testing  - HLC/TC Design Corre

Desc temp_Table1

column1 VARCHAR2(30)

Desc Table1

column1 VARCHAR2(30)
like image 943
Shitu Avatar asked Sep 09 '16 06:09

Shitu


1 Answers

You're seeing the difference between character and byte length semantics:

You must specify a maximum length for a VARCHAR2 column. This maximum must be at least 1 byte, although the actual string stored is permitted to be a zero-length string (''). You can use the CHAR qualifier, for example VARCHAR2(10 CHAR), to give the maximum length in characters instead of bytes. A character is technically a code point of the database character set. You can use the BYTE qualifier, for example VARCHAR2(10 BYTE), to explicitly give the maximum length in bytes. If no explicit qualifier is included in a column or attribute definition when a database object with this column or attribute is created, then the length semantics are determined by the value of the NLS_LENGTH_SEMANTICS parameter of the session creating the object.

If your session is using byte semantics then the column in your table will default to that:

select value from nls_session_parameters where parameter = 'NLS_LENGTH_SEMANTICS';

VALUE                                  
----------------------------------------
BYTE                                    

create table t42(text varchar2(5));

Table T42 created.

select char_used from user_tab_columns where table_name = 'T42' and column_name = 'TEXT';

C
-
B

That is the same as explicitly doing:

create table t42(text varchar2(5 byte));

If your source data is five characters but includes any multibyte characters then the number of bytes will exceed five:

insert into t42 (text) values ('Hello');

1 row inserted.

insert into t42 (text) values ('Señor');

SQL Error: ORA-12899: value too large for column "SCHEMA"."T42"."TEXT" (actual: 6, maximum: 5)

Which is what you're seeing. When you insert the values from your other table you are filter on the length of the values, but length() counts characters rather than bytes. There is a lengthb() function that does count bytes. If you check the byte length of the 30-character value you're selecting you'll see it is in fact 31 bytes, so one of those characters is multibyte.

with t42 (text) as (
  select 'Hello' from dual
  union all select 'Señor' from dual
  union all select 'Testing  - HLC/TC Design Corre' from dual
)
select text, length(text) as chars, lengthb(text) as bytes, dump(text, 16) as hex
from t42;

TEXT                            CHARS BYTES HEX                                                                                                      
------------------------------- ----- ----- ----------------------------------------------------------------------------------------------------------
Hello                               5     5 Typ=1 Len=5: 48,65,6c,6c,6f                                                                               
Señor                               5     6 Typ=1 Len=6: 53,65,c3,b1,6f,72                                                                            
Testing  - HLC/TC Design Corre     30    31 Typ=1 Len=31: 54,65,73,74,69,6e,67,c2,a0,20,2d,20,48,4c,43,2f,54,43,20,44,65,73,69,67,6e,20,43,6f,72,72,65

From the dump() values you can see that after Testing (54,65,73,74,69,6e,67) and before the space and dash (20,2d) you have c2,a0, which is the UTF-8 multibyte non-breaking space character. (You often see that, along with curly quotes and other non-ASCII-range characters, in text that has been copied from,. say, a Word document).

You can either change your insert to filter on LENGTHB(column1)=30 (which will exclude the row you currently find), or change your column definition to 30 characters instead of 30 bytes:

drop table t42;

Table T42 dropped.

create table t42(text varchar2(5 char));

Table T42 created.

select char_used from user_tab_columns where table_name = 'T42' and column_name = 'TEXT';

C
-
C

insert into t42 (text) values ('Hello');

1 row inserted.

insert into t42 (text) values ('Señor');

1 row inserted.

Or replace any unexpected multibyte characters with single-byte equivalents, if that's possible and makes sense for your data; in this case a normal space might work, but with any substitution you are destroying information that might actually be important.

like image 89
Alex Poole Avatar answered Oct 21 '22 20:10

Alex Poole