您正在了解 字符长度语义和字节长度语义之间的区别:
您必须为 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多字节不间断空格字符。 (您经常在从Word文档等地方复制的文本中看到它,以及与花式引号和其他非ASCII范围字符一起)。
您可以将插入更改为过滤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.
如果可能并对您的数据有意义,请替换任何意外的多字节字符为单字节等效字符;在这种情况下,普通空格 可能 有效,但是使用任何替换都会破坏可能实际上很重要的信息。