# Oracle中nls_characterset与nls_nchar_characterset的设置及其影响
在众多的资料中,仅是说了
nls_characterset数据库字符集 nls_nchar_characterset数据库国家字符集或者国际化设置字符集
nls_characterset与数据库中char、varchar blob类型的属性字段有密切的关系
nls_nchar_characterset与nchar、nvarchar2属性字段类型相关
偶然的一个机会,让我怀疑,虽然,还没有查到确切的文献依据,nls_characterset Oracle数据库字符集的设置,对C/S结构下的数据库访问应用有比较大的影响。在这种结构中存在一个不起眼的细节,即在C/S结构下的数据库访问中,客户端经常将要执行的SQL语句发送到数据服务器端DBMS去执行,而这个SQL语句必定是要按照一定的字符集进行编码的。发送SQL语句编码的这个字符集或者是约定或者是客户端和服务器端协商得到的。但是,不管这个字符集是如何确定的,这个对我们要说明的问题影响不是太大,我们所需假设的即这个发送到服务器端执行的SQL语句是按照某种字符集编码。
一般情况下,服务器端只要对客户端发送过来的SQL语句解码正确,我们是不会发现其有异常的。但是,在国际化数据库应用下或扩展已有的数据库应用成为国际化版本时,如果nls_nchar_characterset设置欠乏考虑,就会出现写入数据库的数据,在字节级的信息就是乱码,更况论其显示值了,那就更是错误百出了,因为根基就是错的!观察表属性的字节级信息,在oracle里是通过dump(columnName,16)观察。
例如,在某个数据库中nls_nchar_characterset的设置为GBK,为了应付国际化,数据库应用对数据库表中可能包含非GBK字符集范围的列,全部定义为nachar或者其他国际化属性类型。按照通常网上资料或者简明Oracle说明书,这样的设置应该是没有问题,但是,在这里再次强调下,我们出问题的不起眼的源头,某些C/S结构下的数据库访问,客户端是通过发送SQL语句到数据库执行的方式,达到访问数据库的效果。这样架构下,就会牵扯数据库服务器端,会以何种字符集,让DBMS统一看待、处理SQL语句呢?我想,这里Oracle数据库系统,可能会简单地以ASCII字符集处理SQL关键字、分割符,而以nls_characterset设定的字符集应对SQL语句中非关键字,例如查询条件、赋值等。或者它会以更简单、明快的处理方案,将客户端不管以何种编码过来的SQL语句串都转换到nls_characterset设定的字符集,统一进入SQL后续执行的核心区。不管Oracle可能会采用这两种可能的哪一种,对于客户端发送过来的SQL语句中非SQL规范关键字范畴的部分,数据库服务器端可能都会采用nls_characterset设定的字符集在后期进行处理,这样,在后面的继续处理中就会发生问题。
关于Oracle客户端访问程序,其发送过来的SQL语句的字符集可以和Oracle数据库nls_characterset设置是不同的,而不是像有些网上文档说的两者之间要保持严格相同。实际上,Oracle可能提供了对客户端不同编码格式的数据进行解码的过程,但是,Oracle解码完成后的终点基座、最终的目标可能是Oracle自身设置的nls_characterset数据库字符集。
呵呵,在这里声明一下,我也是以概念逻辑上的推测、猜度别人,而非查到第一手的关键文献资料。但是,上面所有基于猜测,都是某次Oracle故障后,自己找出的唯一比较近乎合理的解释!我也没有强求这个文档的真金白银的价值,只希望对大家有用,提供一个可能的新视角,对其价值看做仅作参考即可。很多时候,我喜欢的编程生活是,不需要懂得太多的技术细节,只需要知道它的概念逻辑即可,即其可能的实现是什么就可以了!懂得太深,会耗去很多宝贵的时间的。而且,只要你懂得其概念逻辑和模型后,后面的全部只是时间问题,也和具体实现语言也没有太大的关系,这就是概念逻辑的优势,在没有硝烟的战场是取得胜利。在这里将这次的故障总结写入博客,也是希望有人能够指出其中纰露 :)
基于上面的所有分析,我们就可以看出来,如果客户端JDBC访问以utf-8方式将要在服务器端执行的SQL语句发送到服务器端,而其insert或者update的值域信息里面含有非nls_characterset,这个时候,请注意,字节级的乱码就出现了。因为两个字符集间utf-8<-->nls_characterset的映射关系,在事实上确实也并不是完全包含的。当然,你可以严格限定你的客户端发送到服务器端的utf-8字符编码,就是nls_characterset的一个子集,这样你也就不会出现乱码问题。但是,当你扩展你这个数据库应用,来应付更多的国际化的时间,你一定要小心了。虽然,Oracle JDBC驱动为了处理国际化的问题,已经将自己发送到服务器端的SQL语句的字符编码按照utf-8字符集进行实施,它可以是被认为近乎无限的语言处理能力,但是,你的数据库服务器端,在Oracle9i版本,未必有能力能够处理,因为它可能按照nls_characterset字符集的设定去处理客户端发送过来的SQL语句。不过,实际上Oracle可以作的更多,它可以进一步分析,如果SQL语句中含有nchar的属性列的处理,就不能以nls_characterset来处理SQL中的"值域“信息,我们程序员的控制力是还有很大空间的 :)。从故障的各方面分析来看,在Oracle9i版本好似还没有这样的特殊智能。
验证我这个推测的一个佐证是查看V$SQLArea视图的定义,可以看到其SQL_TEXT列的类型为varchar(1000),即这个号称可以共享SQL语句的缓存统计表,处理客户端发送过来的SQL语句,其存储的字符集为nls_characterset!
欢迎大家指正!
在众多的资料中,仅是说了
nls_characterset数据库字符集 nls_nchar_characterset数据库国家字符集或者国际化设置字符集
nls_characterset与数据库中char、varchar blob类型的属性字段有密切的关系
nls_nchar_characterset与nchar、nvarchar2属性字段类型相关
偶然的一个机会,让我怀疑,虽然,还没有查到确切的文献依据,nls_characterset Oracle数据库字符集的设置,对C/S结构下的数据库访问应用有比较大的影响。在这种结构中存在一个不起眼的细节,即在C/S结构下的数据库访问中,客户端经常将要执行的SQL语句发送到数据服务器端DBMS去执行,而这个SQL语句必定是要按照一定的字符集进行编码的。发送SQL语句编码的这个字符集或者是约定或者是客户端和服务器端协商得到的。但是,不管这个字符集是如何确定的,这个对我们要说明的问题影响不是太大,我们所需假设的即这个发送到服务器端执行的SQL语句是按照某种字符集编码。
一般情况下,服务器端只要对客户端发送过来的SQL语句解码正确,我们是不会发现其有异常的。但是,在国际化数据库应用下或扩展已有的数据库应用成为国际化版本时,如果nls_nchar_characterset设置欠乏考虑,就会出现写入数据库的数据,在字节级的信息就是乱码,更况论其显示值了,那就更是错误百出了,因为根基就是错的!观察表属性的字节级信息,在oracle里是通过dump(columnName,16)观察。
例如,在某个数据库中nls_nchar_characterset的设置为GBK,为了应付国际化,数据库应用对数据库表中可能包含非GBK字符集范围的列,全部定义为nachar或者其他国际化属性类型。按照通常网上资料或者简明Oracle说明书,这样的设置应该是没有问题,但是,在这里再次强调下,我们出问题的不起眼的源头,某些C/S结构下的数据库访问,客户端是通过发送SQL语句到数据库执行的方式,达到访问数据库的效果。这样架构下,就会牵扯数据库服务器端,会以何种字符集,让DBMS统一看待、处理SQL语句呢?我想,这里Oracle数据库系统,可能会简单地以ASCII字符集处理SQL关键字、分割符,而以nls_characterset设定的字符集应对SQL语句中非关键字,例如查询条件、赋值等。或者它会以更简单、明快的处理方案,将客户端不管以何种编码过来的SQL语句串都转换到nls_characterset设定的字符集,统一进入SQL后续执行的核心区。不管Oracle可能会采用这两种可能的哪一种,对于客户端发送过来的SQL语句中非SQL规范关键字范畴的部分,数据库服务器端可能都会采用nls_characterset设定的字符集在后期进行处理,这样,在后面的继续处理中就会发生问题。
关于Oracle客户端访问程序,其发送过来的SQL语句的字符集可以和Oracle数据库nls_characterset设置是不同的,而不是像有些网上文档说的两者之间要保持严格相同。实际上,Oracle可能提供了对客户端不同编码格式的数据进行解码的过程,但是,Oracle解码完成后的终点基座、最终的目标可能是Oracle自身设置的nls_characterset数据库字符集。
呵呵,在这里声明一下,我也是以概念逻辑上的推测、猜度别人,而非查到第一手的关键文献资料。但是,上面所有基于猜测,都是某次Oracle故障后,自己找出的唯一比较近乎合理的解释!我也没有强求这个文档的真金白银的价值,只希望对大家有用,提供一个可能的新视角,对其价值看做仅作参考即可。很多时候,我喜欢的编程生活是,不需要懂得太多的技术细节,只需要知道它的概念逻辑即可,即其可能的实现是什么就可以了!懂得太深,会耗去很多宝贵的时间的。而且,只要你懂得其概念逻辑和模型后,后面的全部只是时间问题,也和具体实现语言也没有太大的关系,这就是概念逻辑的优势,在没有硝烟的战场是取得胜利。在这里将这次的故障总结写入博客,也是希望有人能够指出其中纰露 :)
基于上面的所有分析,我们就可以看出来,如果客户端JDBC访问以utf-8方式将要在服务器端执行的SQL语句发送到服务器端,而其insert或者update的值域信息里面含有非nls_characterset,这个时候,请注意,字节级的乱码就出现了。因为两个字符集间utf-8<-->nls_characterset的映射关系,在事实上确实也并不是完全包含的。当然,你可以严格限定你的客户端发送到服务器端的utf-8字符编码,就是nls_characterset的一个子集,这样你也就不会出现乱码问题。但是,当你扩展你这个数据库应用,来应付更多的国际化的时间,你一定要小心了。虽然,Oracle JDBC驱动为了处理国际化的问题,已经将自己发送到服务器端的SQL语句的字符编码按照utf-8字符集进行实施,它可以是被认为近乎无限的语言处理能力,但是,你的数据库服务器端,在Oracle9i版本,未必有能力能够处理,因为它可能按照nls_characterset字符集的设定去处理客户端发送过来的SQL语句。不过,实际上Oracle可以作的更多,它可以进一步分析,如果SQL语句中含有nchar的属性列的处理,就不能以nls_characterset来处理SQL中的"值域“信息,我们程序员的控制力是还有很大空间的 :)。从故障的各方面分析来看,在Oracle9i版本好似还没有这样的特殊智能。
验证我这个推测的一个佐证是查看V$SQLArea视图的定义,可以看到其SQL_TEXT列的类型为varchar(1000),即这个号称可以共享SQL语句的缓存统计表,处理客户端发送过来的SQL语句,其存储的字符集为nls_characterset!
欢迎大家指正!