您的位置:首页 > 数据库 > Oracle

oracle中char和varchar2数据类型的区别

2012-03-19 19:40 363 查看
一道面试题:

Oracle中char和varchar2数据类型有什么区别?有数据”test”分别存放到char(10)和varchar2(10)

类型的字段中,其存储长度及类型有何区别?

首先解释一下:

CHAR的长度是固定的,而VARCHAR2的长度是可以变化的。比如存储的字符串是:“liming”那么在char中占10个字符,包括“liming” + 3个空格。在varchar2中长度为7。

char的效率比varchar2的效率稍高。

char和varchar是一对矛盾体。但是存在即是合理。

VARCHAR2虽然比CHAR节省空间,但是假如一个VARCHAR2列经常被修改,而且每次被修改的数据的长度不同,这会引起‘行迁移’(Row Migration)现象,而这造成多余的I/O,是数据库设计和调整中要尽力避免的,在这种情况下用CHAR代替VARCHAR2会更好一些。

一下内容来自:/article/8738540.html

char

char类型保存定长字符串,最小长度为1字节(或字符数),最长2000字节(注意这里是字节,而不是字符数),如果不指定,则默认为1。

SQL> create table t(name char);

表已创建。

SQL> desc t;

名称 是否为空? 类型

----------------------------------------- -------- ----------------------------

NAME CHAR(1)

SQL> create table t(name char(2001));

create table t(name char(2001))

*

第 1 行出现错误:

ORA-00910: 指定的长度对于数据类型而言过长

如果插入的字符串长度不足,会以空格填充。如果超过长度,则会报错。

SQL> insert into t values('aa');

insert into t values('aa')

*

第 1 行出现错误:

ORA-12899: 列 "SYS"."T"."NAME" 的值太大 (实际值: 2, 最大值: 1)

下面是例子展示了关于char类型的插入后续空格值的问题:

SQL> create table t(name char(20));

表已创建。

SQL> insert into t values('a');

已创建 1 行。

SQL> insert into t values('a ');

已创建 1 行。

SQL> select '"'||name||'"' from t where name='a';

'"'||NAME||'"'

----------------------

"a "

"a "

SQL> select '"'||name||'"' from t where name='a ';

'"'||NAME||'"'

----------------------

"a "

"a "

SQL> insert into t values(' a');

已创建 1 行。

SQL> select '"'||name||'"' from t where name='a';

'"'||NAME||'"'

----------------------

"a "

"a "

SQL> select '"'||name||'"' from t where name=' a';

'"'||NAME||'"'

----------------------

" a "

可以看到,如果插入的字符串最后带有空格,对于定长的字符串的比较,oracle会将较短的字符串右边填上空格,直到对比的两边的字符串长度一致。

varchar2和varchar

varchar2和varchar是同义词,都是指变长字符串类型。但是由于某种原因,varchar可能在后续版本中改变varchar的意思还是怎么的,oracle建议使用varchar2。

在使用varchar2类型时必须指定其长度(最小1字节或字符数,最大4000字节),不能像char一样省略,虽然concepts文档上说其默认为1。这里不知道oracle为什么会这样实现,我认为这应该算是一个缺陷。

SQL> drop table t;

表已删除。

SQL> create table t(name varchar);

create table t(name varchar)

*

第 1 行出现错误:

ORA-00906: 缺失左括号

SQL> create table t(name varchar2);

create table t(name varchar2)

*

第 1 行出现错误:

ORA-00906: 缺失左括号

由于varchar2不会在短于指定长度的字符串后面补齐空格,所以char类型中的后续空格问题不会在varchar2类型中发生。

SQL> create table t(name varchar2(20));

表已创建。

SQL> insert into t values('a');

已创建 1 行。

SQL> insert into t values('a ');

已创建 1 行。

SQL> select '"'||name||'"' from t where name='a';

'"'||NAME||'"'

----------------------

"a"

SQL> select '"'||name||'"' from t where name='a ';

'"'||NAME||'"'

----------------------

"a "

关于字符型数据的长度问题

前面在说到char/varchar2类型时,指定其长度可能是1字节或者1个字符数。在不同的编码方式中,一个字符可能占1字节,2字节,3字节甚至4字节。

SQL> select name,value$ from props$ where name='NLS_CHARACTERSET';

NAME VALUE$

------------------------------ ------------------------------

NLS_CHARACTERSET ZHS16GBK

SQL> create table t(name char(1 byte));

表已创建。

SQL> insert into t values('测');

insert into t values('测')

*

第 1 行出现错误:

ORA-12899: 列 "SYS"."T"."NAME" 的值太大 (实际值: 2, 最大值: 1)

SQL> create table t(name char(1 char));

表已创建。

SQL> insert into t values('测');

已创建 1 行。

SQL> create table t(name char(1));

表已创建。

SQL> insert into t values('测');

insert into t values('测')

*

第 1 行出现错误:

ORA-12899: 列 "SYS"."T"."NAME" 的值太大 (实际值: 2, 最大值: 1)

可以看到,由于一个汉字占两个byte,如果char/varchar2的长度是按byte计算的,那么就无法插入,如果是按字符数计算的,则可以插入。而在默认情况下,oracle是按byte计算长度的,这是由于参数nls_length_semantics的默认值是byte。

SQL> show parameter nls_length

NAME TYPE VALUE

------------------------------------ ----------- ------------------------------

nls_length_semantics string BYTE

但是,char类型的最大上限是2000字节,而不是2000字符数。

SQL> drop table t;

表已删除。

SQL> create table t(name char(2000 char));

表已创建。

SQL> insert into t values(lpad('测',2000,'测'));

已创建 1 行。

SQL> select length(name) from t;

LENGTH(NAME)

------------

1000

SQL> select lengthb(name) from t;

LENGTHB(NAME)

-------------

2000

SQL> insert into t values(lpad('测',2002,'测'));

insert into t values(lpad('测',2002,'测'))

*

第 1 行出现错误:

ORA-12899: 列 "SYS"."T"."NAME" 的值太大 (实际值: 2002, 最大值: 2000)

从上面的例子可以看出,最多只能插入1000个字符。顺便提一下,在lpad/rpad函数中,第二个参数也是指的字节数,而不是字符数。并且,lpad/rpad的输出也是varchar2类型的,所以第二个参数不能超过4000,如果超过,会被截断成4000。

在数据字典中,oracle还是会将按字符数计算的长度,按照系统当前的字符集选择,转换成按字节计算的长度。由于我目前的测试系统中使用的ZHS16GBK字符集是定长字符集,每个字符占两个字节,所以这里data length为2。在一些变长字符集中,比如UTF8,则是4(UTF8中一个字符最长占4个字节)

SQL> drop table t;

表已删除。

SQL> create table t(name char(1 char));

表已创建。

SQL> select TABLE_NAME,COLUMN_NAME,DATA_TYPE,DATA_LENGTH from dba_tab_columns where

table_name='T' a

nd owner=user;

TABLE_NAME COLUMN_NAM DATA_TYPE DATA_LENGTH

---------- ---------- ---------- -----------

T NAME CHAR 2

但是,oracle在判断插入的时候不是以dba_tab_columns中记录的data_length来做为限制条件的。看看UTF8下的试验结果:

SQL> select name,value$ from props$ where name='NLS_CHARACTERSET';

NAME VALUE$

------------------------------ ------------------------------

NLS_CHARACTERSET AL32UTF8

SQL> drop table t;

表已删除。

SQL> create table t(name char(1 char));

表已创建。

SQL> select TABLE_NAME,COLUMN_NAME,DATA_TYPE,DATA_LENGTH from dba_tab_columns where

2 table_name='T' and owner=user;

TABLE_NAME COLUMN_NAM DATA_TYPE DATA_LENGTH

---------- ---------- ---------- -----------

T NAME CHAR 4

SQL> insert into t values('aa');

insert into t values('aa')

*

第 1 行出现错误:

ORA-12899: 列 "SYS"."T"."NAME" 的值太大 (实际值: 2, 最大值: 1)

SQL> insert into t values('a');

已创建 1 行。

SQL> insert into t values('测');

已创建 1 行。

SQL> select dump(name) from t;

DUMP(NAME)

--------------------------------------------------------------------------------

Typ=96 Len=1: 97

Typ=96 Len=3: 230,181,139

可以看到,一个英文字符占一个字节,那么'aa'只占两个字节,却无法插入。'测'占3个字节,却能插入。所以在判断插入的数据是否符合长度要求时,oracle会根据定义的数据类型是按字节还是按字符数,然后参考系统当前的字符集设置来做出正确的判断。

实际上,oracle提供的求字符串长度的函数length,返回的也是字符个数,而非字节数:

SQL> select length('a') from dual;

LENGTH('A')

-----------

1

SQL> select length('测') from dual;

LENGTH('测')

------------

1

而lengthb返回的才是字节数:

SQL> select lengthb('a') from dual;

LENGTHB('A')

------------

1

SQL> select lengthb('测') from dual;

LENGTHB('测')

-------------

3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: