AL32UTF8/UTF8(Unicode)数据库字符集含义 (文档 ID 1946289.1)

适用于:

Oracle Database Cloud Schema Service - 版本 N/A 和更高版本

Oracle Database Exadata Cloud Machine - 版本 N/A 和更高版本

Oracle Cloud Infrastructure - Database Service - 版本 N/A 和更高版本

Oracle Database Cloud Exadata Service - 版本 N/A 和更高版本

Oracle Database Backup Service - 版本 N/A 和更高版本

本文档所含信息适用于所有平台

用途

对如何处理转换到 AL32UTF8 数据库字符集以及使用 Unicode 客户端的影响提供一些实用指导。

对于实际转换到 AL32UTF8 的文章,参见 ​​Note 260192.1​​ Changing the NLS_CHARACTERSET to AL32UTF8/UTF8 (Unicode)  in 8i, 9i , 10g and 11g 或者 ​​Note 1272374.1​​ The Database Migration Assistant for Unicode (DMU) Tool

虽然本文档描述的是转换到 AL32UTF8/UTF8,但大部分情形也适用于转换到其他多字节字符集(ZHS16GBK,ZHT16MSWIN950,ZHT16HKSCS,ZHT16HKSCS31,KO16MSWIN949,JA16SJIS ...),,您可以简单的将 AL32UTF8 替换成成 xx16xxxx 目标字符集。但是在这种情况下,转换成 AL32UTF8 显然是一个更好的主意。

适用范围

本文档采用 AL32UTF8,如果没有明确注明,所有信息同样适用于 UTF8。

注意 UTF8 和 AL32UTF8 是 Oracle 特定名称而 UTF-8(带一个 -)指的是 Unicode 标准的 UTF-8 编码方案。

 

重要提示:不要在低于 10.2.0.4(包括 10.1.0.5)的所有 10g 版本上通过 Expdp/Impdp 来转换到(AL32)UTF8 或者其他多字节字符集。11.1.0.6 也同样受到影响。

这样做会招致数据损坏,除非您在 impdp 端打上了 ​​Patch 5874989​​,Expdp 不受到影响。旧的 exp/imp 工具不受到影响。该问题在 10.2.0.4 和 11.1.0.7 中被修复了。

对于 Windows 来说,该修复被包含在如下 Patch 中:

10.1.0.5.0 Patch 20 (10.1.0.5.20P) or later, see ​​Note 276548.1​

10.2.0.3.0 Patch 11 (10.2.0.3.11P) or later, see ​​Note 342443.1​

详细信息

A) 经常被问到的问题:

A.1) 我需要使用 Nchar,Nvarchar2 或者 Nclob么?

人们经常认为像 NCHAR,NVARCHAR2 或者 NCLOB(NLS_NCHAR_CHARACTERSET/国家字符集数据类型)等数据类型需要在具有 UNICODE 支持的 Oracle 中使用。

这是不正确的。


NLS_NCHAR_CHARACTERSET("国家字符集")定义了 NCHAR,NVARCHAR2 和 NCLOB 列的编码并且在 9i 及以上版本已经是 Unicode了(参见 ​​Note 276914.1​​ The National Character Set in Oracle 9i 10g and 11g)。

NLS_CHARACTERSET("字符集")定义了"普通的" CHAR,VARCHAR2,LONG 和 CLOB列编码,这些也可以被用于存放 Unicode。在这样的情况下需要 AL32UTF8 或者 UTF8 NLS_CHARACTERSET 数据库。


查看 NLS_CHARACTERSET 的值可以用如下查询:

SQL> select value from NLS_DATABASE_PARAMETERS where parameter='NLS_CHARACTERSET';


VALUE

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

AL32UTF8

当使用 AL32UTF8 作为 NLS_CHARACTERSET 时,所有"普通的" CHAR,VARCHAR2,LONG 和 CLOB 数据类型是"Unicode"的并且您可以存储世界上任何语言到 CHAR,VARCHAR2,LONG 和 CLOB 数据类型中。

可能将 AL16UTF16 作为 NLS_CHARACTERSET,AL16UTF16 只能被用于 NLS_NCHAR_CHARACTERSET,参见 ​​Note:276914.1​​ The National Character Set in Oracle 9i 10g and 11g。

人们经常采取简单的使用 NCHAR,NVARCHAR2 或者 NCLOB 来将一个应用"Unicode"化并认为这样做比改变 NLS_CHARACTERSET 要减少很多工作,然而 N- 类型在(其他厂商)编程语言和应用层通常只有很少的支持。使用 N- 类型需要客户端应用/程序的明确支持。

这就是 Oracle 通常推荐不要使用 N-types 而是使用 AL32UTF8 (或者 UTF8) 的 NLS_CHARACTERSET 结合 CHAR,VARCHAR2,LONG 和 CLOB 数据类型的原因。

A.2) 我的操作系统需要支持 Unicode 或者我需要在 OS 里安装字符集么?

对于一个 Unicode 数据库,Oracle 不需要运行这个数据库的 OS 具有"Unicode 支持"因为 Oracle AL32UTF8 的实现不依赖于 OS。

例如,完全可以在没有安装任何 UTF-8 语言环境的 Unix 的系统上运行/使用 AL32UTF8 数据库。不过建议您配置操作系统使用 UTF-8,这样您可以将此环境作为 UTF-8*客户端*使用。


同样也没有必要为 Oracle database/client 软件本身"安装Unicode"或者类似的东西,在 Oracle 某版本中所有已知的字符集,包括 Unicode 字符集,是被始终安装的。您根本无法选择不安装它们。

请注意这里是关于使用 Oracle 定义的(使用 AL32UTF8 作为 NLS_CHARACTERSET 或 NLS_LANG),如果你想在 Unix 系统上使用例如 sqlplus 作为一个 UTF-8 客户端,那么你可能需要 OS 操作系统的 UTF-8 支持来使该应用正常工作。

A.3) Oracle RDBMS有哪些 Unicode 字符集和 Unicode 版本?

关于 Oracle 中的 Unicode 字符集信息和 Unicode 支持的版本,请参考: ​​Note 260893.1​​ Unicode character sets in the Oracle database。

关于 UTF8  与 AL32UTF8 的不同,请参考本文档的B.5)。

注意 UTF8 和 AL32UTF8 是 Oracle 特定名称而 UTF-8(带一个 -)指的是 Unicode 标准的 UTF-8 编码方案。

如果您的安装是完整安装,在 9i 或者更高版本(所有 clients 和所有 servers),Oracle 强烈建议使用 AL32UTF8 作为 NLS_CHARACTERSET 除非有应用层/供应商所带来的限制,例如低于版本 12 的 Oracle Applications。

如果有旧的 8i 或者更低的客户端使用了 UTF8 而不是 AL32UTF8 作为 NLS_CHARACTERSET,参见 ​​Note 237593.1​​ Problems connecting to AL32UTF8 databases from older versions (8i and lower)。

A.4) Oracle AL32UTF8/UTF8 database 支持/定义了/识别我们插入的语言或者字符么?

简短的回答,当使用 AL32UTF8 时,答案是肯定的。

对于一些语言,如 HKCSC2004,UTF8 可能不是很理想(参见 B.5)。

如果您想 100% 确保,请检查 Oracle release 的 Unicode 版本并查看 ​​http://www.unicode.org​​ 或者 ​​Note 1051824.6​​ What languages are supported in an Unicode (UTF8/AL32UTF8) database?

相比 AL32UTF8 数据库来说,客户端环境能否支持该语言才是更大的问题。

请注意在一个 Unicode 数据库中,对存放某一种语言的支持与如下的因素没有任何关系:

  • 与安装时选择的"产品语言"没有任何关系。已安装的"产品语言"指的是数据库消息的翻译。参见 ​​note 985974.1​​ Changing the Language of RDBMS (Error) Messages。即使您的"产品语言"只安装了英语,仍然可以在 AL32UTF8 数据库中存放任何语言
  • 与 NLS_SESSION_PARAMETERS,NLS_INSTANCE_PARAMETERS,NLS_DATABASE_PARAMETERS 等视图中看到的 NLS_LANGUAGE 和 NLS_TERRITORY 的值没有任何关系。参见 ​​NOTE 241047.1​​ - The Priority of NLS Parameters Explained (Where To Define NLS Parameters)。即使数据库使用的 NLS_LANGUAGE 和 NLS_TERRITORY 被设置为 AMERICAN AMERICA,您仍然可以在 AL32UTF8 数据库中存放任何语言

A.5) 怎样将一个非 Unicode NLS_CHARACTERSET 转换成 Unicode(AL32UTF8 或者 UTF8)?

对于转换到 Unicode(AL32UTF8 或者 UTF8 NLS_CHARACTERSET),我们有一篇 ​​Note 260192.1​​ Changing the NLS_CHARACTERSET to AL32UTF8/UTF8 (Unicode) in 8i, 9i , 10g and 11g

通过一步一步的方式描述了如何通过 csscan 连同:

* full exp/imp 的方式转换到一个新的 AL32UTF8 数据库

* 或者部分 exp/imp 结合 csscan/csalter 来转换现有数据库。

请注意当转换整个数据库到 AL32UTF8 时,甚至使用 full exp/imp 到一个新的 AL32UTF8 数据库时,强烈推荐按照以上文档操作。

如果您只需要在现有数据库和目标数据库之间 exporting/importing 某个用户或者表,并且目标数据库是一个 UTF8 或者 AL32UTF8 数据库,请参考:

​Note 1297961.1​​ ORA-01401 / ORA-12899 While Importing Data In An AL32UTF8 / UTF8 (Unicode) Or Other Multibyte NLS_CHARACTERSET Database。

对于迁移数据库到 AL32UTF8(和已弃用的 UTF8),有一个新工具叫 Database Migration Assistant for Unicode(DMU),DMU 是唯一的下一代迁移工具,它为数据库从遗留编码迁移到 Unicode 提供了一个端对端的解决方案。DMU 的直观用户界面大大简化了迁移过程,并通过在整个迁移过程中指导 DBA 以及很多自动化的迁移任务减轻了对字符集迁移专业知识的需要。

DMU 工具在 Oracle11.2.0.3 和更高版本上被支持,同时支持选择旧版本和平台组合。

更多详情请参考 ​​Note 1272374.1​​ The Database Migration Assistant for Unicode (DMU) Tool 和 ​​the DMU pages on OTN​​。

从 Oracle12C 起,DMU 是迁移到 Unicode 唯一可用的工具。


在 csscan/csalter/部分 exp/imp 或者 DMU 或者 full exp/imp 到一个新的 AL32UTF8 数据库之间没有孰优孰劣。

通常的:

  •   DMU 工具通常被认为是更友好的,可以不通过 exp/imp 来完成转换并且在数据需要纠正和停机时间有限的情况下更为容易。
  •   当需要迁移到新的服务器/OS 或者停机时间不是问题的时候,full exp/imp 到一个新的 AL32UTF8 数据库通常被采用。
  •   当停机时间有限时,csscan/ csalter (10g/11g) 或者 Alter Database Character Set (8i/9i) / 部分 exp/imp 通常会被喜欢脚本的人优先采用。

但是对于所有的这 3 种转换到 AL32UTF8 的方式来说,最终结果是相同的。


对于 Oracle Applications 数据库请参考 ​​Note 124721.1​​ Migrating an Applications Installation to a New Character Set。

这是 Oracle applications 支持的唯一途径,如果您有疑惑请开启一个 Oracle Applications 的 SR 来获得帮助。


对于 Oracle PeopleSoft 数据库请参考 ​​note 703689.1​​ Converting PeopleSoft Systems to Unicode Databases。

如果您有疑惑请开启一个 Oracle PeopleSoft 的 SR 来获得帮助。

A.6) 我想同时升级 Oracle 版本和转换数据库字符集到 AL32UTF8,那么我应当在升级前还是升级后转换?

如果您当前的 Oracle 版本是 8.1.7 或更低那么最好先升级到更高版本,主要是因为:a)升级后您可以使用 AL32UTF8 了(这在 8i 中是不能的)和 b )CSSCAN 在 817 有少量问题可能会引起混乱。


如果您当前的 Oracle 版本是 9i 或更高,那么二者(升级之前或之后转换)都是不错的选择,它只是取决于您的喜好或需要的应用修改。不过我们建议不要同时升级数据库和改变字符集,仅仅是为了在发生问题时,能辨别问题是来源于升级数据库还是改变字符集。因为升级数据库或改变字符集需要适当的测试和 Q&A,在所有改变被充分测试之前,不要在生产系统上执行。

不要在低版本上运行 csscan,执行数据库升级后再运行 csalter。升级数据库后您需要再次运行 csminst.sql 和 csscan。

在迁移数据库到 AL32UTF8 之前先升级数据库到 11.2.0.3(或更高)可能是一个好主意因为数据库升级后 Database Migration Assistant for Unicode(DMU)就可以替代 Csscan/csalter 来转换数据库到 AL32UTF8 而不需要 export /import(一部分)数据集。

DMU 工具在 Oracle11.2.0.3 和更高版本上被支持,同时支持选择旧版本和平台组合。

更多详情请参考 ​​Note 1272374.1​​ The Database Migration Assistant for Unicode (DMU) Tool 和 ​​the DMU pages on OTN​​。

从 Oracle12C 起,DMU 是迁移到 Unicode 唯一可用的工具。

A.7) 如何确认我的运行在 AL32UTF8 或者 UTF8 数据库上的应用是工作正常的?

为了使用 Unicode(AL32UTF8/ UTF8)数据库,你需要确保你的应用程序支持使用 Unicode 数据库。这不是 Oracle 数据库支持可以"检查"或"确认"的事情,没有"通用"的选择或配置可供您检查。

请参考应用程序文档或咨询应用程序供应商/支持团队来确认您的应用程序是否认证使用 AL32UTF8 或 UTF8 作为 NLS_CHARACTERSET。

B) 服务器端影响

B.1) 以 AL32UTF8 存储数据(insert 时的 ORA-01401/ORA-12899 错误)

AL32UTF8 是一个变宽度的字符集,表示为 1 个字符的代码可以是 1,2,3 或 4 个字节长。这与 WE8ISO8859P1 或 WE8MSWIN1252 字符集中 1 字符始终是 1 个字节有很大的区别。


US7ASCII 字符集(A-Z,a-Z,0-1 和 ./?,*# 等等)在 AL32UTF8 中总是 1 字节,因此,对于大多数西欧语言的影响是相当有限的,因为在大多数西方语言中,在 8 bit 字符集中,整个数据集只有"特殊"字符会使用更多的字节,(比起 A-Z 来说)他们不经常使用。

当转换西里尔文或阿拉伯文系统到 AL32UTF8 时,由于所有的西里尔或阿拉伯数据将会花费更多的字节来存储,对整个数据集的影响将更大。


请注意,任何 US7ASCII(A-Z,a-Z,0-1 和 ./?,*# ..)以外的字符将采取更多的“字节”来存储相同的字符,所以在列级可能产生很大的影响。


列需要足够大来存放附加的字节,在默认情况下,列的大小是以字节定义而不是字符。

默认情况下,"create table (<colname> VARCHAR2 (300));" 指该列能存放300字节

这意味着您能存放这样的300字节:比如 300个 1字节的字符例如"a" ,或者 100个 3字节的字符例如€。

如果您尝试存放 101个 3字节的字符您将看到 ORA-01401/ORA-12899 因为这 101个字符的字节长度超过了 300。


从 9i 开始,以您想存放的字符数来定义列的长度而不管字符集是可能的。

关于这是如何工作的,有哪些限制和当前已知的问题,参见 ​​Note 144808.1​​ Examples and limits of BYTE and CHAR semantics usage (NLS_LENGTH_SEMANTICS)。


更多关于 AL32UTF8 编码是如何工作的,参见 ​​Note 69518.1​​ Storing and Checking Character Codepoints in a UTF8/AL32UTF8 (Unicode) database。

注意: UTF8 可以是 1,2,3 或者 6字节/字符,所有 4字节的 AL32UTF8 字符在 UTF8 中将会被存储成 2个 3字节。

那些在 UTF8 中占 6字节、在 AL32UTF8 中占 4字节的"real life"字符是有限的并且只会在使用一些中文字符时存在,参见 ​​Note 69518.1​​ 和 ​​note 787371.1​​。

B.2) 如果使用 AL32UTF8 我的数据库会增长多少?

最大的扩展将会发生在 CLOB 类型上(或者 XMLtype – 该类型后台使用 CLOB),如果源数据库是 8 bit 字符集(WE8ISO8859P1,WE8MSWIN1252 等),那么转换后的 Clob 列会在磁盘大小上变成原来的两倍。

参见 ​​Note 257772.1​​ CLOBs and NCLOBs character set storage in Oracle Release 8i, 9i, 10g and 11g。


关于扩展评估,请参照下面的条目:

  • 如果使用 csscan,Csscan.txt 输出文件会在 Expansion 标题下列出扩展评估,参见 ​​note 444701.1​​ Csscan output explained。
  • 如果使用 DMU 工具,那么在 ​​Database Properties: Scanning tab​​ 栏目里点击 Estimate Tablespace Extension 来检查扩展评估。

我们建议在转换到 AL32UTF8 时总是使用 Csscan 或者 DMU 工具;并且在使用 csscan 或者 DMU 工具扫描源库前不要导出/导入数据,参见A.5)。


对于西欧数据库来说,非CLOB 类型的扩展通常是几个百分比,因为大多数字符实际上是 US7ASCII 字符。

对于存放其他语言群体,像阿拉伯语,西里尔语等的数据库,将从整体上有比西欧数据库更高的数据扩展量。

B.3) 字符的码点(Codepoints)可能在 AL32UTF8 中发生改变

有一种普遍的误解,认为一个字符总是相同的代码,例如英镑符号£通常被称为"code 163"字符。

这是不正确的,只有在某个特定的字符集中,才能说一个字符是一个特定的代码(!)如果你不知道使用的是什么字符集,那么该代码本身没有任何含义。


这种差别看起来很小,实际上不是这样。


例如英镑符号£在 WE8ISO8859P1 和 WE8MSWIN1252 字符集中的确是"code 163"(十六进制A3),但在 AL32UTF8 中英镑符号£是代码 49827(十六进制C2 A3)。

在 AL32UTF8 数据库中,使用 chr(163) 得到的 163 代码是非法字符,因为 163 代码在 UTF8 根本不存在,英镑符号£在 UTF8/AL32UTF8 系统中是 chr(49827)。


因此,当使用类似 CHR() 的函数时要小心,一个字符的代码依赖于数据库的字符集!


使用 Unistr('\codepoint>') 要远远好于使用 CHR(). Unistr() (9i 的一个新功能)在每一个可识别该字符的字符集中均能正常工作。例如当字符集从 WE8MSWIN1252 变到 AL32UTF8 时,不需要改变欧元符号的 Unistr 的值。


关于如何在 AL32UTF8 字符集中检查/查找某一个字符和使用Unistr 的更多信息,参见 ​​Note 69518.1​​ Storing and Checking Character Codepoints in an UTF8/AL32UTF8 (Unicode) database。


只有 US7ASCII(A-Z,a-z,0-9) 字符在 AL32UTF8 中和 US7ASCII,WE8ISO8859P1,AR8MSWIN1256 等中具有相同的码点(codepoints)。这意味着使用 chr() 应当避免高于 128 的值。

B.4) SP2-0784, ORA-29275 和 ORA-600 [kole_t2u], [34] 错误/转换时丢失字符的含义

为什么看到这些错误?

如果您收到类似 "SP2-0784: Invalid or incomplete character beginning 0xC4 returned" 或者 "ORA-29275: partial multibyte character" 或者 "ORA-600 [kole_t2u], [34]" 的错误,那么这意味着您正在使用字符类型来存储数据并且*数据*没有使用 AL32UTF8 编码。

当使用 AL32UTF8 数据库时,有时会遇到不常见的"ORA-00911: invalid character" 或者 "ORA-24812: character set conversion to or from UCS2 failed" 大多数情况下与上面的错误情形相同。

请注意,这些错误不能被"关闭",也不应该被"关闭",这些错误表明您的设置有严重的问题,您需要加以解决。

当使用 Clob 数据时,您不会遇到 ORA-29275,但是会遇到 ORA-600 [kole_t2u], [34]. 参见 ​​Note 734474.1​​ ORA-600 [kole_t2u], [34] - description, bugs, and reasons。

SP2-0784 是一个纯客户端错误/由 sqlplus 返回的警告,它与 ORA-29275 含义相同。


类似 CHAR,VARCHAR2,LONG 和 CLOB 的字符数据类型,我们所期望的是,数据由 NLS_CHARACTERSET 定义编码。

在CHAR,VARCHAR2,LONG 和 CLOB 数据类型中存放非 NLS_CHARACTERSET 编码格式是不支持的。

与 NLS_CHARACTERSET 编码格式不同的数据应当考虑使用 BINARY 数据类型例如 RAW 或者 BLOB 来存储和处理。


同理,对使用"convert"函数来说也是这样的,从 NLS_CHARACTERSET 到其他字符集的转换也应当考虑使用 binary 数据,因为结果不在 NLS_CHARACTERSET 中。当使用 convert 函数时,您可能看到一些错误比如 ORA-12703: this character set conversion is not supported。

请注意这是期望的行为并且 ​​convert 函数不应当在普通应用逻辑中被使用。​​如果数据需要以非 UTF8 编码格式存储,那么应当使用 UTL_RAW.CAST_TO_RAW 和 UTL_RAW.CONVERT 并且结果应当以 RAW 存放 或者 使用DBMS_LOB.CONVERTTOBLOB来定义最终的字符集( csid -> NLS_CHARSET_ID('<encoding_name>')  ), 结果以BLOB处理/存放。


字符类型被设计成使用 NLS_CHARACTERSET 编码格式来存放,这是唯一的解决方案。

如果您想使用 UTL_FILE 将数据写入到一个 AL32UTF8 之外的另一个字符集,参见 B.10)。


校验是如何工作的?


UTF8/AL32UTF8 的校验方式(参见 ​​Note 69518.1​​ Storing and Checking Character Codepoints in an UTF8/AL32UTF8 (Unicode) database),该文档列举了一些简单的方法来校验一(大部分)数据流的有效性。例如一个在 00 到 79 (16 进制表示法)之间的代码后面只能跟着另一个(00 到79)的代码或者(C2 到 EF)的代码。如果跟在一个(00 到 79)之后的代码是 C1,那么出错了。

另外一个例子,比如您不能在一个(C2 到 EF)的代码后面跟一个(00 到 79)的代码,或者您有一个(00 到 79)的代码后面跟着一个(C2 到 EF)代码,那么您知道还需要第3个(80 到 BF)的代码,等等...

如果这些"规则"中的一个被违反,那么 Oracle 会给出一个 ORA-29275 错误。


但是相同的数据在旧版本中没有报错...


ORA-29275: partial multibyte character 错误是 Oracle 在对字符串在很低的级别上做"理智"校验来判定编码序列是否是一个合法的 AL32UTF8 而产生的结果,该校验不能捕获 100% 的情况,因此一些不合法的编码序列可能不会被发现,这完全取决于数据集,这就是错误经常看起来是"随机的" 而且可能对某些行不出现的原因。

他们在每个版本都被增强,比如 11g 的校验远比 9i 要好/更严格。

这么做是为了避免函数返回错误结果集和减少因注入问题而导致安全问题的风险。


我如何能找到所有可能引发此问题的数据?


要找到所有可能引发此问题的*已存储的*数据


1) Unicode 的客户端比如 Sqldeveloper 可以被用来检查数据并更新它们,参见本文c)章节。


2) 如果想在整个数据库中找出非 AL32UTF8 编码:


2a) 对于 11.2.0.3 和更高的版本,可以用 DMU 工具,参考


​Note 1272374.1​​ The Database Migration Assistant for Unicode (DMU) Tool / 9) What is the "Install in Validation mode" option when installing the DMU repository when connecting to an UTF8 or AL32UTF8 NLS_CHARACTERSET database with the DMU tool?


根据以上文档来安装 DMU 工具,然后连接并选择以"validation"模式安装资料库,不要选择 "migration" 模式。


2b) 对于 11G 或者较低版版,可以用 csscan。


首先安装 csscan schema:

​Note 458122.1​​ Installing and configuring CSSCAN in 8i and 9i

​Note 745809.1​​ Installing and configuring CSSCAN in 10g and 11g


然后以如下语法运行 csscan:

$ export ORACLE_SID=<sid>

$ csscan \"/ as sysdba\" FULL=Y FROMCHAR=<current NLS_CHARACTERSET> TOCHAR=<current NLS_CHARACTERSET> LOG=dbcheck CAPTURE=N ARRAY=1000000 PROCESS=2

* 永远以'sysdba'用户连接运行 csscan,不要用"system" 或者 "csmig" 用户。

* <current NLS_CHARACTERSET> 可以在 NLS_DATABASE_PARAMETERS 视图中查询到。

select value from NLS_DATABASE_PARAMETERS where parameter='NLS_CHARACTERSET';

* TOCHAR=<current NLS_CHARACTERSET> 不是打印错误,该设计是为了检查编码没有被定义在 NLS_CHARACTERSET 中的当前字符集。

* 尽可能的通过在本地使用ORACLE_SID来运行Csscan

* 以上的语法会执行一个全库扫描,如果您想只扫描一个特定的表或者某一个用户,请参考 ​​Note 444701.1​​ Csscan output explained 的第E)点: Do I need to always run a full database scan?"。

如果需要了解 csscan 输出的概览和含义,请参见 ​​Note 444701.1​​ Csscan output explained。

文件dbcheck.err中会纪录"Lossy"数据的rowid



被 csscan 报告成"Lossy" 或者被 DMU 报告成 "Invalid Binary Representation"的数据可能导致 ORA-29275 和/或 ORA-600 [kole_t2u], [34] 。


导致 ORA-29275 的数据(或者被 csscan 报告成"Lossy" 或者被 DMU 报告成 "Invalid Binary Representation"的数据)是加密的或者是 binary 数据。


如果被 csscan 报告成"Lossy" 或者被 DMU 报告成 "Invalid Binary Representation"的 CHAR,VARCHAR2 或者 CLOB 列包含"encrypted"的数据 (密码 ) 或者 "binary" 数据 (images, .doc , pdf 等文件),参见 ​​Note 1297507.1​​ Problems with (Importing) Encrypted Data After Character Set Change Using Other NLS_CHARACTERSET Database or Upgrading the (client) Oracle Version。

导致 ORA-29275 的数据(或者被 csscan 报告成"Lossy" 或者被 DMU 报告成 "Invalid Binary Representation"的数据)是实际的"text"数据。


如果数据不是"encrypted"或者"binary",而是"normal text",那么表明数据以非 UTF8 编码存放(由于数据加载/插入时错误的 client 配置)。

这样的数据需要被修正,修正方式可以采用正确的重新加载,手工 update,或者如果知道实际的编码方式,可以使用 CONVERT 或者 UTL_RAW.CAST_TO_RAW ,UTL_RAW.CONVERT 和 UTL_RAW.CAST_TO_VARCHAR2 然后 update 该行。

​这里​​有一个示例脚本,它是关于使用 csscan 结果(不是DMU的结果)和假定发现"lossy"的数据是因缺少正确转换而存放在 UTF8 或者 AL32UTF8 数据库里的 WE8MSWIN1252 编码数据。这是"原样"并且默认情况下不会更新任何数据。如果使用DMU的"validation"模式,您可以通过DMU的借口来更新数据,参照 ​​DMU文档​​ .

关于DMU的"validation"模式(使用DMU1.2, 这样与当前的DMU版本相比可以看到更多的不同)我们有一个webcast, , 打开 ​​note 1456176.1​​ Oracle Database Advisor Webcast Schedule and Archive recordings , 点击 "Archived 2013" 列,选择"Introduction to Character Set correction with the DMU tool".

修正所有的数据是不太可能的,并且如果数据的含义是正确的,不可能通过脚本方式来校验。


可以采取什么措施来避免这个问题?


检查和更正插入数据的客户端,参见本文档的第C)点。


一种非常常见的 UTF8/AL32UTF8 数据库中的非 UTF8 编码来源是数据被 sqlldr 加载但是加载时使用了错误的 CHARACTERSET 申明。

 参考 ​​Note 227330.1​​ Character Sets & Conversion - Frequently Asked Questions / 第18点:What is the best way to load non-US7ASCII characters using SQL*Loader or External Tables?


常见可能引发 ORA-29275 或者 ORA-600 [KOLE_T2U] 的已知 Oracle Bug:

​Bug 4562807​​ ORA-600 [KOLE_T2U]  - 只与 oracle text 有关,发生在错误的输入数据上

在 10.2.0.4 patchset  以及 11.1 和更高基础版本中修复。

​Bug 6268409​​ ORA-29275 ERROR WHEN QUERYING THE SQL_REDO/UNDO COLUNMS IN V$LOGMNR_CONTENTS

在 10.2.0.5,11.1.0.7 及以上修复。

​Bug 5915741​​ ORA-29275 selecting from V$SESSION with Multibyte DB

在 10.2.0.5,11.1.0.6 及以上修复。

​Bug 10334711​​ ORA-600 [KOLE_T2U] when using EXTENDED feature of AUDIT_TRAIL

在 11.2.0.3 patchset 以及 12.1 基础版本中修复。

B.5) 我是否需要把 UTF8 字符集转换成 AL32UTF8? 怎样从 UTF8 NLS_CHARACTERSET 转换到  AL32UTF8 NLS_CHARACTERSET?

在 8.1.7 及以上,Oracle UTF8 是 Unicode 版本 3.0。

在每一个主要的 Oracle 版本中,AL32UTF8 被更新到较新的 Unicode 版本,在 Oracle RDBMS 12.1 中他被更新成 Unicode 6.1。也可以参考 ​​Note 260893.1​​ Unicode character sets in the Oracle database。

注意 UTF8 和 AL32UTF8 是 Oracle 特定名称而 UTF-8(带一个 -)指的是 Unicode 标准的 UTF-8 编码方案。


除了不同 Unicode 版本之间的差异,UTF8 和 AL32UTF8 之间的"大不同"是 AL32UTF8 有对"补充字符"("Supplementary characters")的内嵌支持,所谓"补充字符"是指通过"代理对"("Surrogate Pairs")编码的字符(也被错误的称为 ​​"Surrogate characters​​")。

Oracle UTF8(Unicode 3.0)以 2个字符来存储补充字符,共 6个字节,为补充字符通过"modified UTF-8"来替带"standard UTF-8"(在 Oracle 9.2 及以上通过 AL32UTF8 实现)的 4字节。

"modified UTF-8" 也被称为 ​​CESU-8​​。


这实际上意味着 99% 的数据在 UTF8 和 AL32UTF8 的*存储*上是相同的,只有补充字符在 UTF8 和 AL32UTF8 的实际存储字节/编码上有差异。

因此,从 UTF8 到 AL32UTF8 只有补充字符是"可转换的" (当使用 csscan)或者"需要被转换的"(当使用 DMU)。

所有其他数据将保持不变并且不需要任何操作。


只有少数情况下补充字符会在客户端被用到。

在现实中("real life")有使用 HKSCS 2001/2004(香港扩展)字符集的 Windows 系统,请参考 ​​Note 787371.1​​ Oracle Database Server support for HKCSC 1999, 2001 and 2004 character sets。

当从 UTF8 转换到 AL32UTF8 时您需要检查您的应用是否工作正常,如果您的应用使用了 HKSCS 字符非私人使用区编码(non-PUA)映射。比如客户端使用了 ZHT16HKSCS 那么将您的客户端改为使用 ZHT16HKSCS31(或者更好的 AL32UTF8)。


所以”我是否需要把 UTF8 字符集转换成 AL32UTF8?”的答案是:看情况而定。

AL32UTF8 更好,所以推荐使用它。转换成 AL32UTF8 额外的好处是,如果没有用到 HKSCS,那么转换会更简单(这说明没有数据需要真正的转换-更少的停机时间);并且数据库为之后使用 HKSCS 做好了准备。


将 UTF8 转换成 AL32UTF8,可以使用 ​​Note 260192.1​​ Changing the NLS_CHARACTERSET to AL32UTF8 / UTF8 (Unicode) in 8i, 9i , 10g and 11g,该文档通过一步一步的方式描述了如何通过 csscan 连同如下方式来转换数据库:

* 或者通过 full exp/imp 的方式转换到一个新的 AL32UTF8 数据库

* 或者部分 exp/imp 结合 csscan / csalter 来转换现有数据库


虽然有一些版本限制,但是我们还有 DMU 图形化工具可用于转换到 AL32UTF8,参见 ​​Note 1272374.1​​ The Database Migration Assistant for Unicode (DMU) Tool。

在 full exp/imp 到一个新 AL32UTF8 数据库或者 csscan/csalter/部分 exp/imp 或者 DMU 之间没有孰优孰劣。

通常的:

* DMU 工具通常被认为是更友好的,可以不通过 exp/imp 来完成转换并且在数据需要纠正的情况下更适用。

* 当需要迁移到新的服务器 /OS 的时候,full exp/imp 到一个新的 AL32UTF8 数据库通常被采用。

* csscan/csalter/ 部分 exp/imp 通常会被喜欢脚本的人优先采用。

对于 Oracle Applications 数据库请参考 ​​Note 124721.1​​ Migrating an Applications Installation to a New Character Set。

这是 Oracle applications 支持的唯一途径,如果您有疑惑请开启一个 Oracle Applications 的 SR 来获得帮助。

B.6) 导入数据到 AL32UTF8 数据库(或者使用 dblink 来转数据)发生的 ORA-01401 / ORA-12899 错误

如果导入遇到类似如下的错误:

IMP-00019: row rejected due to ORACLE error 1401

IMP-00003: ORACLE error 1401 encountered

ORA-01401: inserted value too large for column

或者在 10g 之后:

ORA-02374: conversion error loading table "TEST"."NTEST"

ORA-12899: value too large for column COMMENT (actual: 6028, maximum: 4000)

或者通过 dblink 从一个非 AL32UTF8 数据库来 move 数据时发生的 ORA-01401/ORA-12899,表明列无法容纳数据的"字节增长"。


如果您

  • 在现有的数据库之间 exporting/importing 特定用户或表,并且有一个数据库是 UTF8 或者 AL32UTF8 数据库
  • 通过 dblink 从非 AL32UTF8 数据库到 AL32UTF8 数据库 move 数据

请参考: ​​Note 1297961.1​​ ORA-01401 / ORA-12899 While Importing Data In An AL32UTF8 / UTF8 (Unicode) Or Other Multibyte NLS_CHARACTERSET Database。


如果您想使用 export/import 转换整个数据库到 AL32UTF8,请参考: ​​Note 260192.1​​ Changing the NLS_CHARACTERSET to AL32UTF8/UTF8 (Unicode) in 8i, 9i , 10g and 11g。

B.7) 对象或者用户名使用了非 US7ASCII  字符

我们强烈建议永远不要使用非 US7ASCII 名称作为数据库名或者 database link 名。参见文档 ​​Restrictions on Character Sets Used to Express Names​​ 列出的限制。这意味着对于任何"无""可变宽度"列的名称,您在 AL32UTF8 数据库中只能使用US7ASCII 字符(a-z,A-Z, 1-0)。对这样的名称使用非 US7ASCII 名是不支持的。这些名称必须在转换到 AL32UTF8 之前更正。一般来说尽可能避免使用非 US7ASCII 字符作为数据库对象名是一个非常不错的主意。

(参见"Database SQL Reference" 中的"Schema Object Naming Rules " 章节)


数据库对象名称可以是 1 到 30 个字节长,除了如下列外:

  •  数据库名被限制在 8 个字节。
  •  database links 可长达 128 字节。

​http://docs.oracle.com/​​​​docs/cd/B19306_01/server.102/b14200/sql_elements008.htm#i27570​


如下的 select 语句

SQL> select OWNER, OBJECT_NAME from dba_objects where OBJECT_NAME <> convert(OBJECT_NAME,'US7ASCII');

将返回所有使用非 US7ASCII 名称的对象。

如下的 select 语句

SQL> select OWNER, TABLE_NAME , COLUMN_NAME from dba_tab_columns where COLUMN_NAME <> convert(COLUMN_NAME,'US7ASCII');

将返回所有含有非 US7ASCII 列名的表。


如下的 select 语句

SELECT OWNER, OBJECT_NAME FROM dba_objects WHERE OBJECT_ID IN (SELECT UNIQUE obj# FROM sys.com$ WHERE COMMENT$ <> convert(COMMENT$,'US7ASCII')) ORDER BY owner ;

将返回所有使用非 US7ASCII 作为注释的对象。


如果在 AL32UTF8 中有非 US7ASCII 名称的列名、用户对象或者注释超过了 30字节,那么只能重命名受影响的对象或者用户使之最大只占用 30字节的名称,除此之外没有其他办法。

用户名最大能到 30字节

​http://docs.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_8003.htm#SQLRF01503​


并且"该名称只能包含您数据库字符集中的字符并且必须遵循"Schema Object Naming Rules"章节中的描述的规则,Oracle 推荐用户名至少包含一个单字节字符,无论数据库字符集是否包含多字节字符"。


如下的 select 语句

SQL> select username from dba_users where username <> convert(username,'US7ASCII');

将返回所有的非 US7ASCII  名称的用户。

B.8) 在 Oracle 10g 及以下,用户的密码只能包含单字节数据

​http://docs.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_8003.htm#SQLRF01503​


密码只能包含数据库字符集中的单字节字符,无论字符集是否包含多字节字符。这意味着在 AL32UTF8 数据库中,用户密码只能是 US7ASCII 字符因为它们是 AL32UTF8 中唯一的单字节字符。

这可能会引发一个问题,例如当您从 CL8MSWIN1251 数据库做迁移时,用户可以在它们的密码中使用西里尔字母,在 CL8MSWIN1251 中西里尔字母是一个单独的字节,而在 AL32UTF8 它们不是。

密码是以 hash 方式存储的,意味着它不会出现在 Csscan 的结果中。您需要为客户端重设密码为 US7ASCII 字符串。

以上限制在 11g 中被解除了,多字节字符可用于密码串. 请注意在使用 11g 新 hash 系统之前他们需要被更改。请参考 ​​Note 429465.1​​ 11g R1 New Feature Case Sensitive Passwords and Strong User Authentication。

B.9) 当使用 DBMS_LOB.LOADFROMFILE 时

当使用 DBMS_LOB.LOADFROMFILE 时请参考 ​​Note 267356.1​​ Character set conversion when using DBMS_LOB。

B.10) 当使用 UTL_FILE 时

当使用 UTL_FILE 时请参考 ​​Note 227531.1​​ Character set conversion when using UTL_FILE。

B.11) 当使用 sqlldr 或者外部表时

当使用 sqlldr 或者外部表时请确保在(sqlldr 或者外部表的)控制文件中指定正确的字符集。数据库字符集与文件的编码方式没有直接的关系,换句话说,不要因为数据库使用了 AL32UTF8 字符集就认为使用 AL32UTF8 作为 NLS_LANG 的值或者作为控制文件中的字符集值永远是对的。您需要指定的是 sqlldr 加载的文件的编码方式


请参考 ​​Note 227330.1​​ Character Sets & Conversion - Frequently Asked Questions

中的第18条: What is the best way to load non-US7ASCII characters using SQL*Loader or External Tables?

B.12) 确保您没有在字符数据类型(CHAR,VARCHAR2,LONG,CLOB)中存放"binary" (比如 pdf,doc,docx,jpeg,png 等文件)或者加密数据(比如密码)

如果 binary 数据(比如 PDF,doc,docx,jpeg,png 等文件)或者加密数据(比如哈希/加密过的密码)作为 CHAR,VARCHAR2,LONG 或者 CLOB 数据类型被存储/处理时,尤其是在使用 AL32UTF8 数据库时(即使没有使用 exp/imp),丢失数据是正常的,或者会出现一些比如 ORA-29275 或者 ORA-600 [kole_t2u], [34] 的错误。


对于存放 binary 数据(比如 PDF,doc,docx,jpeg,png 等文件)或者加密数据(比如哈希/加密过的密码),唯一支持的数据类型是 LONG RAW 或者 BLOB。

如果您想在 CHAR,VARCHAR2,LONG 或者 CLOB 数据类型中存放 binary 数据(比如 PDF,doc,docx,jpeg,png 等文件)或者加密数据(比如哈希/加密过的密码),那么它们必须在应用层被转化成"字符集安全"的形式,比如 base64 编码。


​Note 1297507.1​​ Problems with (Importing) Encrypted Data After Character Set Change Using Other NLS_CHARACTERSET Database or Upgrading the (client) Oracle Version

​Note 1307346.1​​ DBMS_LOB Loading and Extracting Binary File To Oracle Database

B.13) 字符串函数是基于字符(characters)的而不是字节(byte)(比如 length,like,substr ...)

类似 Length 和 Substr 的函数是以字符(characters)来计算的,而不是字节(byte)。因此在 AL32UTF8 数据库中 Length 或者 substr 的结果会不同于这个字符串(string)使用的字节数(byte)。

类似 Length 和 Substr 的函数经常被用来准备或限制字符串(string),这意味着应用逻辑需要检查。

当然也有一些例外,比如 lengthB,substrB 和 instrB 显式地通过字节(byte)来处理,一个字符串(string)的的字节(byte)长度在 AL32UTF8 环境中是无法预知的,因此基于字节(byte)的长度操作应当被避免。

注意: substrB 在 UTF8 环境可能产生与预期不同的结果:

-- 欧元符号 € (U+20AC) 在 UTF8 中是 3 byte

-- a ( U+0061) 是 1 byte

SQL> select dump(to_char(UNISTR('\0061\20AC\0061')),1016) from dual;


DUMP(TO_CHAR(UNISTR('\0061\20AC\0061')),1016)

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

Typ=1 Len=5 CharacterSet=AL32UTF8: 61,e2,82,ac,61


SQL> select dump(substrB(to_char(UNISTR('\0061\20AC\0061')),1,4),1016) from dual;


DUMP(SUBSTRB(TO_CHAR(UNISTR('\0061\20AC\0061')

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

Typ=1 Len=4 CharacterSet=AL32UTF8: 61,e2,82,ac


SQL> select dump(substrB(to_char(UNISTR('\0061\20AC\0061')),1,3),1016) from dual;


DUMP(SUBSTRB(TO_CHAR(UNISTR('\0061\20AC\006

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

Typ=1 Len=3 CharacterSet=AL32UTF8: 61,20,20


SQL> select dump(substrB(to_char(UNISTR('\0061\20AC\0061')),1,2),1016) from dual;


DUMP(SUBSTRB(TO_CHAR(UNISTR('\0061\20AC\

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

Typ=1 Len=2 CharacterSet=AL32UTF8: 61,20

这里的要点是 SubstrB 会把在 AL32UTF8 中不合法的编码序列替换成空格符(=code 20)。比如"e2,82,ac"在 AL32UTF8 编码中是欧元符,但是如果除去了第一个 byte 后,该编码变成了"82,ac",这在 AL32UTF8 中是一个不合法的编码序列,因此,"82,ac"会被替换成合法的"20,20"(= 2 个空格符),否则 SubstrB 将生成能触发 ORA-29275 的字符串(string)。

除了 R/Lpad 以外(见下文),其他需要检查的函数包括"REPLACE","TRANSLATE" 和 "CHR"/"ASCII"。AL32UTF8 中的"CHR" 和 "ASCII"函数的返回结果往往与期望的不同(参见B.3)。

另外 NLS_INITCAP,NLS_LOWER 和 NLS_UPPER 可能返回多字节字符(multi byte characters),因此返回的字符串(string)可能比输入的字符串(string)的字节(byte)数要长。

同时请注意 sqlplus 输出的默认列宽可能发生改变,参见C4。

B.14) LPad 和  Rpad 的计数是基于"显示单位"而不是字符

​http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/functions087.htm#sthref1621​


这意味着

 select lengthb(rpad(bytestst ,10,'x')) lengthb, lengthc(rpad(bytestst ,10,'x')) lengthc, rpad(bytestst ,10,'x') from (select UNISTR('\00E9') bytestst from dual);

将返回 10 个字符(characters)但是实际上是 11 字节(byte)( \00E9 – 字符 é –在 AL32UTF8 是 2 bytes)。


在复杂的脚本中,"显示出来的字符"(character on the display)可能由几个组合字符组成,因此差异(chars vs bytes的次数)会更大。

在一些脚本中,返回的字符(characters)数量也可能不同,比如大多数亚洲字符都是 2 个"显示单位"的宽度,所以日语"住"字符(为全角的 U+4F4F,或者叫做 zenkaku)将在 L/RPAD 函数中计数为 2。

select lengthc(rpad(japanesechar ,10,'x')) lengthc,

rpad(japanesechar ,10,'x') from (select UNISTR('\4F4F')

japanesechar from dual);

Lengthc 将返回 9 个字符(character)。

事实上日语字符"住"(U+4F4F)占据了 2  次ascii  字符(比如 x)的显示宽度,因此 RPAD 将产生总共 9 个字符(character)的字符串(string)(2 次的"住"字符显示 + 8 次的 x 字符显式 = 10 个显示单位)。

想得到一定数量的字符(character)或者字节(bytes),可以用如下的脚本:

RPAD ( str , n - LENGTHC(str),'c')

如果要求的宽度 以byte 计算,那么使用 LENGTHB,如果要求的宽度以 character 计算,那么使用 LENGTHC。

SUBSTR( str || RPAD( 'c', n, 'c' ), 1, n )

如果要求的宽度以 byte 计算,那么使用 SUBSTRB,如果要求的宽度以 character 计算,那么使用 SUBSTR。


在上面的脚本中

* str 是需要填充的字符串(string)。

* 'c' 是填充符 – 必须是单字节的字符(single-byte char),通常空格(space)会被采用。

* n 是需要填充的宽度(按 bytes 或者 character 计算均可,因为是单字节)


示例函数代码:

create or replace function Rpad_char ( p_str varchar2, p_occ number , p_pad varchar2 default ' ') return varchar2 is

v_Rapd varchar2(4000);

v_pad varchar2(4000);

begin

v_Rapd :=  p_str;

v_pad := p_pad;

v_Rapd := SUBSTR(v_Rapd || RPAD(v_pad,p_occ, v_pad),1,p_occ);

return v_Rapd;

end;

/


create or replace function Rpad_byte ( p_str varchar2, p_occ number , p_pad varchar2 default ' ') return varchar2 is

v_Rapd varchar2(4000);

v_pad varchar2(4000);

begin

v_Rapd :=  p_str;

v_pad := p_pad;

v_Rapd := SUBSTRB(v_Rapd || RPAD(v_pad,p_occ, v_pad),1,p_occ);

return v_Rapd;

end;

/

有一些 9i 和 10g 关于 Lpad/rpad 问题和 East_Asian_Width Ambiguous character 的行为: ​​http://unicode.org/reports/tr11/#Ambiguous​


"Ambiguous width characters 全部都是那些作为全角字符出现在一些东亚遗留字符编码中的字符。他们具有一个“可解析的”宽度,或窄或宽,基于他们被使用的上下文。如果他们没有被使用在属于他们的特定遗留编码的上下文中,那么他们的宽度将被解析成“窄的”"。


比如字符 ↘ '东南箭头'(U+2198)是一个"East_Asian_Width Ambiguou"字符 ​​http://unicode.org/cldr/utility/character.jsp?a=2198​

在 9i 中,大多数 East_Asian_Width Ambiguous characters在UTF8 和 AL32UTF8 之间有不同的行为,在 9i UTF8 字符集中大多数 Ambiguous width characters 总是"窄的"(= 1 个显示位置, rpad('↘',5,'x')的结果是↘xxxx),不过同样的Ambiguous width characters 在 AL32UTF8 中是"宽的"(=2 个显示位置,rpad('↘',5,'x')的结果是↘xxx)。

这并不是对每个的这样的字符都对(一些"Ambiguous width character"列表中的字符没有在 oracle 的 UTF8/AL32UTF8 字符集中正确收录,因而导致了不同的 l/rpad 行为)。

在 11gR1 中, UTF8 和 AL32UTF8 都更新了 unicode "Ambiguous width character"列表并且使用"窄"模式(= 1 显示位置, rpad('↘',5,'x')返回 ↘xxxx)。"East_Asian_Width Ambiguous"字符的"上下文敏感"行为则因为设计限制无法实现。


AL16UTF16 实现了"East_Asian_Width Ambiguous"的"上下文敏感"行为。

注意:这需要将 NLS_NCHAR_CHARACTERSET 设置成 AL16UTF16。

alter session set NLS_LANGUAGE='dutch';

select rpad('↘',5,'x') from dual;

select rpad(to_nchar('↘'),5,to_nchar('x')) from dual;

alter session set NLS_LANGUAGE='simplified chinese';

select rpad('↘',5,'x') from dual;

select rpad(to_nchar('↘'),5,to_nchar('x')) from dual;


-- 将返回:


session SET altered.

RPAD('↘',5,'X')

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

↘xxxx          


RPAD(TO_NCHAR('↘'),5,TO_NCHAR('X'))

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

↘xxxx                   <<<<<---- 这里 ↘ 是 1 个显示宽度,因为上下文环境(NLS_LANGUAGE)被设置成荷兰语。


session SET altered.

RPAD('↘',5,'X')

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

↘xxxx          


RPAD(TO_NCHAR('↘'),5,TO_NCHAR('X'))

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

↘xxx                     <<<<<---- 这里 ↘ 是 2 个显示宽度,因为上下文环境(NLS_LANGUAGE)是中文。

如下脚本可以用来强制"East_Asian_Width Ambiguous"字符使用 2 个显示宽度:

create or replace function Rpad_amb ( p_str varchar2, p_occ number , p_pad varchar2 default ' ') return varchar2 is

v_nls_language varchar2(40);

v_Rapd_n Nvarchar2(2000);

v_pad_n Nvarchar2(2000);

begin

v_Rapd_n := to_nchar(p_str);

v_pad_n := to_nchar(p_pad);

select value into v_nls_language from nls_session_parameters where parameter = 'NLS_LANGUAGE';

execute immediate ('alter session set NLS_LANGUAGE=''simplified chinese''');

v_Rapd_n := Rpad(v_Rapd_n,p_occ,v_pad_n);

execute immediate ('alter session set NLS_LANGUAGE='''||v_nls_language||'''');

return to_char(v_Rapd_n);

end;

/

B.15) 使用 LIKE 和 INSTR

使用 LIKE 操作时最好参见 ​​Note 232085.1​​ comparison of LIKE2, LIKE4 and LIKEC operators。对于 INSTR/INSTR2/INSTR4/INSTRC 函数也是这样。

B.16) 返回字符值的字符函数可能静默截断数据

注意返回字符值的字符函数可能静默截断数据,请参考


​http://docs.oracle.com/docs/cd/B28359_01/server.111/b28286/functions001.htm#sthref928​


这些函数有:CHR,CONCAT,INITCAP,LOWER,LPAD,LTRIM,NLS_INITCAP,NLS_LOWER,NLSSORT,NLS_UPPER,REGEXP_REPLACE,REGEXP_SUBSTR,REPLACE,RPAD,RTRIM,SOUNDEX,SUBSTR,TRANSLATE,TREAT, TRIM 和 UPPER。


您可以在 ​​Bug 4185519​​ 找到一个 replace 函数的例子. 请注意这不是一个 Bug 而是期望的行为。

B.17) 在 AL32UTF8 数据库和非 AL32UTF8 数据库之间通过 dblink(正向或反向)来创建(Materialized)Views/CTAS,列的 size 变成 2 倍或 3 倍

当在一个 AL32UTF8 数据库中通过 database link 使用物化视图(Materialized View)或视图(View)或者执行 CTAS(Create Table As Select)时,如果 dblink 指向一个非 AL32UTF8 数据库,那么您可能会看到(一些)列在 size 上发生了改变。

这会发生在那些使用 BYTE 语义的列上: ​​Note 144808.1​​ Examples and limits of BYTE and CHAR semantics usage。

对于使用 CHAR 语义的表则不会看到列 size 的改变,因此我们建议在非 AL32UTF8 数据库端(= 基表列定义的那边)使用 CHAR 语义来避免列 size 的问题并且避免 ORA-1401 或者 ORA-12899 错误。


从一个 16bit(xx16xxxx 比如 ZHT16MSWIN950),8 bit(xx8xxxx 比如 WE8ISO8859P1 或者 AR8MSWIN1256)或者 7 bit(US7ASCII)的源连到 AL32UTF8(CTAS target/(Materialized)View side)数据库:


当在列定义的源端使用 CHAR 语义时,列 size 会保持不变(源库的"VARCHAR2 20 CHAR"列将在 AL32UTF8/UTF8 的目标库显示为 "VARCHAR2 20 CHAR")

注意"VARCHAR2 20 CHAR"可能包含 60~80 bytes,为了避免在刷新物化视图、select 视图或者 CTAS 的时候遇上 ORA-1401 或者 ORA-12899 错误,当双向使用 CHAR 语义时,确保 8 bite 的那边没有列大于 CHAR(666 CHAR)或者 VARCHAR2 (1333 CHAR)。参见 ​​Note 144808.1​​ Examples and limits of BYTE and CHAR semantics usage。


当在列定义的源端不使用 CHAR 语义而是使用 BYTE 语义时,列的 size 会变成 3 倍(源库的"VARCHAR 20 BYTE"列将在 AL32UTF8/UTF8 的目标库显示"VARCHAR2 60 BYTE")。

注意: 一个字符(character)在 UTF8 中的最大长度是 3,在 AL32UTF8 中的最大长度是 4,但是,在 AL32UTF8 中,没有 8bit 字符集定义的字符(character)超过 3 字节(byte)(例如欧元符号在 UTF8/AL32UTF8 编码中最大 3 byte), 所以(对于 8 bit 字符集编码的数据来说)最大可能的 BYTE 扩展是 3,无论目标库是 UTF8 还是 AL32UTF8。

有一个 ​​Bug 9901628​​ - add parameter to control expansion factor of columns for al32utf8

如果在 AL32UTF8 数据库中真的需要使用 BYTE 并且需要在创建物化视图(Materialized View)或视图(View)或者 CTAS 表时保持列的 size 不变,那么可以在 AL32UTF8 数据库端设置参数 _keep_remote_column_size=true。

注意当数据集中有非 US7ASCII 字符(character)时,使用 _keep_remote_column_size=true 可能触发 ORA-1401 或者 ORA-12899 错误,因为非 US7ASCII 字符将会扩展。

这甚至会在源库是 US7ASCII 但是 US7ASCII 包含一些非 US7ASCII 编码的情况下发生。

这可以通过在源库的 tables/user 上运行 csscan TOCHAR=AL32UTF8 来提前检查,如果有"Truncation"的数据,那么 ORA-1401 或者 ORA-12899 将会发生,如果只有"Changeless"和/或"Convertible"并且没有"Truncation"数据,那么不会发生 ORA-1401 或者 ORA-12899,即使在列定义为 BYTE 语义的源端设置了_keep_remote_column_size=true。

避免在同一张表中混合使用 BYTE 和 CHAR 语义。

_keep_remote_column_size=true 对使用 CHAR 语义的通过 database link 创建的远程对象((Materialized)View/CTAS)不兼容。

或者,如果使用 _keep_remote_column_size=true 时,那么所有远程对象需要使用 BYTE 语义。

或者,不设置 _keep_remote_column_size=true,那么所有的远程对象可以使用 CHAR 语义。

当设置 _keep_remote_column_size=true 时,在 AL32UTF8 端本地对象使用 CHAR 语义不是问题。

从一个 AL32UTF8 的源连到 16 bit(xx16xxxx 比如 ZHT16MSWIN950),8 bit(xx8xxxx 比如 WE8ISO8859P1 或者 AR8MSWIN1256)或者 7 bit(US7ASCII)(CTAS target/(Materialized)View side)的数据库:


当在源端(AL32UTF8)列定义中使用 CHAR 语义时,列的 size 会保持不变(源库的"VARCHAR2 20 CHAR"列将会在 16,8 或者 7 bit 的目标库中显示为"VARCHAR2 20 CHAR")。


当在源库(AL32UTF8)列定义中没有使用 CHAR 语义而是使用 BYTE 语义时,列的 size 将是如下情况:

* 如果目标库是 8 bit(xx8xxxx 比如 WE8ISO8859P1 或者 AR8MSWIN1256)或者 7 bit(US7ASCII)的数据库:列的 BYTE 长度不会改变。源库(AL32UTF8)中的"VARCHAR 20 BYTE"列在 8 或者 7 bit 的目标库中仍然显示为"VARCHAR2 20 BYTE"。

* 如果目标库是 16 bit(xx16xxxx 比如 ZHT16MSWIN950)的数据库:源库列的 BYTE 长度将会在目标库中变成 2 倍。源库(AL32UTF8)中的"VARCHAR 20 BYTE"列将会在 16 bit 的目标库中显示为"VARCHAR2 40 BYTE"。当在 16 bit 数据库端创建来源于 AL32UTF8 数据库的(Materialized)View/CTAS table 时,如果实在需要使用 BYTE 语义定义,并且想保持列的 size 与 AL32UTF8 源数据库相同,那么可以在 16 bit 数据库那边设置参数 _keep_remote_column_size=true。

即使采用了 _keep_remote_column_size=true,也不会看到有 ORA-1401 或者 ORA-12899 发生,在从 AL32UTF8/UTF8 到 16bit 的字符集的转换中,大部分情况下,数据长度实际上会减少。


注意当从 AL32UTF8 到非 Unicode 数据库 moving data 时,可能会发生数据丢失。

例如,当在一个 WE8MSWIN1252 的数据库中创建基表在 AL32UTF8 数据库的(Materialized)View 时,如果 AL32UTF8 数据库的基表中包含阿拉伯语或者日语,那么阿拉伯语或者日语会被替换成一个倒写的问号(inverse question mark)。

这属于正常的情况,因为 WE8MSWIN1252 只定义了西欧语言而没有阿拉伯语和日语。

这种情况可以通过在 AL32UTF8 源库的表/用户上运行 csscan TOCHAR=<target NLS_CHARACTERSET> 来进行检查,如果结果有"Lossy"的数据,那么一部分的数据集会丢失。

从 AL32UTF8 转换到 UTF8 或者反向转换:


当从 UTF8 到 AL32UTF8 数据库(或者反向)使用(Materialized)Views 或者做 CTAS(Create Table As Select)时,如果基表列定义采用了 BYTE 语义,3 倍扩展的现象也会发生。AL32UTF8 数据库中的"VARCHAR 20 BYTE"列会在 UTF8 数据库中显示为"VARCHAR2 60 BYTE",反向也成立。

这种情况下最好的解决方案是两端都采用 AL32UTF8 编码或者列定义采用 CHAR 语义。

如果基表的列没有采用 CHAR 语义,那么也可以使用 _keep_remote_column_size=true 参数。

如果使用了 _keep_remote_column_size=true 并且源端列定义采用了 BYTE 语义,当从 UTF8 数据库 moving data 到 AL32UTF8 数据库时,ORA-1401 或者 ORA-12899 错误不可能发生,当从 AL32UTF8 数据库 moving data 到 UTF8 数据库时,ORA-1401 或者ORA-12899 错误在技术上可能发生但是基本上不可能。

避免在同一张表中混合使用 BYTE 和 CHAR 语义

_keep_remote_column_size=true 对使用 CHA R语义的通过 database link 创建的远程对象((Materialized)View/CTAS)不兼容。

或者,如果使用 _keep_remote_column_size=true 时,那么所有远程对象需要使用 BYTE 语义。

或者,不设置 _keep_remote_column_size=true,那么所有的远程对象可以使用 CHAR 语义。

当设置 _keep_remote_column_size=true 时,在 AL32UTF8 端本地对象使用 CHAR 语义不是问题。

B.18) 从非 AL32UTF8 数据库使用 cursors(PL/SQL)来 fetch 数据时的 ORA-01406 或者 ORA-06502 错误

请参考 ​​Note 269381.1​​ ORA-01406 or ORA-06502 in PLSQL when querying data in (AL32)UTF8 db from remote non-UTF8 db using cursor。

B.19) 当使用 HTMLDB 时

当您使用 HTMLDB 时,请注意切换到(AL32)UTF8 后有一个密码相关的问题,此时请开启一个组件为(5/RDBMS/NLS)的 SR 并且参考本 Note。

B.20) 当使用非 US7ASCII 目录名称或者文件名时

在 windows 平台上,这基本上是一个相当糟糕的主意。在 Unix 平台上也会有问题.更多信息请参考 ​​Note 738758.1​​ Using Non US7ASCII characters in filenames with Extproc, Bfile and other callouts。

请注意在 AL32UTF8 数据库的 dafafile 名或者 directory 名中使用非 US7ASCII 字符是不支持的。

B.21) 当使用 XDB(xmltype)时

请参考 ​​Note 229291.1​​ XDB (xmltype) and NLS related issues for 9.2 and up。

B.22) Upper and NLS_upper give unexpected results on the Micro symbol or turkish i and I characters.

参见 ​​Note 1148599.1​​ Upper and NLS_Upper on the ( Micro ) symbol in an Unicode (AL32UTF8 or AL16UTF16 database)

和 ​​Note 329828.1​​ upper and lower function don't work as expected in Turkish for i and I characters。

B.23) Lower 和 NLS_lower 不能基于 Sigma 符号的位置正确地将希腊大写 Sigma/大写 Σ 转换成小写

希腊字母 Sigma 存在 3 个版本:'GREEK CAPITAL LETTER SIGMA' (Σ): U+03A3,'GREEK SMALL LETTER SIGMA' (ó): U+03C3,'GREEK SMALL LETTER FINAL SIGMA' (?): U+03C2,最后一个只会在一个单词的末尾用到。

Lower 和 NLS_lower 总是将大写 Sigma(Σ) 转换到小写 Sigma(ó): U+03C3,而不管 Sigma 符号(Σ)的位置。这种情况不限于 AL32UTF8 数据库,是当前 Oracle RDBMS 的行为,一个 enhancementt ​​Bug 411067​​ 已经被开出来以便在今后的版本中实现。

B.24) 当转换到  AL32UTF8 后 ORA-24816: Expanded non LONG bind data supplied after actual LONG or LOB column 错误可能会发生

有两件事情组合起来会触发错误 ORA-24816: Expanded non LONG bind data supplied after actual LONG or LOB column error


* 当一个 client 连接到一个多字节数据库(比如 AL32UTF8),当字符串被转换到多字节数据库编码时,一旦转换后的长度大于 4000 BYTES,Oracle 会内部地将它转换成 Long 类型,因为数据已经大于 varchar2 类型的最大长度 4000 bytes。

* OCI 要求 LOB 和 LONG 类型需要在绑定列表的最后,这意味着"真正"的 LONG/LOB 类型必须在最后,并且必须在因字符集转换而扩展超过 4000 BYTE 的 VARCHAR2 类型的后面.

​http://docs.oracle.com/docs/cd/E11882_01/appdev.112/e10646/oci05bnd.htm#LNOCI16372​


ORA-24816 错误本身是从 10.1 引入的,不会在 9i 中看到此错误。

因此总的来说,ora-24816 错误是一个预期的数据库端错误,当使用非 LONG 和非 LOB 的超过 4000 byte 的变量绑定时,"扩展的"的绑定必须放在绑定列表中的任何 LONG 或者 LOB 之前。

B.25) 对 CPU 和内存使用有何影响?

通常情况转换到 AL32UTF8 后本身不会对 RAM 和 CPU 使用产生很大影响。


在内存级别上,影响最大的事情是使用 CHAR 语义,比如一个 80 CHAR 变量将分配 4 倍的大小,到(320 bytes),因此使用 CHAR 语义时 user process 的大小和 data buffer 的确会有增长。不过请注意这只是一部分的内存消耗,因此并不是说您的内存消耗会 4 倍于之前,通常地,当在 AL32UTF8 中使用 CHAR 语义时,内存的扩展范围在 20% 到 30% 之间,但这只是大概数据。


在 CPU 级别上 CHAR 语义/AL32UTF8 本身不会有很大影响,通常影响会是几个百分比,一些其他的操作比如正则匹配在使用 AL32UTF8 时,CPU 使用会高些。


但是当转换到 AL32UTF8 后大量的语言排序会用到(NLS_COMP = LINGUISTIC 和 NLS_SORT 被设置成 GENERIC_M 或者其他类型的排序 – 参见  ​​Note:227335.1​​ Linguistic Sorting - Frequently Asked Questions),如果这个被用到,那么将会看到一个大的 CPU 使用率增长。

语言排序相对于 BINARY 排序来说是非常消耗 CPU 的,为了减少影响,必须使用正确语言索引和确保准确的统计信息,但语言排序总是会比 binary 排序消耗更多的 CPU – 多多少无法预测,这高度依赖于数据集大小和应用中用到的语言排序的量的多少。


如果您真的需要知道在您的特定环境中的影响,基本上只有一种方法,从您的生产系统中做一个 copy 迁移到测试环境,然后在完整的数据集和与生产环境当量的 user activity 下,执行适当负载的测试。

C) 客户端

C.1) 关于 NLS_LANG 常见误解

NLS_LANG的构成是: NLS_LANG=<NLS_Language>_<NLS_Territory>.<clients characterset>

当连接到一个 AL32UTF8 数据库时,通常认为 NLS_LANG 应当设置为 UTF8 或者 AL32UTF8。这不一定是正确的,NLS_LANG 与数据库字符集事实上没有关系。该参数的目的是让 Oracle 知道 client 的字符集是什么,这样 Oracle 才能在客户端编码(= NLS_LANG)和数据库字符集(NLS_CHARACTERSET)之间做必要的转换。


​Note 158577.1​​ NLS_LANG Explained (How does Client-Server Character Conversion Work?)

1.2 What is this NLS_LANG thing anyway?


请注意能连接到 Unicode database 的客户端(8.0 及以上的 UTF8 数据库客户端和 9i 及以上的 AL32UTF8 数据库客户端)与一个"真正的" Unicode 客户端之间的区别。

"真正的 Unicode 客户端" 指的是能显示/插入所有 Unicode 已知字符而不需要重新编译或者改变(操作系统)设置的客户端。


还有关于 NLS 参数在哪里定义的话题经常令人混淆,这在如下 Note 中的F) What are valid values to use for NLS parameters?部分有讨论 ​​Note:241047.1​​ The Priority of NLS Parameters Explained


注意 NLS_LANGUAGE 和 NLS_TERRITORY 与数据库能*存储*哪种字符的能力无关。

将 NLS_LANG 设置成 JAPANESE_JAPAN.WE8MSWIN1252 不会让您能*存储*日语因为 WE8MSWIN1252 没有定义日语字符。

但是将 NLS_LANG 设置成 AMERICAN_AMERICA.JA16SJIS *会*允许您存储日语(如果数据库也使用了能存储日语的 NLS_CHARACTERSET,比如 UTF8 或者 JA16SJIS,并且客户端是一个真正的日语 windows 系统)。

同样的,如果您的 Unix prompt 使用 UTF-8 作为操作系统语言环境(OS locale),那么您可以在 sqlplus 中使用 AMERICAN_AMERICA.AL32UTF8 来显示/输入日语,法语,俄语等等。

参照文档241047.1 The Priority of NLS Parameters Explained (Where To Define NLS Parameters) / 第F部分)  What are valid values to use for NLS parameters?

C.2) 将您的 UNIX 客户端配置成一个 UTF-8(Unicode)客户端

要想获得一个 Unix 的"Unicode 客户端",您需要首先将您的 Unix 环境配置成 UTF-8,您必须检查您的 telnet/ssh 软件能使用 Unicode,(作为最后一步)您可以将 NLS_LANG 设置成比如 AMERICAN_AMERICA.AL32UTF8 的值来启动 sqlplus。

以上描述在如下 Note 中有解释: ​​Note:264157.1​​ The correct NLS_LANG setting in Unix Environments。

请别忘记配置您的 telnet/ssh 客户端.错误的 telnet/ssh 配置是大多数"unix shell 显示问题"的 SR 开出来的原因。我们强烈建议优先使用 Oracle SQL Developer 作为 Windows 客户端,正如如下 Note 提到的: note 1628060.1 How to diagnose losing characters , getting "funny" output when inserting or selecting other than A-Z,a-z data ( = non English data like Chinese, Russian, Hebrew , insert any language here to the list that is not English) CHAR, VARCHAR2, LONG or CLOB,来检查数据库中的数据并且在尝试配置 telnet/ssh 环境之前确保数据是正确存储的。

C.3) 将您的 Microsoft Windows 客户端配置成一个 UTF-8(Unicode)客户端

在 Microsoft Windows 操作系统中,sqlplusw.exe 或者 sqlplus.exe 不能用作 Unicode/AL32UTF8 客户端来显示或者交互式地插入数据(= 输入一些东西和通过 sqlplus "输入"来 select / update 数据),几乎在所有的情况下,通过设置 NLS_LANG 为<NLS_Language>_<NLS_Territory>.AL32UTF8 来使用 sqlplus(w).exe 都是完全错误的。

正确的 sqlplusw.exe 或者 sqlplus.exe 的设置取决于您的 Microsoft Windows 客户端配置,请参见 ​​note:179133.1​​ The correct NLS_LANG in a Windows Environment。

在 Microsoft Windows 平台上,对 sqlplusw.exe 或者 sqlplus.exe 来说,可使用 AL32UTF8 作为 NLS_LANG 的唯一情形如下

* 执行一个 UTF-8 编码的脚本

* 创建一个 UTF-8 编码的 spool 文件

请一定注意这里指的是 .sql 或者 spool 文件的编码/字符集,参见本文 C6)。

对 Microsoft Windows 系统来说,有 2 个 Oracle 提供的 AL32UTF8(Unicode)sql 客户端:


* Oracle SQL Developer

这也是一个可用于调试显示问题(你看到了 "奇怪的" 符号或者字符比如 "?" , "¿" or "ÃçÂçÂçÂ")的 Unicode 客户端。

Oracle SQL Developer 是一个"公认的好客户端",它不需要客户端的 Oracle NLS 配置。

如何检查和配置 Oracle SQL Developer?如下 Note 有截图说明 ​​note 1628060.1​​ How to diagnose losing characters , getting "funny" output when inserting or selecting other than A-Z,a-z data ( = non English data like Chinese, Russian, Hebrew , insert any language here to the list that is not English) CHAR, VARCHAR2, LONG or CLOB

当遇到显示问题时,最好的办法是通过"公认的好客户端"检查并且检查数据是否在数据库中正确存储。Oracle SQL Developer 是做这个的最好工具。

* iSqlplus(不推荐使用 – 尽可能使用Oracle SQL Developer)

​Note 231231.1​​ Quick setup of iSQL*Plus 9.2 as Unicode (UTF8) client on Microsoft Windows .

​Note 281847.1​​ How do I configure or test iSQL*Plus 10i?


其他工具:

当在 Microsoft Windows 系统中使用/编写一个 Unicode 应用时,该应用应当被专门设计成使用 Microsoft Windows 上的 Unicode API,设置 NLS_LANG 为 AMERICAN_AMERICA.AL32UTF8 足以"使"应用 Unicode 化。

请咨询您的应用供应商或者供应商的开发环境来检查该应用是否能用作 AL32UTF8 客户端或者 Unicode 开发环境以及怎样使用。


常用的 Toad 工具较老的版本不能运行在 AL32UTF8 数据库上。请不要采用"变通方法"来为 toad 设置 NLS_LANG 为 UTF8,这样做数据将会损坏。

关于此问题的详细信息参考 Quest's 官网e ​​here​​ 和 ​​here​​ 。

关于 Toad 的 Unicode 支持问题,请联系 Quest software。


所有的 Oracle 9i 及以上的 Oracle 客户端对 AL32UTF8 数据库是"兼容的",即使使用非 AL32UTF8 的 NLS_LANG(参见本文C.1))。


在西欧/美国语言环境下,Microsoft Windows 客户端(设置 NLS_LANG 为 AMERICAN_AMERICA.WE8MSWIN1252,这在西欧/美国语言环境下的 Microsoft Windows 系统来说是正确的值)通过使用 sqlplusw.exe 来连接 AL32UTF8 是完全正确的。

然而,西方语言环境的 Microsoft Windows 的 sqlplus 客户端只能插入/查看西欧字符。

如果另外一个用户,在正确配置的中文环境 Microsoft Window 系统上使用 sqlplus 插入数据,那么插入的数据在西欧语言环境 Microsoft Windows sqlplus 客户端是无法显示的,如果通过西欧语言环境 Microsoft Windows sqlplus 客户端来 update 中文数据,那么数据会变成倒写的问号。

C.4) sqlplus 输出的默认列宽会改变

当转换到 AL32UTF8 后,另外一个不常被注意的副作用(直到迁移完成)就是 sqlplus 输出的默认列宽会发生改变。


当连接到 WE8MSWIN1252 或者其他 8 bit 字符集的数据库时,sqlplus 的输出:

SQL> select rpad(dummy,10,'x') from dual;

RPAD(DUMMY,10,'X')

----------

Xxxxxxxxxx

而当连接到 AL32UTF8 数据库时,输出变成如下结果:

SQL> select rpad(dummy,10,'x') from dual;

RPAD(DUMMY,10,'X')

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

Xxxxxxxxxx

更多信息请参考 ​​Note 330717.1​​ Output widths change after upgrade, or change of character set。

C.5) 将您的 web 客户端配置成一个 UTF-8(Unicode)客户端

当使用基于 web 的应用时,推荐阅读如下文档:


​Note 229786.1​​ NLS_LANG and webservers explained.

​Note 115001.1​​ NLS_LANG Client Settings and JDBC Drivers

当遇到显示问题时,最好的办法是通过"公认的好客户端"检查并且检查数据是否在数据库中正确存储。Oracle SQL Developer 是做这个的最好工具。

C.6) 使用 Sqlplus 或者 Oracle SQLDeveloper 来运行插入非 US7ASCII 数据的脚本

当使用 sqlplus 来执行包含非 US7ASCII 字符的 .sql 文件时,NLS_LANG 需要设置成文件的编码。


所以,当您想加载美国或者西欧 Microsoft Windows 下的 notepad 编写成的 txt/sql flat 文件时,请注意西欧 Microsoft Windows 系统默认是一个 WE8MSWIN1252 客户端,因此如果你在 sqlplus 中运行该脚本时 NLS_LANG 需要设置成WE8MSWIN1252,无论该脚本是在 Unix 还是 Windows 下的 sqlplus 执行。

例如:

* 数据库的 NLS_CHARACTERSET 是 UTF8 或者 AL32UTF8

* 您在 Microsoft Windows 客户端上安装了最新的 Oracle SQLDeveloper 软件

* 您已经遵循了 ​​Note 264157.1​​ The correct NLS_LANG setting in Unix Environments,并且正确配置了您的 Unix 环境,您的 telnet/ssh 客户端并且设置了 NLS_LANG 为 AMERICAN_AMERICA.AL32UTF8

* 您已经检查过当通过 telnet/ssh 来使用 Unix sqlplus 的情况下,"select UNISTR('\20AC') from dual;"能返回真正的 €(欧元)符号。

* 您想在 Unix server 上通过 sqlplus 来运行插入非 US7ASCII 数据的脚本。

* 现在,在一个美国/西欧 windows 客户端上,通过 notepad 来创建一个 test.sql 件,并且保存为 ANSI 编码方式(file - save as - choose ANSI),文件包含如下内容:

Drop table scott.test;

create table scott.test (numcol number, testcol VARCHAR2(10 CHAR));

insert into scott.test values (1,'Â');

insert into scott.test values (2,UNISTR('\00c2'));

commit;

select * from scott.test;

* 通过 ftp 或者 Samba 将此 test.sql 文件传输到您的 Unix 系统中,然后通过 Unix server 下的 sqlplus 来运行这个 test.sql 脚本。

* 在 Microsoft Windows 客户端上使用 Oracle SQLDeveloper 来 select 数据,您将看到第 2 行的 Â (大写 A 声调字符),但是第 1 行不能正确显示。

* 通过该 Unix server 的 sqlplus(=telnet/ssh)来 select 数据,您将看到第 2 行的 Â (大写 A 声调字符),但是第1行不能正确显示。

这里问题在于文件的编码不是 AL32UTF8(像 Unix server 上您的 NLS_LANG 设置)而是 WE8MSWIN1252。

使用 UNISTR('\00c2') 的行"工作正常"因为它是通过 Unicode 码点(codepoint)来插入数据,而不是来源于存储在 flat 文本文件中实际的"物理"字符。

为了正确的加载该 test.sql 文件,您需要:

  • 或者在 Unix server 上设置 NLS_LANG 为 AMERICAN_AMERICA.WE8MSWIN1252 来运行此 .sql 脚本,即使您的 Unix server 环境不是一个 1252 配置。WE8MSWIN1252 是正确的因为它体现了该文件的 8 bit 1252 编码。
  • 或者将此文件保存为一个 UTF-8 编码格式的文件而不是 8 bit 的编码(比如 ANSI)。一旦该文件的数据使用了 UTF-8 编码,那么通过设置 NLS_LANG 为 AMERICAN_AMERICA.AL32UTF8 来加载文件是正确的。AL32UTF8 是正确的因为它体现了该文件的 UTF-8 编码。

但是,为了检查("显示")文件加载的数据结果,您需要将 NLS_LANG 设置回您的 Unix 和 telnet/ssh 客户端的编码:

AMERICAN_AMERICA.AL32UTF8. 需要 AL32UTF8 因为它体现了您用来"显示"数据的 OS 环境编码。

通常对于大多数客户

或者有一个策略来规定在 sql 代码/脚本中不用非 US7ASCII 字符 – 这样使用了何 NLS_LANG 不再是问题了。

或者有一个策略来规定在所有的 flat txt/sql 文件中使用不带字节序标记(Byte Order Mark,简称 BOM,稍后会详细介绍)的 UTF8 编码。


如果您想在sqlplus里加载 UTF-8 编码的 .sql 文件,无论是什么平台(包括 Windows),NLS_LANG 都需要设置成<NLS_Language>_<NLS_Territory>.AL32UTF8,(例如 AMERICAN_AMERICA.AL32UTF8),因为它需要匹配文件的编码

问题是在 Microsoft Windows 上如果您采用了这样的设置,您将不能正确地"看到"UTF8 数据,但是数据将会被正确的 insert 到数据库中(假设 .sql 文件包含的是正确的 UTF-8 数据)– 例如您可以通过 Oracle SQLDeveloper 来检查结果。

只有在 Unix 平台上您才可以配置一个设置 NLS_LANG 为 UTF8/AL32UTF8 的系统,并且使该系统既可用于加载 UTF-8 编码的 .sql 文件又可用于"交互式地"insert/select UTF8 数据。


注意您不能使用 sqlplus 来加载 UTF-16 的文件,只有 UTF-8 文件才可能,比如在 Microsoft Windows 系统上使用 notepad 将文件明确的存为 UTF-8 编码。注意 notepad 中的"save as - encoding "Unicode""会创建 UTF-16 文件,而不是 UTF-8。


当采用 UTF8/AL32UTF8 作为 NLS_LANG 时,有另外一个问题: BOM ( ​​http://unicode.org/faq/utf_bom.html#BOM​​ ) 。

BOM 允许(不是强制的但是允许)在 flat 文件的开头设置 3 字节序(3 bytes sequence)来表明该文件是 UTF-8 编码(一些产品比如 Microsoft Windows Notepad 会插入BOM)。

大部分的 Microsoft 产品对 UTF8 编码文件会默认插入 BOM。


这给 sqlplus 带来一个问题,假设有两个文件通过 notepad 存为 UTF8(file - save as - choose UTF-8 as encoding):


test1.sql 包含 1 行:

select sysdate from dual;

test2.sql 包含 2 行:

-- this file needs to be saved as UTF-8

select sysdate from dual;

当 NLS_LANG 设置成 UTF8/AL32UTF8 时,windows 下的 sqlplus 给出如下结果:

SQL> @d:\test1.sql

SP2-0734: unknown command beginning "select sy..." - rest of line ignored.

SQL> @d:\test2.sql

SP2-0734: unknown command beginning "-- this f..." - rest of line ignored.


SYSDATE

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

23-JUL-09


SQL>

这个在第一行放一个注释行的"变通方法"有一点"丑陋",它会报出 SP2-0734,但毕竟能工作。

另外一个解决方案是使用脚本或者其他方法来从 flat/txt 文件去除 BOM(如果存在的话)​​http://www.w3.org/International/questions/qa-utf8-bom​

有一个 enhancement request -​​bug 13515585​​ add support for the utf-8 bom in sqlplus


同样方法我们可以使用 sqlplus(包括在 windows 系统上)来创建 UTF-8 spool 文件。

注意虽然在 sqlplus 中的输出是不正确的,但是 spool 文件中的结果数据是正确的。在 Windows 上检查一个 UTF-8 文件,可以用 notepad,file -open - choose "UTF-8" as "encoding"。如果您看到了方块而不是字符,请尝试其他的字体比如"Arial Unicode MS"。


如果不知道脚本使用了何种编码方式,我们可以使用如下文档中的"技巧": ​​Note 227330.1​​ Character Sets & Conversion - Frequently Asked Questions / 18. What is the best way to load non-US7ASCII characters using SQL*Loader or External Tables? 先找出 script/.sql file 的编码方式,然后设置 NLS_LANG 为 AMERICAN_AMERICA.<encoding of the script>来运行该脚本。

对于 Oracle SQLdeveloper 来说,加载和保存文件是在设置菜单中指定的: "Tools" - "Preferences" - "Environment" - "encoding" 下拉选择框,它是不依赖于 NLS_LANG 设置的。

C.7) 当 NLS_LANG 设置成 UTF8 或者 AL32UTF8 时,使用 sqlplus spool 到文件变得非常慢

这是由于 ​​bug 6350579​​ - spooling with trimspool and linesize takes too long in 10.2.0.3

修复版本: 11.2.0.1, 10.2.0.5, Windows 10.2.0.4.0 Patch 14 及以上,在 9i 不会发生

详情: 当 NLS_LANG 设置为 UTF8 或者 AL32UTF8 时,spool 文件会比 9i 花的时间长得多,尤其是 sqlplus 设置了"trimspool on"的时候,花的时间会格外长。

变通方法: 使用非 UTF8 的 nls_lang 或者 trimspool off

C.8) 使用 Oracle Applications

请参考:

​Note 393861.1​​ Globalization Guide for Oracle Applications Release 12

​Note 393320.1​​ Internationalization Update Notes for Release 12

​Note 222663.1​​ Internationalization Update Notes for the Oracle E-Business Suite 11i

C.9) 使用 Portal

您需要在数据库变更之后改变 Portal DAD 来适应新的字符集。可以通过如下方式:

1. 登录到 EM Application Server Control

2. 点击 midtier farm

3. 点击 HTTP_Server

4. 点击 Administration

5. 点击 PL/SQL Properties

6. 向下滚动到 DAD 然后点击它

在这里您可以修改 NLS Language 来匹配您的数据库。在作出变更后点击 Apply。

C.10) Oracle Forms PDF 与 Unicode

​Note 97441.1​​ Does Oracle Reports Support Unicode ( UTF8 ) Characters in PDF Output?

C.11) 将存放 OracleAS 10g Metadata Repository 的数据库转换成 AL32UTF8

参见 ​​Note 260192.1​​ Changing the NLS_CHARACTERSET to AL32UTF8/UTF8 (Unicode) in 8i, 9i , 10g and 11g 并且依照如下文档中的步骤 ​​Oracle Application Server Administrator's Guide 10g Release 2 (10.1.2) , 6.5 Changing the Character Set of OracleAS Metadata Repository​

D) 已知问题

* ​​Bug 16931734​​ - IN/OUT STRING PARAMETER TRUNCATES RIGHT INTO VARIABLE ON SQL*PLUS

修复版本:尚未修复

症状:如果客户端多字节字符集与多字节 NLS_CHARACTERSET(比如 UTF8/AL32UTF8)不一致时,数据可能会被静默截断或导致错误绑定长度,此时应当报出 ORA-06502 却没有报

变通方法: 采用 AL32UTF8 客户端设置

* ​​bug 6350579​​ - spooling with trimspool and linesize takes too long in 10.2.0.3

修复版本: 11.2.0.1, 10.2.0.5, Windows 10.2.0.4.0 Patch 14 及以上

详情: 当 NLS_LANG 设置为 UTF8 或者 AL32UTF8 时,spool 文件会比 9i 花的时间长得多,尤其是 sqlplus 设置了"trimspool on"的时候,花的时间会格外长

Workaround: 使用非 UTF8 的 nls_lang 或者不使用 trimspool 


* ​​Bug 9727970​​ NLS_INITCAP RETURNS WRONG RESULTS WITH NLS_SORT='XTURKISH'

修复版本: 尚未修复

Details: 在非土耳其字符集比如 AL32UTF8 中,NLS_INITCAP 对土耳其文的结果给出错误,NLS_UPPER 和 NLS_LOWER 工作正常

变通方法: 使用 NLS_INITCAP( NLS_LOWER( ... ) ... ) 作为替代。


* ​​bug 5010130​​ LPAD/RPAD BEHAVIOR NOT CONSISTENT IN PRE 10GR2 RELEASES WITH AMBIGUOUS CHARS

修复版本: 11.1.0.6

详情: ​​Unicode Ambiguous characters​​ 能有 1 或者 2 的显示宽度,取决于上下文,导致了使用 L/Rpad 的不同行为,AL16UTF16 完全实现了 Unicode Ambiguous characters 的行为,在 Oracle 10.2 中, UTF8 定义所有的 Unicode Ambiguous characters 的显示宽度为 1,AL32UTF8 定义 Unicode Ambiguous characters 的显示宽度为 2。

在 Oracle 11.1.0.6 中修复(UTF8 和 AL32UTF8 对 Unicode Ambiguous characters 现在都定义为显示宽度 1,采用 AL16UTF16 作为 NLS_NCHAR_CHARACTERSET 和 N-types 来完全实现 Unicode Ambiguous character 的行为),可以对 10g backport。


Unicode ​​EastAsianWidth.txt​​ 展示了哪些字符是"Ambiguous"的,他们具有"一个"东亚宽度属性。请注意这包括非亚洲字符比如希腊或者西里尔字符。


* ​​Bug 5581731​​ - Errors loading wrapped PLSQL in multibyte from client other than SQLPLUS

修复版本: 11.1.0.6, 10.2.0.4

详情: 试图通过 SQL * Plus 以外的机制(例如:OCI)来加载/编译一个 wrapped PLSQL 时,如果数据库的字符集是多字节,那么可能因 PLS-103,ORA-24344 或其他语法相关的编译错误导致失败。

变通方法: 从 input 中移除结尾空白行

E) 其他有用的参考

​Note 1137194.1​​ Master Note For Bugs, Fixed Versions and Workarounds in PL/SQL

​Note 268895.1​​ Oracle Database Server Patchset Information, Versions: 8.1.7 to 11.2.0

参考

​BUG:7448978​​ - ORA-24816 RESTRICTION ON LOB DML

​BUG:5915741​​ - SELECT * FROM V$SESSION; ENDS WITH ORA-29275

​NOTE:458122.1​​ - Installing and Configuring Csscan in 8i and 9i (Database Character Set Scanner)

​NOTE:229291.1​​ - XDB (xmltype) and NLS related issues for 9.2 and up

​NOTE:276914.1​​ - The National Character Set ( NLS_NCHAR_CHARACTERSET ) in Oracle 9i, 10g , 11g and 12c

​NOTE:393320.1​​ - Internationalization Update Notes for Release 12

​NOTE:144808.1​​ - Examples and limits of BYTE and CHAR semantics usage (NLS_LENGTH_SEMANTICS)

​NOTE:231231.1​​ - Quick setup of iSQL*Plus 9.2 as unicode (UTF8) client on windows.

​NOTE:745809.1​​ - Installing and configuring Csscan in 10g and 11g (Database Character Set Scanner)

​NOTE:985974.1​​ - Changing the Language of RDBMS (Error) Messages

​NOTE:1051824.6​​ - What languages are supported in an Unicode (UTF8/AL32UTF8) database?

​NOTE:444701.1​​ - Csscan Output Explained

​NOTE:1148599.1​​ - Upper and NLS_Upper on the µ ( Micro ) symbol in an Unicode (AL32UTF8 or AL16UTF16 database)

​NOTE:267356.1​​ - Character set conversion when using DBMS_LOB

​NOTE:393861.1​​ - Globalization Guide for Oracle Applications Release 12

​NOTE:333489.1​​ - Choosing a database character set means choosing Unicode

​NOTE:1297507.1​​ - Problems with (Importing) Encrypted Data After Character Set Change Using Other NLS_CHARACTERSET Database or Upgrading the (client) Oracle Version

​NOTE:227335.1​​ - Linguistic Sorting - Frequently Asked Questions

​BUG:13515585​​ - ADD SUPPORT FOR THE UTF-8 BOM IN SQLPLUS

​NOTE:260893.1​​ - Unicode Character Sets In The Oracle Database

​NOTE:429465.1​​ - 11gR1 New Feature: Case-Sensitive Passwords and Strong User Authentication

​NOTE:124721.1​​ - Migrating an E-Business Suite Installation to a New Character Set

​NOTE:738758.1​​ - Using Non US7ASCII characters in filenames with Extproc, Bfile and other callouts

​BUG:5010130​​ - LPAD/RPAD BEHAVIOR NOT CONSISTENT IN PRE 10GR2 RELEASES WITH AMBIGUOUS CHARS

​NOTE:281847.1​​ - How to Configure or Test iSQL*Plus 10g

​NOTE:734474.1​​ - ORA-600 [kole_t2u], [34] - description, bugs, and reasons

​NOTE:241047.1​​ - The Priority of NLS Parameters Explained (Where To Define NLS Parameters)

​NOTE:257772.1​​ - CLOBs and NCLOBs character set storage in Oracle Release 8i, 9i, 10g and higher

​NOTE:269381.1​​ - ORA-01406 or ORA-06502 in PLSQL when querying data in (AL32)UTF8 db from remote non-UTF8 db using cursor

​NOTE:222663.1​​ - Internationalization Update Notes for Oracle E-Business Suite 11i

​NOTE:232085.1​​ - comparison of LIKE2, LIKE4 and LIKEC operators

​BUG:5581731​​ - WRAPPED PACKAGES WITH TRAILING BLANK LINES AND A / VS UNICODE D/B GIVES PLS-103

​NOTE:1307346.1​​ - DBMS_LOB Loading and Extracting Binary File To Oracle Database

​NOTE:342443.1​​ - 10.2.0.x Oracle Database and Networking Patches for Microsoft Platforms

​NOTE:276548.1​​ - 10.1.0.x Oracle Database and Networking Patches for Microsoft Platforms

​NOTE:229786.1​​ - NLS_LANG and webservers explained.

​NOTE:329828.1​​ - upper and lower function don't work as expected in Turkish for i and I characters

​NOTE:268895.1​​ - Oracle Database Patchset Information, Versions 10.2.0 to 12.2.0

​NOTE:264157.1​​ - The Correct NLS_LANG Setting in Unix Environments

​NOTE:115001.1​​ - NLS_LANG Client Settings and JDBC Drivers

​BUG:4185519​​ - REPLACE SILENTLY DROPS DATA IN IN UTF8 DATABASE WHEN COL IS REACHING 4K BYTES

​NOTE:69518.1​​ - Storing and Checking Character Codepoints in an UTF8/AL32UTF8 (Unicode) database

​NOTE:227531.1​​ - Character set conversion when using UTL_FILE

​BUG:6268409​​ - ORA-29275 ERROR WHEN QUERYING THE SQL_REDO/UNDO COLUNMS IN V$LOGMNR_CONTENTS

​BUG:9727970​​ - NLS_INITCAP RETURNS WRONG RESULTS WITH NLS_SORT='XTURKISH'

​NOTE:237593.1​​ - Problems connecting to AL32UTF8 databases from older versions (8i and lower)

​NOTE:1297961.1​​ - ORA-01401 / ORA-12899 / ORA-01461 While Importing Or Loading Data In An AL32UTF8 / UTF8 (Unicode) Or Other Multibyte NLS_CHARACTERSET Database.

​NOTE:1137194.1​​ - Master Note For Bugs, Fixed Versions and Workarounds in PL/SQL


​BUG:9901628​​ - ADD PARAMETER TO CONTROL EXPANSION FACTOR OF COLUMNS FOR AL32UTF8


​NOTE:158577.1​​ - NLS_LANG Explained (How does Client-Server Character Conversion Work?)

​NOTE:179133.1​​ - The Correct NLS_LANG in a Microsoft Windows Environment


​NOTE:260192.1​​ - Changing the NLS_CHARACTERSET to AL32UTF8 / UTF8 (Unicode) in 8i, 9i , 10g and 11g

​NOTE:97441.1​​ - Does Oracle Reports Support Unicode ( UTF8 ) Characters in PDF Output?

​NOTE:227330.1​​ - Character Sets & Conversion - Frequently Asked Questions

​NOTE:330717.1​​ - Output widths change after upgrade, or change of character set