相同的字符在不同的字符集中对应着不同的字符编码,这个通常称为字符集不兼容或者不完全兼容;比如zhs16gbk和al32utf8,他们存储的ascii码的字符编码都是相同的,但对于汉字却是不同的。
如果两个字符集对于相同的字符采用的相同的字符编码,我们称之为字符兼容,范围大的叫做范围小的字符集的超级。我们通常遇到的zhs16cgb231280,zhs16gbk就是这样的情况,后者是前者的超集。
操作系统字符集和数据库字符集没有任何区别,都是按照固定编码来存储字符的,而我们看到的东西,那个应该叫作字体
客户端字符集变量的意义:数据库端并不知道你客户端OS使用什么字符集,只有通过NLS_LANG这个参数才能告诉数据库我将以什么字符集来和server进行通讯,这样server就会知道该不该做相应的字符转换。
关键是这支NLS_LANG参数,决定了客户端与db之间的转换模式,而数据库通讯字符转换都是在客户端完成的。而db的字符集定义时主要考虑兼容性,而UTF-8可以最大限度支持各种字符,所以Oracle也推荐采用UTF-8模式
数据库字符集-unicode
客户端字符集=操作系统字符集
我的意思是,将数据库的字符集设置到足够大,最好是unicode。
这时候即使发生字符转换,也不会出现问题,因为这个字符集包含了所以其他字符集的字符。
之所以要将客户端字符集设置成和操作系统字符集一致,就是避免显示问题。
如果将客户端字符集设置和数据库一致,有一个危险的地方就是,此时数据库不在对输入字符作字符转换,如果你实际输入的字符编码并不是字符集编码,那样就会出问题了。这个我已经在文章里举了例子。
如果使用 3个或者3个以上字节的数据库字符集,则client 推荐使用 本地字符集。
虽然这样可能影响 varchar 类型的实际最大字符数,但这样做也很有好处,上面都说了。
在这种情况下如果将 client 设置为和数据库字符集一致,则可能遭遇bug。比如对于中文或者日文,utf8的数据库字符集,client 也设置为 utf8的环境变量,则很多字符出现在字符串的末尾的时候 在传输层就会出错。我看见过有人通过加 空格在末尾来解决的,但这毕竟不是正规方法。
如果数据库字符集本来就使用了 US7ASCII一类,或者某些国家单字节 or 双字节字符集,要在client设置本地的不同的字符集则可能在数据库端无法表达而丢失信息出现?? 。
说到底就是用于显示字符的操作系统没有在字符编码中找到对应的字符导致的,造成这种现象的主要原因可能是:
1:输入操作的os字符编码和查询的os字符编码不一致导致出现乱码。
2:输入操作的客户端字符集(nls_lang)和查询客户端字符集(nls_lang)不同,也可能导致查询返回乱码或者错误的字符。
一个误区:
看到很多人在出现乱码的时候都首先要做的就是将客户端字符集设置和数据库一致,其实这是没有太多根据的。
设想一下,假如数据库字符集是al32utf8,里面存储这一些中文字符,而我的客户端操作系统是英文的,此时我将客户端的nls_lang设置成al32utf8,这样会解决问题吗?这样客户端就能显示中文了吗?客户端就能输入中文了吗?现在客户端是英文的,它的字符集里根本就没有汉字的编码,我们简单的修改一下客户端的字符集又有什么用?客户端字符集设置无非就是告诉oracle我将以什么样的字符集与数据库进行数据交换,对于解决乱码问题毫无关系。
正确的做法是将客户端的操作系统改成支持中文字符,并将客户端字符集改成和操作系统一致的字符集,这样才能真正的解决问题。
如相符,在日志中记录
DatabaseCharacterset is ZHS16GBK。
如果不相同,以数据库字符集为准,更新控制文件中字符集记录,
类似日志如下:updating characterset in controlfile to ZHS16GBK