我用的数据库是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输入的字符变成了这样的乱码。