设为首页 收藏本站
查看: 687|回复: 0

[经验分享] Oracle varchar2或char类型的byte和char的区别

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2017-1-23 08:47:17 | 显示全部楼层 |阅读模式
Oracle定义字符串类型VARCHAR2和CHAR指定长度的用法如下:

varchar2(<SIZE> <BYTE|CHAR>) <SIZE>是介于1~4000之间的一个数,表示最多占用4000字节的存储空间。
char(<SIZE> <BYTE|CHAR>) <SIZE>是介于1~2000之间的一个数,表示最多占用2000字节的存储空间。
那其中的BYTE和CHAR有什么区别呢
  • BYTE,用字节指定:VARCHAR2(10 BYTE)。这能支持最多10字节的数据,在一个多字节字符集中,这可能只是两个字符。采用多字节字符集时,字节与字符并不相同。
  • CHAR,用字符指定:VARCHAR2(10 CHAR)。这将支持最多10字符数据,可能是多达40字节的信息。另外,VARCHAR2(4000 CHAR)理论上支持最多4000个字符的数据,不过由于Oracle中字符串数据类型限制为4000字节,所以可能无法得到全部4000个字符。

使用UTF8之类的多字节字符集时,建议你在VARCHAR2/CHAR定义中使用CHAR修饰会,也就是说,使用VARCHAR2(30 CHAR),而不是VARCHAR2(30),因为你的本意很可能是定义一个实际上能存储30字符数据的列。还可以使用会话参数或系统参数NLS_LENGTH_SEMANTICS来修改默认行为,即把默认设置BYTE改为CHAR。不建议在系统级修改这个设置,而应该使用ALTER SESSION修改会话级。还有重要的一点,VARCHAR2中存储的字节数上界是4000。不过,即使你指定了VARCHAR(4000 CHAR),可能并不能在这个字段中放下4000个字符实际上,采用你选择的字符集时,如果所有字符都要用4个字节来表示,那么这个字段中就只能放下1000个字符!
下面使用一个小例子展示BYTE和CHAR之间的区别,并显示出上界的作用。
测试环境11.2.0.4,是在多字节字符集数据库上完成的,在此使用了字符集AL32UTF8,这个字符集支持最新版本的Unicode标准,采用一种变长方式对每个字符使用1~4个字节进行编码
1
2
3
4
5
6
7
8
9
10
11
12
zx@ORCL>col value for a30
zx@ORCL>col parameter for a30
zx@ORCL>select * from nls_database_parameters where parameter='NLS_CHARACTERSET';

PARAMETER              VALUE
------------------------------ ------------------------------
NLS_CHARACTERSET           AL32UTF8
zx@ORCL>show parameter nls_leng

NAME                     TYPE                 VALUE
------------------------------------ --------------------------------- ------------------------------
nls_length_semantics             string                   BYTE



创建测试表

1
2
3
zx@ORCL>create table t (a varchar2(1),b varchar2(1 char),c varchar2(4000 char));

Table created.



现在,这个表中插入一个UTF字符unistr('\00d6'),这个字符长度为2个字节,可以观察到以下结果:
1
2
3
4
5
6
7
8
9
10
11
zx@ORCL>select length(unistr('\00d6')),lengthb(unistr('\00d6')) from dual;

LENGTH(UNISTR('\00D6')) LENGTHB(UNISTR('\00D6'))
----------------------- ------------------------
              1             2

zx@ORCL>insert into t (a) values (unistr('\00d6'));
insert into t (a) values (unistr('\00d6'))
                          *
ERROR at line 1:
ORA-12899: value too large for column "ZX"."T"."A" (actual: 2, maximum: 1)



这说明:VARCHAR(1)的单位是字节而不是字符。这里确实只有一个Unicode字符,但是它在一个字节中放不下;将应用从单字节定宽字符集移植到一个多字节字符集时,可能会发现原来在字段中能放下的文本现在却无法放下。第二点的原因是:在一个单字节字符集中,包含20个字符的字符串长度就是20字节,完全可以在VARCHAR2(20)中放下。不过在一个多字节字符集中,20个字符的长度可以达到80字节(如果每个字符用4个字节表示),这样一杰,20个Unicode字符很可能无法在20个字节中放下。你可能会考虑将DDL修改为VARCHAR2(20 CHAR),或在运行DDL创建表时使用前面提到的NLS_LENGTH_SEMENTICS会话参数。
插入包含一个字符的字段时观察到以下结果:
1
2
3
4
5
6
7
8
9
10
zx@ORCL>insert into t (b) values (unistr('\00d6'));

1 row created.

zx@ORCL>col dump for a30
zx@ORCL>select length(b),lengthb(b),dump(b) dump from t;

LENGTH(B) LENGTHB(B) DUMP
---------- ---------- ------------------------------
     1     2 Typ=1 Len=2: 195,150



这个INSERT成功了,而且可以看到,所有插入数据的长度(LENGTH)就是一个字符,所有字符串函数都以字符为单位工作。LENGTHB函数(字节长度)显示出这个字段占用了2字节的存储空间,另外DUMP函数显示了这些字节到底是什么。这个例子展示了VARCHAR2(N)并不一定存储N个字符,而只是存储N个字节。
下面测试VARCHAR2(4000)可能存储不了4000个字符
1
2
3
4
5
6
7
8
9
10
11
12
13
zx@ORCL>declare
  2  l_date varchar2(4000 char);
  3  l_ch   varchar2(1 char) := unistr('\00d6');
  4  begin
  5  l_date:=rpad(l_ch,4000,l_ch);
  6  insert into t(c) values(l_date);
  7  end;
  8  /
declare
*
ERROR at line 1:
ORA-01461: can bind a LONG value only for insert into a LONG column
ORA-06512: at line 6



在此显示出,一个4000字符的实际上长度为8000字节,这样一个字符串无法永久地存储在一个VARCHAR(4000 char)字段中,这个字符串能放在PL/SQL变量中,因为在PL/SQL中VARCHAR2最大可以达到32K。不过,存储在表中,VARCHAR2则被硬性限制为最多只能存放4000字节。我们可以成功地存储其中2000个字符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
zx@ORCL>declare
  2  l_date varchar2(4000 char);
  3  l_ch   varchar2(1 char) := unistr('\00d6');
  4  begin
  5  l_date:=rpad(l_ch,2000,l_ch);
  6  insert into t(c) values(l_date);
  7  end;
  8  /

PL/SQL procedure successfully completed.

zx@ORCL>
zx@ORCL>select length(c),lengthb(c) from t where c is not null;

LENGTH(C) LENGTHB(C)
---------- ----------
      2000       4000



输出可见,c占用了4000个字节的存储空间。



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-332255-1-1.html 上篇帖子: linux下Oracle数据库的单个表导出导入 下篇帖子: 关于报错"ORA-01747: user.table.column, table.column 或列说明无效" Oracle
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表