我用的数据库是oracle11.2.0.4,数据库字符集是al32utf8。

客户端就是同一台机器的windows 7

用window7的客户端连接

查看windows客户端中文字符



虚拟机   192.168.10.5    数据库是test1    数据库字符集是al32utf8。


C:\Users\Administrator>echo %NLS_LANG%             -------查看客户端字符命令

SIMPLIFIED CHINESE_CHINA.AL32UTF8  


set NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK       -----设置客户端字符命令



数据库是test1   ,数据库字符集是al32utf8。



--session 1 设置客户端字符集为 zhs16gbk(修改注册表nls_lang项的characterset 为zhs16gbk) 向表中插入两个中文字符。



C:\Users\Administrator>echo %NLS_LANG%

SIMPLIFIED CHINESE_CHINA.ZHS16GBK


C:\Users\Administrator>sqlplus sys/oracle@test1 as sysdba;      


SQL> create table test(col1 number(1),col2 varchar2(10));

   SQL> insert into test values(1,'中国'); --1为session 1的标记

        1 row created.

        SQL> commit;

        Commit complete.  

        

SQL> create table test(col1 number(1),col2 varchar2(10));


表已创建。


SQL>  insert into test values(1,'中国');


已创建 1 行。


SQL>        

        

     

     

        

-session 2 设置客户端字符集 al32utf8(修改注册表nls_lang项的characterset 为al32utf8),与数据库字符集相同。 向表中插入两个和session 1相同的中文字符。        

  

C:\Users\Administrator>echo %NLS_LANG%

SIMPLIFIED CHINESE_CHINA.AL32UTF8


C:\Users\Administrator>sqlplus sys/oracle@test1 as sysdba;


SQL*Plus: Release 11.2.0.1.0 Production on 鏄熸湡涓 11鏈?28 10:35:33 2016


Copyright (c) 1982, 2010, Oracle.  All rights reserved.



杩炴帴鍒?

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options


SQL>





  SQL> insert into test values(2,'中国'); --2为session 2的标记

        1 row created.

        SQL> commit;

        Commit complete.


--session 1


       SQL> select * from test;


      COL1 COL2

---------- --------------------

         1 中国

         2 ???    

                                                                  

--session 2

     SQL> select * from test;


      COL1 COL2

---------- ----------

         1 涓浗

         2 中国


SQL>

                 

 

 

 从session 1和session 2的结果中可以看到,相同的字符(注意,我指的是我们看到的,显示为相同的字符 中国),在不同的字符集输入环境(客户端环境变量)下,显示成了乱码。    

   在zhs16gbk字符集的客户端,我们看到了utf8字符集客户端输入的相同的中文变成了乱码-->col1=2的col2字段

SQL> select * from test;


      COL1 COL2

---------- --------------------

         1 中国

         2 ???

   

   

   

  在utf8字符集客户端,我们看到zhs16gbk字符集的客户端输入的中文变成了另外的字符  -->col1=1的col2字段   

  

  SQL> select * from test;


      COL1 COL2

---------- ----------

         1 涓浗      -----三个字符

         2 中国

         

         

 

 

SQL> select col1,dump(col2,1016) from test;


      COL1

----------

DUMP(COL2,1016)

-------------------------------------------------------------


         1

Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd


         2

Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa        



不同的客户端输入的中文字符,在数据库中存储的字符编码不一致。



session 1 输入的字符"中国" 在数据库中存储的字符编码为”e4,b8,ad,e5,9b,bd".

session 2 输入的字符"中国" 在数据库中存储的字符编码为”d6,d0,b9,fa".


会话一、

中国-------e4,b8,ad,e5,9b,bd     数据库发现客户端16gbk跟数据库端的utf8字符不一致    做了字符转换  首先将客户端 中国 16gbk的(双字节)编码转成utf8(三字节编码) 然后存储下来。 


会话二、

中国-------d6,d0,b9,fa           数据库发现客户端utf8跟数据库端的utf8字符一致      欺骗了数据库端,不做字符转换,直接以16gbk(双字节)的字符编码存储到数据库(utf8数据库编码为三字节)中。



              

session 1 输入的字符"中国" 在数据库中存储的字符编码为”e4,b8,ad,e5,9b,bd"

session 2 输入的字符"中国" 在数据库中存储的字符编码为”d6,d0,b9,fa"



数据库看到客户端的字符集和数据库的字符集一致,此时oracle将不会再对字符作转换,因为它认为两边的字符编码是一致的。而此时,

我们欺骗了数据库,尽管我们将客户端字符集设置为和数据库一致,但是其实我们使用的是zhs16gbk字符集编码(因为此时windows使用的就是这个字符编码),

对于字符"中国",zhs16gbk字符集里对应的编码为d6,d0,b9,fa。此时,oracle不加理会的直接将这个编码保存到了数据库中。




客户端的字符集的作用就是告知数据库端传输的字符编码,跟数据库端的字符集一致就直接存储,不一致就字符转换。




查询是存储的字符转换的逆过程。

session1查询

SQL> select * from test;


      COL1 COL2

---------- --------------------

         1 中国

         2 ???

         



当session 1开始查询时,oracle从表中取出这两个字符,并按照字符集al32utf8和字符集zhs16gbk的编码映射表,将它的转换成zhs16gbk字符编码,对于编码“e4,b8,ad,e5,9b,bd”,

它对应的zhs16gbk的字符编码为"d6,d0,b9,fa",这个编码对应的字符为”中国“,所以我们看到了这个字符正常显示出来了,

而对于字符集存储的al32utf8字符编码“d6,d0,b9,fa”,

由于我们用于显示字符的windows环境使用的是zhs16gbk字符集,而在zhs16gbk字符集里面并没有对应这个编码的字符或者属于无法显示的符号,utf8---16gbk 转换找不到

于是使用了"?"这样的字符来替换,这就是为什么我们看到session 2输入的字符变成了这样的乱码。






session2查询

SQL> select * from test;


      COL1 COL2

---------- ----------

         1 涓浗

         2 中国

SQL>



当session 2开始查询时,oracle从表中取出这两个字符,由于客户端(nls_lang)和数据库的字符集设置一致,oracle将忽略字符的转换问题,

于是直接将数据库中存储的字符返回给客户端。对于编码为"d6,d0,b9,fa"的字符,返回给客户端,而客户端显示所用的字符集正好是zhs16gbk,在这个字符集里,########(虽然客户端变成ut8,但是转换回来还是以16gbk的,windows依然是16gbk。)

这个编码对应的是"中国"两个字符,所以就正常显示出来了。

对于字符编码“e4,b8,ad,e5,9b,bd”,返回到客户端後,因为在zhs16gbk里采用的是双字节存储字符方式,(虽然客户端变成utf8,但是转换回来还是以16gbk的,windows依然是16gbk。)

所以这6字节对应了zhs16gbk字符集的3个字符,也就是我们看到的"涓浗"。






导入导出 exp   imp 客户端对字符的影响实验

 

 


创建两个库

test1 源数据库 字符集-----------   SIMPLIFIED CHINESE_CHINA.AL32UTF8     


test2 目标数据库 字符集-----------   SIMPLIFIED CHINESE_CHINA.16gbk


创建用户jiang/oracle  建立test表




oracle@linux5:/oracle>export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK" ###########环境变量


SQL>  select col1,dump(col2,1016) from test;


      COL1

----------

DUMP(COL2,1016)

--------------------------------------------------------------------------------

         2

Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa


         1

Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd




Export: Release 11.2.0.4.0 - Production on 星期一 11月 28 17:05:50 2016


Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.



连接到: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options

已导出 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集

服务器使用 AL32UTF8 字符集 (可能的字符集转换)

. 正在导出 pre-schema 过程对象和操作

. 正在导出用户 JIANG 的外部函数库名

. 导出 PUBLIC 类型同义词

. 正在导出专用类型同义词

. 正在导出用户 JIANG 的对象类型定义

即将导出 JIANG 的对象...

. 正在导出数据库链接

. 正在导出序号

. 正在导出簇定义

. 即将导出 JIANG 的表通过常规路径...

. . 正在导出表                            TEST导出了           2 行

. 正在导出同义词

. 正在导出视图

. 正在导出存储过程

. 正在导出运算符

. 正在导出引用完整性约束条件

. 正在导出触发器

. 正在导出索引类型

. 正在导出位图, 功能性索引和可扩展索引

. 正在导出后期表活动

. 正在导出实体化视图

. 正在导出快照日志

. 正在导出作业队列

. 正在导出刷新组和子组

. 正在导出维

. 正在导出 post-schema 过程对象和操作

. 正在导出统计信息

成功终止导出, 没有出现警告。







导入到test2 ,目标数据库字符集为16gbk


oracle@linux5:/oracle>imp jiang/oracle@test2 fromuser=jiang touser=jiang file=/backup/test1.dmp log=/backup/testimp.log


Import: Release 11.2.0.4.0 - Production on 星期一 11月 28 17:28:32 2016


Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.



连接到: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options


经由常规路径由 EXPORT:V11.02.00 创建的导出文件

已经完成 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集中的导入

. . 正在导入表                          "TEST"导入了           2 行

成功终止导入, 没有出现警告。

oracle@linux5:/oracle>



SQL> select * from test;


      COL1 COL2

---------- ----------

         2 ???

         1 中国

         

 

 SQL>  select col1,dump(col2,1016) from test;


      COL1

----------

DUMP(COL2,1016)

--------------------------------------------------------------------------------

         2

Typ=1 Len=4 CharacterSet=ZHS16GBK: a3,bf,3f,3f        


         1

Typ=1 Len=4 CharacterSet=ZHS16GBK: d6,d0,b9,fa          ------------2个字节一个汉字, 16gbk编码存储 数据库做了数字编码转换工作。



SQL>         







参考   http://blog.csdn.net/wuweilong/article/details/39694531

源端数据库test1 (1)字符集为utf8 →EXP客户端(2)→IMP客户端(3)→目标数据库(4) test2字符集为16gbk,

数据在迁移过程中要经历如上的4个点,数据在流动过程中(如上的3个箭头)需要依次比较箭头两端的字符集,

如果相同则不转换,如果不同则进行转换。如果相邻的两个点之间设置的字符集均不相同,则需要转换3次。

最好的设置方式是,因为(1)(4)数据库的字符集是固定的,则设置客户端的字符集(2)(3)均与(1)相同,

这样最多只在(3)→(4)的过程中发生一次字符集的转换。但是前提是(4)的字符集必须是(1)的的字符集的超集。客户端字符集是通过环境变量NLS_LANG来设置。




写入数据----字符转换过程


数据库端通过简单的判断跟 客户端环境变量的字符集  是否一致进行字符转换   一致则不转换,直接存储到数据库中,不一致则字符转换。

客户端的字符集的作用就是告知数据库端传输的字符编码


数据库看到客户端的字符集和数据库的字符集一致,此时oracle将不会再对字符作转换,因为它认为两边的字符编码是一致的。而此时,

我们欺骗了数据库,尽管我们将客户端字符集设置为和数据库一致,但是其实我们使用的windows7是zhs16gbk字符集编码(因为此时windows使用的就是这个字符编码),

对于字符"中国",zhs16gbk字符集里对应的编码为d6,d0,b9,fa。此时,oracle不加理会的直接将这个编码保存到了数据库中。




查找数据----字符转换过程(win7  16gbk一定)


当session 1开始查询时,oracle从表中取出这两个字符,并按照字符集al32utf8和字符集zhs16gbk的编码映射表,将它的转换成zhs16gbk字符编码,对于编码“e4,b8,ad,e5,9b,bd”,

它对应的zhs16gbk的字符编码为"d6,d0,b9,fa",这个编码对应的字符为”中国“,所以我们看到了这个字符正常显示出来了,3---2


而对于字符集存储的al32utf8字符编码“d6,d0,b9,fa”,

由于我们用于显示字符的windows环境使用的是zhs16gbk字符集,而在zhs16gbk字符集里面并没有对应这个编码的字符或者属于无法显示的符号,utf8---16gbk 转换找不到

于是使用了"?"这样的字符来替换,这就是为什么我们看到session 2输入的字符变成了这样的乱码。