兄弟给了个案例,这是在设计开发环节很可能被忽略的一个问题,如下测试表,c1字段按照char存储,c2字段按照byte存储,

create table test(c1 varchar2(10 char), c2 varchar2(10 byte));

现在要扩容字段,以下两种操作,有什么区别?

alter table test modify c1 varchar2(20);
alter table test modify c2 varchar2(30);

《NLS_LENGTH_SEMANTICS参数引申的问题》文章介绍了字符串类型字段按照char和byte存储的形式,默认按照byte存储,但是上述测试表,一个是按照char,一个是按照byte,其实问题可以翻译成,两个扩容操作,一个是char->byte,一个是byte->byte,有什么区别?

从语义上讲,例如UTF-8字符集,1个中文占用三个字节,GBK字符集,1个中文占用两个字节,如果按照存储中文来计算,

1. UTF-8字符集,

原始:c1 varchar2(10 char)可以存储10个中文,占30个字节。

c2 varchar2(10 byte)可以存储10/3=3个中文,占9个字节。

扩容:c1 varchar2(20)是20个字节,可以存储20/3=6个中文。

c2 varchar2(30)是30个字节,可以存储30/3=10个中文。

2. GBK字符集,

原始:c1 varchar2(10 char)可以存储10个中文,占20个字节。

c2 varchar2(10 byte)可以存储10/2=5个中文,占10个字节。

扩容:c1 varchar2(20)是20个字节,可以存储20/2=10个中文。

c2 varchar2(30)是30个字节,可以存储30/2=15个中文。

因此,不同字符集,char和byte之间的转换,存储中文字符的个数,有可能存储更多,有可能存储更少,还可能是相同的,取决于char和byte的换算关系以及扩容的数值。如果只存储英文字符,从存储容量来说,几乎无影响。

从实际运行上,char->byte和byte->byte,还是有些区别的,主要体现在对性能的影响。

如果是char->byte,除了扩容长度,他还设计类型上的转换,从逻辑上讲,需要判断当前字段存储的值,在进行转换后,是否出现超常的情况,

alter table test modify c1 varchar2(20);

这是上述操作对应的10046,为了扩容字段,在数据字典层面,执行了35条SQL,

select count(*) from ind$ i where i.bo#=:1
select /*+ rule */ bucket_cnt, row_cnt, cache_cnt, null_cnt, timestamp#, sample_size, minimum, maximum, distcnt, lowval, hival, density, col#, spare1, spare2, avgcln from hist_head$ where obj#=:1 and intcol#=:2
select /*+ rule */ bucket, endpoint, col#, epvalue from histgrm$ where obj#=:1 and intcol#=:2 and row#=:3 order by bucket
select obj#,type#,ctime,mtime,stime, status, dataobj#, flags, oid$, spare1, spare2 from obj$ where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null
select t.ts#,t.file#,t.block#,nvl(t.bobj#,0),nvl(t.tab#,0),t.intcols,nvl(t.clucols,0),t.audit$,t.flags,t.pctfree$,t.pctused$,t.initrans,t.maxtrans,t.rowcnt,t.blkcnt,t.empcnt,t.avgspc,t.chncnt,t.avgrln,t.analyzetime,t.samplesize,t.cols,t.property,nvl(t.degree,1),nvl(t.instances,1),t.avgspc_flb,t.flbcnt,t.kernelcols,nvl(t.trigflag, 0),nvl(t.spare1,0),nvl(t.spare2,0),t.spare4,t.spare6,ts.cachedblk,ts.cachehit,ts.logicalread from tab$ t, tab_stats$ ts where t.obj#= :1 and t.obj# = ts.obj# (+)
select o.owner#,o.name,o.namespace,o.remoteowner,o.linkname,o.subname from obj$ o where o.obj#=:1
select col#, grantee#, privilege#,max(mod(nvl(option$,0),2)) from objauth$ where obj#=:1 and col# is not null group by privilege#, col#, grantee# order by col#, grantee#
select grantee#,privilege#,nvl(col#,0),max(mod(nvl(option$,0),2))from objauth$ where obj#=:1 group by grantee#,privilege#,nvl(col#,0) order by grantee#
select con#,obj#,rcon#,enabled,nvl(defer,0),spare2,spare3 from cdef$ where robj#=:1
select con#,type#,condlength,intcols,robj#,rcon#,match#,refact,nvl(enabled,0),rowid,cols,nvl(defer,0),mtime,nvl(spare1,0),spare2,spare3 from cdef$ where obj#=:1
select intcol#,nvl(pos#,0),col#,nvl(spare1,0) from ccol$ where con#=:1
select count(*) from (                     select 1 from ind$ i, jijoin$ j                      where i.bo# = tab1obj# and j.obj#=i.obj# and                            j.tab2obj#=:1                     union all                     select 1 from ind$ i, jijoin$ j                      where i.bo# = tab2obj# and j.obj#=i.obj# and                            j.tab1obj#=:1)
select nvl((decode(i.type#, 2, :1, 0) + sum(c.length + decode(sign(c.length - 128), -1, 1, 2))),0), i.obj#, i.initrans, i.type#, decode(i.type#, 4, MOD(i.pctthres$, 256), 0) from icol$ ic,col$ c,ind$ i where i.bo# = :2 and i.obj# in(select i.obj# from ind$ i,icol$ ic where i.bo#=:2 and i.obj#=ic.obj# and ic.intcol#=:3)and i.obj# = ic.obj# and ic.bo# = :2 and ic.intcol# = c.intcol# and c.obj# = :2 group by i.obj#, i.type#, i.initrans, i.pctthres$
select nvl((decode(i.type#, 2, :1, 0) +  sum(c.length + decode(sign(c.length - 128), -1, 1, 2))),0),  i.obj#, i.initrans, i.type#, decode(i.type#, 4, MOD(i.pctthres$, 256), 0),  i.bo#  from ind$ i, jijoin$ j, icol$ ic, col$ c  where i.obj# = j.obj# and        i.bo# = j.tab1obj# and        i.obj# = ic.obj# and        c.obj# = ic.bo# and        c.col# = ic.col# and        j.tab2obj#=: 2  group by i.obj#, i.type#, i.initrans, i.pctthres$, i.bo#
select 1 from icol$ c,ind$ i where i.bo#=:1 and i.type# = 9   and i.obj#=c.obj# and c.intcol# in       (select c1.intcol# from col$ c1 where c1.obj#=:1 and c1.col#=:2)
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("TEST") FULL("TEST") NO_PARALLEL_INDEX("TEST") */ 1 AS C1, CASE WHEN LENGTHB("TEST"."C1")>20 THEN 1 ELSE 0 END AS C2 FROM "BISAL"."TEST" "TEST") SAMPLESUB
select /*+ first_rows */ 1 from "BISAL"."TEST" where LENGTHB("C1") > 20
select 1 from icoldep$ i   where i.bo#= :1 and         i.intcol#= :2 and         i.obj# NOT IN (select j.obj# from jijoin$ j)
select 1 from partcol$ c,ind$ i where i.bo#=:1 and i.obj#=c.obj# and   c.intcol#=:2
select 1 from subpartcol$ c,ind$ i where i.bo#=:1 and i.obj#=c.obj# and  c.intcol#=:2
delete from idl_ub1$ where obj#=:1
delete from idl_char$ where obj#=:1
delete from idl_ub2$ where obj#=:1
delete from idl_sb4$ where obj#=:1
delete from error$ where obj#=:1
select o.owner#, o.name,            o.namespace,    o.obj#,   d.d_timestamp, nvl(d.property,0), o.type#, o.subname, d.d_attrs  from dependency$ d, obj$ o   where d.p_obj#=:1  and   (d.p_timestamp=nvl(:2,d.p_timestamp) or d.property=2)  and   o.owner#=nvl(:3,o.owner#)  and   d.d_obj#=o.obj#  order by o.obj#
update dependency$ set p_timestamp=:1, p_obj#=:2 where d_obj#=:3 and p_obj#=:4
update dependency$ set d_reason = :1 where d_obj# = :2 and p_obj# = :3
update dependency$ set d_attrs = :1 where d_obj# = :2 and p_obj# = :3
delete from superobj$ where subobj# = :1
update tab$ set ts#=:2,file#=:3,block#=:4,bobj#=decode(:5,0,null,:5),tab#=decode(:6,0,null,:6),intcols=:7,kernelcols=:8,clucols=decode(:9,0,null,:9),audit$=:10,flags=:11,pctfree$=:12,pctused$=:13,initrans=:14,maxtrans=:15,rowcnt=:16,blkcnt=:17,empcnt=:18,avgspc=:19,chncnt=:20,avgrln=:21,analyzetime=:22,samplesize=:23,cols=:24,property=:25,degree=decode(:26,1,null,:26),instances=decode(:27,1,null,:27),dataobj#=:28,avgspc_flb=:29,flbcnt=:30,trigflag=:31,spare1=:32,spare2=decode(:33,0,null,:33),spare4=:34,spare6=:35 where obj#=:1
update col$ set intcol#=:3,segcol#=:4,type#=:5,length=:6,precision#=decode(:5,182/*DTYIYM*/,:7,183/*DTYIDS*/,:7,decode(:7,0,null,:7)),scale=decode(:5,2,decode(:8,-127/*MAXSB1MINAL*/,null,:8),178,:8,179,:8,180,:8,181,:8,182,:8,183,:8,231,:8,null),null$=:9,fixedstorage=:10,segcollength=:11,col#=:12,property=:13,charsetid=:14,charsetform=:15,spare1=:16,spare2=:17,spare3=:18,deflength=decode(:19,0,null,:19),default$=:20 where obj#=:1 and name=:2
update obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,dataobj#=:10,flags=:11,oid$=:12,spare1=:13, spare2=:14 where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null
select count(FA#) from SYS_FBA_TRACKEDTABLES where OBJ# = :1 and bitand(FLAGS, :2)=0
LOCK TABLE "TEST" IN EXCLUSIVE MODE  NOWAIT

其中值得关注的,有如下几条,这条SQL会根据c1字段的字节长度是否超过20来设置1或者0,

SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) FROM (SELECT /*+ IGNOR E_WHERE_CLAUSE NO_PARALLEL("TEST") FULL("TEST") NO_PARALLEL_INDEX("TEST") */ 1 AS C1,CASE WHEN LENGTHB("TEST"."C1")>20 THEN 1 ELSE 0 END AS C2 FROM "BISAL"."TEST" "TEST") SAMPLESUB

这条SQL应该是找到所有c1的字节长度超过20个记录,

select /*+ first_rows */ 1 from "BISAL"."TEST" where LENGTHB("C1") > 20

以上两个操作,都是用全表扫描,而且还会显式LOCK这张表,

LOCK TABLE "TEST" IN EXCLUSIVE MODE  NOWAIT

另外,还需要更新tab$、col$、obj$这些数据字典,

update tab$ set ts#=:2,file#=:3,block#=:4,bobj#=decode(:5,0,null,:5),tab#=decode(:6,0,null,:6),intcols=:7,kernelcols=:8,clucols=decode(:9,0,null,:9),audit$=:10,flags=:11,pctfree$=:12,pctused$=:13,initrans=:14,maxtrans=:15,rowcnt=:16,blkcnt=:17,empcnt=:18,avgspc=:19,chncnt=:20,avgrln=:21,analyzetime=:22,samplesize=:23,cols=:24,property=:25,degree=decode(:26,1,null,:26),instances=decode(:27,1,null,:27),dataobj#=:28,avgspc_flb=:29,flbcnt=:30,trigflag=:31,spare1=:32,spare2=decode(:33,0,null,:33),spare4=:34,spare6=:35 where obj#=:1
update col$ set intcol#=:3,segcol#=:4,type#=:5,length=:6,precision#=decode(:5,182/*DTYIYM*/,:7,183/*DTYIDS*/,:7,decode(:7,0,null,:7)),scale=decode(:5,2,decode(:8,-127/*MAXSB1MINAL*/,null,:8),178,:8,179,:8,180,:8,181,:8,182,:8,183,:8,231,:8,null),null$=:9,fixedstorage=:10,segcollength=:11,col#=:12,property=:13,charsetid=:14,charsetform=:15,spare1=:16,spare2=:17,spare3=:18,deflength=decode(:19,0,null,:19),default$=:20 where obj#=:1 and name=:2
update obj$ set obj#=:4, type#=:5,ctime=:6,mtime=:7,stime=:8,status=:9,dataobj#=:10,flags=:11,oid$=:12,spare1=:13, spare2=:14 where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null

我们看下char->char,从逻辑上讲,他只是进行长度的扩容,不涉及类型,

alter table test modify c2 varchar2(30);

10046显示执行的SQL明显比char->byte要少,而且不存在对原表的任何访问,不需要更新tab$、col$、obj$等数据字典,不需要LOCK,

select count(*) from ind$ i where i.bo#=:1
select count(*) from (                     select 1 from ind$ i, jijoin$ j                      where i.bo# = tab1obj# and j.obj#=i.obj# and                            j.tab2obj#=:1                     union all                     select 1 from ind$ i, jijoin$ j                      where i.bo# = tab2obj# and j.obj#=i.obj# and                            j.tab1obj#=:1)
select nvl((decode(i.type#, 2, :1, 0) + sum(c.length + decode(sign(c.length - 128), -1, 1, 2))),0), i.obj#, i.initrans, i.type#, decode(i.type#, 4, MOD(i.pctthres$, 256), 0) from icol$ ic,col$ c,ind$ i where i.bo# = :2 and i.obj# in(select i.obj# from ind$ i,icol$ ic where i.bo#=:2 and i.obj#=ic.obj# and ic.intcol#=:3)and i.obj# = ic.obj# and ic.bo# = :2 and ic.intcol# = c.intcol# and c.obj# = :2 group by i.obj#, i.type#, i.initrans, i.pctthres$
select nvl((decode(i.type#, 2, :1, 0) +  sum(c.length + decode(sign(c.length - 128), -1, 1, 2))),0),  i.obj#, i.initrans, i.type#, decode(i.type#, 4, MOD(i.pctthres$, 256), 0),  i.bo#  from ind$ i, jijoin$ j, icol$ ic, col$ c  where i.obj# = j.obj# and        i.bo# = j.tab1obj# and        i.obj# = ic.obj# and        c.obj# = ic.bo# and        c.col# = ic.col# and        j.tab2obj#=: 2  group by i.obj#, i.type#, i.initrans, i.pctthres$, i.bo#
select 1 from icol$ c,ind$ i where i.bo#=:1 and i.type# = 9   and i.obj#=c.obj# and c.intcol# in       (select c1.intcol# from col$ c1 where c1.obj#=:1 and c1.col#=:2)
select 1 from icoldep$ i   where i.bo#= :1 and         i.intcol#= :2 and         i.obj# NOT IN (select j.obj# from jijoin$ j)
select 1 from partcol$ c,ind$ i where i.bo#=:1 and i.obj#=c.obj# and   c.intcol#=:2
select 1 from subpartcol$ c,ind$ i where i.bo#=:1 and i.obj#=c.obj# and  c.intcol#=:2
delete from idl_ub1$ where obj#=:1
delete from idl_ub2$ where obj#=:1
delete from idl_sb4$ where obj#=:1
delete from error$ where obj#=:1
select o.owner#, o.name,            o.namespace,    o.obj#,   d.d_timestamp, nvl(d.property,0), o.type#, o.subname, d.d_attrs  from dependency$ d, obj$ o   where d.p_obj#=:1  and   (d.p_timestamp=nvl(:2,d.p_timestamp) or d.property=2)  and   o.owner#=nvl(:3,o.owner#)  and   d.d_obj#=o.obj#  order by o.obj#
update dependency$ set p_timestamp=:1, p_obj#=:2 where d_obj#=:3 and p_obj#=:4
update dependency$ set d_reason = :1 where d_obj# = :2 and p_obj# = :3
update dependency$ set d_attrs = :1 where d_obj# = :2 and p_obj# = :3
delete from superobj$ where subobj# = :1
delete from tab_stats$ where obj#=:1

char->byte或者byte->char,数据库需要做更多的操作,还需要进行数据的校验,如果对大表进行这种转换,从执行时间上,就会更久,而且LOCK表的操作,就会影响表的事务并发,进而对系统产生一定的影响。而单纯的char->char或者byte->byte的扩容,仅需要更新一些数据字典,不存在LOCK的需求,不需要读取原表,执行时间上,自然就很快。

归根结底,我认为实际场景中不太可能出现主动char->byte或者byte->char的场景,大多情况下,可能都是“误伤”,例如某个库,初始参数NLS_LENGTH_SEMANTICS设置成了按照byte存储,迁移到的新库,NLS_LENGTH_SEMANTICS设置成了按照char存储,这就涉及到转换。或者测试环境NLS_LENGTH_SEMANTICS按照char存储,生产环境NLS_LENGTH_SEMANTICS按照byte存储,准备上线SQL就忽略了字段应该带着的char或者byte,选择使用默认的,这就很容易“误伤”,生产上执行了,还可能都不知道他的影响,系统并发受到影响的同时,字段存储容量很可能不满足需求。归根结底,这些都是设计开发规范不严谨导致的,前提还是得知道他的原理,只是刻意复制,很可能导致其他的隐患问题。

近期更新的文章:

《小白学习MySQL - 大小写敏感问题解惑》

《小白学习MySQL - only_full_group_by的校验规则》

《最近碰到的几个问题》

《PG逻辑复制的REPLICA IDENTITY设置》

《Linux的dd指令》

文章分类和索引:

《公众号800篇文章分类和索引》

Oracle字符串类型扩容隐患相关推荐

  1. oracle字符串类型的时间常用操作

    实际使用中,我们在数据库中存储的都是字符串[number(14),number(8)],日期类型的很少使用,相信这也是很多公司都是如此. 好处囊,前台取展示等无需转化,无需使用特定组件等. 不便之处囊 ...

  2. Oracle数据库里面查询字符串类型的字段不为空和为空的SQL语句:

    摘要:近期项目中,在做高级查询的时候有个条件是根据选择的字段,然后再选择字段的值为空和不为空做查询,在写SQL语句的时候费了很长时间,现在记录一下,方便日后查看: 一:查询字符串类型的字段的值不为空的 ...

  3. oracle字符串提取函数,oracle字符串分割和提取函数定义

    oracle字符串分割和提取函数定义 oracle字符串分割和提取 分割 create or replace function Get_StrArrayLength ( av_str varchar2 ...

  4. clob类型用java怎么存,Java 储存和读取 oracle CLOB 类型字段的实用方法

    当前位置:我的异常网» 编程 » Java 储存和读取 oracle CLOB 类型字段的实用方法 Java 储存和读取 oracle CLOB 类型字段的实用方法 www.myexceptions. ...

  5. oracle的insert语句clob,.Net 操作 Oracle CLOB类型字段 INSERT 超长数据

    如果仅仅在数据库中设置了类型为 CLOB 字段类型,使用普通的 INSERT 语句直接写入数据到数据库的话,它依然会将其视为 VARCHAR 类型数据,并最大长度为 4000 字符.超过该长度会报出字 ...

  6. redis 自增_坏了,Redis的字符串类型竟然被张三学明白了?

    Redis简介 Redis(Remote dictionary server) 是一款高性能的开源非关系型缓存数据库,Redis使用C语言编写,支持多种类型的数据结构,如字符串,字典,列表,集合,有序 ...

  7. oracle关于字符串函数,Oracle字符串处理函数

    Oracle字符串处理函数 Oracle字符串处理函数 2008年10月18日 星期六 23:45 项目中有涉及存储过程对字符串的处理,所以就将在网上查找到的资料汇总,做一个信息拼接式的总结. 以下信 ...

  8. 字符串类型:不能忽略的 COLLATION

    MySQL 数据库的字符串类型有 CHAR.VARCHAR.BINARY.BLOB.TEXT.ENUM.SET.不同的类型在业务设计.数据库性能方面的表现完全不同,其中最常使用的是 CHAR.VARC ...

  9. oracle blob类型在,Oracle blob类型 实践

    Oracle blob类型 实践 BLOB的含义 BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的容器. 在计算机中,BLOB常常是数据库中用来存储二进制 ...

最新文章

  1. Opengl编程学习笔记(五)——从FRAGMENT到PIXEL(framebuffer 帧缓存)
  2. 在Notes客户端如何打开隐藏视图
  3. ThreadLocal 和神奇的数字 0x61c88647
  4. ssh长时间不操作便断开_不懂Excel文件恢复方法?只要跟着这样操作,便可以光速恢复文件...
  5. 批量备SAP中CBO ABAP 程序代码为TXT文件备份
  6. 7-26 有重复的数据I (10 分)
  7. php删除菜单栏,如何删除WordPress站点健康状态面板和菜单项
  8. 没有基础的想转行学习Python怎么学
  9. [蓝桥] 基础练习 十进制转十六进制 (java)
  10. mdstyle暂存备用
  11. Johnson算法PlantSimulation解决两机器多作业排版问题
  12. 解决ThinkServer TS250中网卡在centos6.5中没有安装驱动(驱动安装)
  13. linux下如何关闭端口占用,Linux端口被占用? -- Linux查看端口使用状态、关闭端口方法...
  14. HTTP接口的请求参数类型有哪些
  15. 看了这些 Go2 错误处理的提案,我真的会谢
  16. 安装图解:Linux Mint 4.0(Daryna)(或者说完美的桌面系统)
  17. java.lang.OutOfMemoryError: Java heap space
  18. 如何安装projectlombok
  19. October 2009
  20. Python OJ输入输出

热门文章

  1. JQuery 添加元素(jquery对象),删除元素( .remove())
  2. 微博登录显示服务器解析失败怎么办,微博air登录失败, air无法登录的原因 -电脑资料...
  3. matlab累积概率分布,[转载]Matlab累积分布函数cdf与概率密度函数pdf
  4. 信号与系统:拉式变换(s域)求解电路的零输入、零状态响应
  5. 软件测试的自我发展规划
  6. 阿里云code登录,阿里云code使用教程
  7. 如何将计算机删除的程序还原,如何去恢复电脑上误删的文件?简单恢复
  8. 什么是 jQuery?
  9. 雷达原理第五版微盘pdf下载_雷达原理(第5版电子信息类精品教材)
  10. 训练ChatGPT的必备资源:语料、模型和代码库完全指南