Вы видите разницу между семантикой длины символов и байтов< /а>:
Вы должны указать максимальную длину столбца VARCHAR2. Этот максимум должен быть не менее 1 байта, хотя фактическая сохраняемая строка может быть строкой нулевой длины (''). Вы можете использовать квалификатор CHAR, например, VARCHAR2(10 CHAR), чтобы указать максимальную длину в символах, а не в байтах. Символ технически является кодовой точкой набора символов базы данных. Вы можете использовать квалификатор BYTE, например, VARCHAR2(10 BYTE), чтобы явно указать максимальную длину в байтах. Если явный квалификатор не включен в определение столбца или атрибута при создании объекта базы данных с этим столбцом или атрибутом, то семантика длины определяется значением параметра NLS_LENGTH_SEMANTICS сеанса, создающего объект.
Если ваш сеанс использует семантику байтов, тогда столбец в вашей таблице по умолчанию будет таким:
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
Это то же самое, что и явное выполнение:
create table t42(text varchar2(5 byte));
Если ваши исходные данные состоят из пяти символов, но содержат многобайтовые символы, количество байтов превысит пять:
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)
Что вы и видите. Когда вы вставляете значения из другой таблицы, вы фильтруете длину значений, но length()
считает символы, а не байты. Существует функция lengthb()
, которая считает байты. Если вы проверите длину в байтах 30-символьного значения, которое вы выбираете, вы увидите, что на самом деле оно составляет 31 байт, поэтому один из этих символов является многобайтовым.
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
Из значений dump()
видно, что после Testing
(54,65,73,74,69,6e,67
) и перед пробелом и тире (20,2d
) у вас есть c2,a0
, что равно многобайтовый неразрывный пробел UTF-8. (Вы часто видите это, наряду с фигурными кавычками и другими символами, не входящими в диапазон ASCII, в тексте, который был скопирован, скажем, из документа Word).
Вы можете изменить свою вставку на фильтрацию по LENGTHB(column1)=30
(что исключит строку, которую вы сейчас находите), или изменить определение столбца на 30 символов вместо 30 байтов:
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.
Или замените любые неожиданные многобайтовые символы однобайтовыми эквивалентами, если это возможно и имеет смысл для ваших данных; в этом случае обычный пробел может работать, но с любой заменой вы уничтожаете информацию, которая может быть действительно важной.
person
Alex Poole
schedule
09.09.2016