设为首页 加入收藏

TOP

Oracle字符集、编码(三)
2015-07-24 11:22:46 来源: 作者: 【 】 浏览:11
Tags:Oracle 字符集 编码
ue

同时在中文字符串前添加“N”前缀:

\ INSERT INTO charset_test VALUES( 2,'中',N'中');
--NVARCHAR2列中的中文不再是乱码了
SELECT * FROM charset_test WHERE id= 2;
2 ? 中

这个配置起到的作用是这样的:在INSERT语句从客户端传输到服务器端之前,Oracle SQL Developer检测(实际上是JDBC检测)语句,如果发现“N”前缀,则事先将这部分的字符串按UTF-16进行编码得到16进制串。也就是相当于执行了这个命令:

INSERT INTO charset_test VALUES(2,’中’,UNISTR('\4e2d'));

C#不需要做特殊的配置来让NVARCHAR2正常工作,只需要在执行INSERT时使用参数并选择正确的参数类型选:

\ 复制代码 cmd.CommandText = "insert into charset_test values(3,:vc,:nvc)";
OracleParameter p1 = new OracleParameter("vc", OracleDbType.Varchar2);
OracleParameter p2 = new OracleParameter("nvc", OracleDbType.NVarchar2);
p1.Value = "中";
p2.Value = "中";
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
cmd.ExecuteNonQuery(); 复制代码


4.客户端的NLS_LANG设置及编码转换

前面我说过Oracle SQL Developer忽略客户端NLS_LANG设置,那么对于其它的工具呢?(这里我们主要关注字符集及编码,不讨论NLS_LANG对日期格式、排序方式、数字显示格式等等的影响)

ü SQLPLUS,插入与查询都依赖于客户端NLS_LANG设置。通常,客户端NLS_LANG设置要与当前的OEM Codepage一致,比如US8PC437。

ü PL/SQL Developer,插入与查询都依赖于客户端NLS_LANG设置。通常,客户端NLS_LANG设置要与数据库字符集一致。

使用SQLPLUS可以清晰地看到Oracle编码转换的过程:

1) 在Oracle客户端向服务器端提交SQL语句时,Oracle客户端根据NLS_LANG和数据库字符集,对从应用程序接传送过来的字符串编码进行转换处理。如果NLS_LANG与数据库字符集相同,不作转换,否则要转换成数据库字符集并传送到服务器。服务器在接收到字符串编码之后,对于普通的CHAR或VARCHAR2类型,直接存储;对于NCHAR或NVARCHAR2类型,服务器端将其转换为国家字符集再存储。

\

2) 在查询数据时,服务器端原样返回存储在库中的数据,由客户端根据返回的元数据中的字符集信息与NLS_LANG和NLS_NCHAR的设置进行比较(如果NLS_NCHAR没有设置,则其默认值为NLS_LANG中的字符集设置),如果元数据中的字符集信息与客户端设置一致,不进行转换,否则要进行转换。国家字符集的转换根据NLS_NCHAR设置进行转换。

\

这里我也举几个使用SQLPLUS的测试例子,分别在A、B两库执行相同的语句,然后通过网络抓包查看从Oracle client传输到服务器的具体内容。

1 客户端NLS_LANG:WE8MSWIN1252

SQL命令:insert into charset_test values(1,'?',null);

网络抓包(A库,数据库字符集为WE8MSWIN1252):91

解释:由于应用程序(即SQLPLUS)使用的编码是Codepage437,所以?的编码是91。当91被传给Oracle client后,Oracle client根据NLS_LANG误判其使用的编码是Codepage1252,又由于NLS_LANG设置与数据库字符集一致,于是Oracle client不进行编码转换,91被直接传给服务器并存储,考虑到数据库字符集是Codepage1252,很显然91是错误的数据(字符[?]在Codepage1252下的编码是E6,而非91)。

这个错误导致了一个有趣的现象,那就是在同一个客户端使用SQLPLUS查询居然可以看到正确字符[?],这是由于SELECT的时候91也被直接返回,并且在Oracle client也不进行编码转换而是直接传给了应用程序,恰巧应用程序根据自己使用的编码可以正确解析91。但是换一个客户端机器,或者换一个客户端工具都可能得到不一样的查询结果。

网络抓包(B库,数据库字符集为AL32UTF8):E2 80 98

解释:由于应用程序(即SQLPLUS)使用的编码是Codepage437,所以?的编码是91。当91被传给Oracle client后,Oracle client根据NLS_LANG误判其使用的编码是Codepage1252,而91在Codepage1252中对应的是字符[‘],根据Codepage1252到数据字符集UTF8的转换,最终转换成了E2 80 98,即UTF8下的[‘]。

2客户端NLS_LANG:US7ASCII

SQL命令:insert into charset_test values(1,'?',null);

网络抓包(A库):BF

解释:由于应用程序(即SQLPLUS)使用的编码是Codepage437,所以?的编码是91。当91被传给Oracle client后,Oracle client根据NLS_LANG误判其使用的编码是ASCII,而91在ASCII中是无效编码,根据ASCII到数据字符集Codepage1252的转换,最终转换成了BF,BF是Codepage1252遇到无效编码时使用的默认替换编码。

网络抓包(B库): EF BF BD

解释:由于应用程序(即SQLPLUS)使用的编码是Codepage437,所以?的编码是91。当91被传给Oracle client后,Oracle client根据NLS_LANG误判其使用的编码是ASCII,而91在ASCII中是无效编码,根据ASCII到数据字符集UTF8的转换,最终转换成了EF BF BD,EF BF BD是UTF8遇到无效编码时使用的默认替换编码。

3客户端NLS_LANG:US8PC437

SQL命令:insert into charset_test values(1,'?',null);

网络抓包(A库):E6

解释:E6是字符[?]的正确的Codepage1252编码,此次由于应用程序(即SQLPLUS)使用的是Codepage437,Oracle client从NLS_LANG获得的编码信息也是Codepage437,于是进行了正确的编码转换。

网络抓包(B库):C3 A6

解释:C3 A6是字符[?]的正确的UTF8编码,此次由于应用程序(即SQLPLUS)使用的是Codepage437,Oracle client从NLS_LANG获得的编码信息也是Codepage437,于是进行了正确的编码转换。

我觉得,只有SQLPLUS的日子总是那么美好,一切看起来既合理又可解释。当其它工具出现之后,世界就变

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Oracle单实例情况下的librarycach.. 下一篇用Navicat连接Oracle数据库时报错..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·新书介绍《Python数 (2025-12-25 04:49:47)
·怎么利用 Python 进 (2025-12-25 04:49:45)
·金融界大佬力荐,Pyt (2025-12-25 04:49:42)
·你必须要弄懂的多线 (2025-12-25 04:22:35)
·如何在 Java 中实现 (2025-12-25 04:22:32)