NLS_LENGTH_SEMANTICS参数引申的问题

简介: 由于某项目的特殊性,开发数据库环境有两套,两边都可能对表结构进行一些修改,因此写了一个工具,比对两边的结构元数据,其中碰到一个问题,很细微,但确实值得注意,在此记录下。

由于某项目的特殊性,开发数据库环境有两套,两边都可能对表结构进行一些修改,因此写了一个工具,比对两边的结构元数据,其中碰到一个问题,很细微,但确实值得注意,在此记录下。


问题:

比对两个环境中同一张表的同一个VARCHAR2类型的字段长度时,发现一个环境中其长度是30,一个环境中其长度是120,两个环境中建表语句该字段定义都是VARCHAR2(30)。

比较字段长度使用的是user_tab_cols视图中DATA_LENGTH这个字段。


分析过程:

1. 查看文档中对DATA_LENGTH的定义,该值是NUMBER类型的,描述是“列的长度(以字节)”



2. 再比对两边环境中user_tab_cols视图中的字段,有个字段引起注意,CHAR_USED,一边是C,一边是B。

再查看这个字段的解释,CHAR_USED是一位的字符类型,B代表该列使用BYTE计算其长度,C表示该列使用CHAR计算其长度,NULL空值表示该列类型不是以下四种之一:CHAR、VARCHAR2、NCHAR、NVARCHAR2。

从以上示例的结果看,说明两边库计算字段长度的单位不同,一边是以BYTE计算,一边是以CHAR计算。



3. 进一步看,以CHAR计算的环境中,还有一个字段CHAR_LENGTH,其值是30,从解释看,该字段含义是以字符计算的列长度,且仅适用于以下四种类型:CHAR、VARCHAR2、NCHAR、NVARCHAR。



4. 那么现在看这个问题基本能确定了,有一套环境,DATA_LENGTH是120,CHAR_LENGTH是30,字符字段的长度是以CHAR计算的,该环境使用的是UTF-8字符集,DATA_LENGTH字段的含义是以BYTE计算,即估算按照4*30=120个字节为该列允许的字段长度。

如果字符类型字段是以CHAR定义,那么DATA_LENGTH字段的值会取决于数据库字符集,表示的是字节。


解决过程:

字符类型长度的计算是受一个NLS_LENGTH_SEMANTICS参数的控制,其默认值是BYTE,该参数是可以动态修改的,一般情况下都是用字节计算,但有时可能为了特殊的应用要求,改为以字符计算。




方法1:系统级修改,永久生效

ALTER SYSTEM SET NLS_LENGTH_SEMANTICS='BYTE', SCOPE=BOTH;

不需要重启数据库,立即生效。


方法2:session级修改

ALTER SESSION SET NLS_LENGTH_SEMANTICS='BYTE';

当前SESSION生效,但只会影响接下来的CREATE TABLE语句,已有表则不会受影响。


方法3:创建表时指定计算方法

CREATE TABLE XXX
(
A     VARCHAR2(1 char)
);

CREATE TABLE XXX
(
A      VARCHAR2(1 byte)
);


引申问题:

1. 如果原来NLS_LENGTH_SEMANTICS值是CHAR,现在改为BYTE,则可能需要注意的是,创建新表字段时,需要考虑应用对这些字符的处理,例如应用会处理中文字符,GBK字符集下一个中文字符占用2个字节,UTF-8下一个中文字符占用3个字符,CHAR改为BYTE方式存储,定义为VARCHAR2(10)类型的字段,原来CHAR时,可以存储10个字符,如果都是中文字符,GBK字符集,那么实际字节是20,现在只能存储10个字节,即5个中文字符。UTF-8字符集,那么实际字节是30,现在只能存储3个中文字符。相当于存储内容缩小了。

如果是从BYTE改为CHAR,那么 VARCHAR2(10),原先只能存储10个字节,GBK字符集下,只能存储5个中文字符,现在改为CHAR计算存储,那么实际能存储10个字符。UTF-8字符集下,原先能存储3个中文字符,现在能存储10个中文字符,相当于存储内容扩大了。

2. 如果使用ALTER SESSION改变的参数值,例如原来是CHAR,现在改为BYTE,再创建表CREATE TABLE,会发现其创建语句默认变为:

create table XXX
(
  A           VARCHAR2(3 BYTE)
...
);
默认带有一个BYTE关键字。

3. NLS_LENGTH_SEMANTICS默认是CHAR,为何我碰到的问题中,该值变为BYTE了?

从alert日志中可以发现,启动数据库时,未采用默认参数值的部分发现了这个参数:


再查对应的init.ora配置文件,发现确实对该值设置了char,因此CREATE TABLE默认字符类型长度都是以CHAR计算。

4. 顺着(3),对于数据库参数设置,有三个视图:nls_database_parameters、nls_instance_parameters和nls_session_parameters,都有该参数的设置,对应值为:

nls_database_parameters中NLS_LENGTH_SEMANTICS是BYTE

nls_instance_parameters中NLS_LENGTH_SEMANTICS是char

nls_session_parameters中NLS_LENGTH_SEMANTICS是CHAR

nls_database_parameters取值与创建数据库时存储在数据库中的信息相关,这与环境变量和参数文件等是统统没有关系的。不会受到环境变量的影响。

nls_instance_parameters只受参数文件的影响,而不受环境变量影响。

nls_session_parameters取值默认会从nls_instance_parameters继承,但是如果,我们在环境变量或者通过ALTER SESSION 改变了nls的相关参数,则会覆盖默认值。

即,他们的使用优先级是nls_session_parameters>nls_instance_parameters>nls_database_parameters。

nls_instance_parameters取自init.ora配置文件中的值,nls_session_parameters默认选择nls_instance_parameters的值,但如果使用ALTER SESSION或环境变量改变该值,则会选择此值。

目录
相关文章
|
5月前
|
编译器 C++ 开发者
C++一分钟之-返回值优化与Move Semantics
【6月更文挑战第19天】C++的RVO与移动语义提升效率,减少对象复制。RVO是编译器优化,避免临时对象的创建。移动语义通过右值引用和`std::move`转移资源所有权。注意RVO不是总有效,不应过度依赖。使用移动语义时,避免误用`std::move`导致对象无效。示例展示了RVO和移动构造函数的应用。理解并恰当使用这些机制能写出更高效代码。
58 3
|
6月前
|
Oracle 安全 Java
[Java] `JDK17` 模式变量 `Pattern variable` Variable ‘request‘ can be replaced with pattern variable
[Java] `JDK17` 模式变量 `Pattern variable` Variable ‘request‘ can be replaced with pattern variable
|
数据可视化 数据处理
解决 FAILED: UDFArgumentException explode() takes an array or a map as a parameter 并理解炸裂函数和侧视图
解决 FAILED: UDFArgumentException explode() takes an array or a map as a parameter 并理解炸裂函数和侧视图
79 0
|
XML SQL Oracle
通过wm_concat 函数报错:ora06502-character string buffer to small浅谈wm_concat、Listagg、xmlagg函数的使用和结果不确定性
wm_concat、Listagg、XMLAGG ora06502-character string buffer to small varchar2类型最多支持4000字符(如果是中文会更短到2000) 那无论是wm_concat函数还是listagg函数,在处理超过4000字符(如果是中文会更短到2000)长度的时候都会报错,提供(xmlagg(xmlparse(content 合并字段||',' wellformed) order by 排序字段).getclobval() )给大家使用来解决此问题!
通过wm_concat 函数报错:ora06502-character string buffer to small浅谈wm_concat、Listagg、xmlagg函数的使用和结果不确定性
|
Python
【hacker的错误集】TypeError: can‘t multiply sequence by non-int of type ‘str‘
我比较喜欢通过单词的意思来分析报错 TypeError类型错误 multiply乘 sequence 序列 通过分析可以得出报错意思大概是类型错误:无法将序列与字符串类型的非整数相乘
365 0
【hacker的错误集】TypeError: can‘t multiply sequence by non-int of type ‘str‘
|
C++ API 存储
protobuf 中的嵌套消息的使用 主要对set_allocated_和mutable_的使用
protobuf的简单的使用,不过还留下了一个问题,那就是之前主要介绍的都是对简单数据的赋值,简单数据直接采用set_xx()即可,但是如果不是简单变量而是自定义的复合类型变量,就没有简单的set函数调用了,下面看一个简单的例子。
2545 0
重构——26以字面常量取代魔法数(Replace Magic Number with Symbolic Constant)
以字面常量取代魔法数(Replace Magic Number with Symbolic Constant):你有一个字面数值,带有特别含义:创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量
1435 0