理解 DB2 Universal Database的字符转换

简介

在当今世界中,许多数据库应用程序都在多个平台上使用多个数据库组件。数据库应用程序可以在 Windows® 系统上运行,但却通过AIX® 上所运行的 DB2 Connect™ 服务器与 DB2 UDB for z/OS® 数据库进行交互。在这些不同服务器之间流动的信息可能经历多次字符转换,而且大多数情况下,这些转换对用户是透明的。

 

然而,偶尔也需要进行一些配置。在这些情况中,理解这些转换如何工作以及哪个组件处理它们就十分有用。例如,请考虑以下情况:

  • “在 SPUFI 中 DB2 UDB for OS/390 表的一列里存储了感叹号(!)。当我从 DB2 UDB for Linux™,UNIX® and Windows 命令行处理器(CLP)检索同一列时,感叹号就转换成为一个方括号())。”
  • “在 DB2 UDB for Linux,UNIX and Windows 数据库中有西班牙语(Spanish)信息。Java™ 应用程序检索这些西班牙语数据(重音符号),但它们都遭到了损坏,即使我可以用CLP 看到正确内容。”

这些例子都是来自于 DB2 UDB 客户的疑问和问题。本文通过描述 DB2 UDB 字符转换过程,解决了所有这些以及类似的问题。

本文重点关注下列产品和版本:DB2 UDB for Linux, UNIX, and Windows version 8.2、DB2 UDB for iSeries™ 5.3 和 DB2 UDB for z/OS version 8。本文可能也适用于这些产品的早期版本。

术语

为了理解字符转换过程如何工作,您需要理解一些关键概念。图 1、2 和 3 提供了其中一些概念的概述。图 1. 字符转换的关键概念—— ASCII 编码模式图 2. 字符转换的关键概念—— EBCDIC 编码模式图 3. 字符转换的关键概念—— Unicode 编码模式

 

在这些图中,每个页码中的实线阴影边框表示的是十六进制数。

代码页(code page):可以定义为字母数字代码及其二进制表示的映射表。图 展示了几个用于不同语言和平台的代码页。在本图中,对于代码页1252,字母“K”可以用二进制数“01001011”(或十六进制表示法:“4B”)表示。而在另一代码页中,则可能用不同的二进制表示来表示这同一字符。

 

代码点(code point):是字符在代码页中的位置。在图 中,代码页 1252 中的代码点“4B”对应字符“K”。

字符集(character set):是一个定义的字符集。例如,一个字符集可以由大写字母 A 到 Z、小写字母 a 到z 以及数字 0 到 9 组成。该字符集可以在多个代码页中重复。例如,在图 和图 中,不同代码页中带点背景的单元格都表示同一字符集。

 

代码页可以分成下列几类:

  • 单字节代码页(有时称作单字节字符集或 SBCS)是一个最多可以容纳 256 (28) 个代码点的代码页。SBCS 中代码点的实际数目可能要少一些。例如,对于地域标识符 US 和代码集 ISO 8859-1,AIX 上的地区 en_US 就是 SBCS 代码页 819。
  • 双字节代码页(有时称作双字节字符集或 DBCS)是一个最多可以容纳 65536 (216) 个代码点的代码页。DBCS 中代码点的实际数目可能要少一些。例如,对于地域标识符 JP 和代码集 IBM-932,AIX 上的地区Ja_JP 就是 DBCS 代码页 932。
  • 复合或混合代码页包含不止一个代码页。例如,Extended UNIX Code(EUC)代码页可以包含多达四个不同的代码页,其中第一个代码页总是单字节的。例如,用于日语的 IBM-eucJP(代码页954)根据 EUC 编码规则引用日本工业标准(Japanese Industrial Standard)字符的编码。

在大型机(z/OS, OS/390®)和iSeries(i5/OS™、OS/400®)领域中,使用术语编码字符集标识符(Coded Character Set Identifier,CCSID)来代替代码页。CCSID 是一个 16 位的无符号整数,惟一地标识一个特定的代码页。例如,在大型机上,US-English 代码页由 CCSID 37 表示。German 代码页是 CCSID 273。其中一些代码页包含了其语言中特定字符的代码点;一些代码页具有相同的字符,但由不同 CCSID 中的不同代码点来表示。CCSID 是基于字符数据表示架构(Character Data Representation Architecture,CDRA)的,这是一个 IBM 架构,其中定义了一组标识符、资源、服务和约定以在异构环境中取得图形字符数据的一致表示、处理和交换。OS/400 完全支持CDRA。OS/390 支持 CDRA 的部分元素。

 

编码模式(encoding scheme):是为在特定计算平台上所使用的不同语言提供的代码页集合。常见的编码模式有:

  • American Standard Code for Information Interchange(ASCII),用于基于 Intel 的平台(如 Windows)和基于 UNIX 的平台,如AIX。图 展示了 ASCII 编码模式的简化表示。
  • Extended Binary Coded Decimal Information Code(EBCDIC),这是由 IBM 设计的编码模式。它通常用于 z/OS 和 iSeries 上。图 展示了 EBCDIC 编码模式的简化表示。
  • Unicode 字符编码标准是固定长度的字符,它为世界上的每一个字符都提供了一个惟一的代码点,无论平台、程序或语言如何。它包含近100,000 个字符,并且还在增加。Unicode 标准已经为很多行业巨头所采用,如 IBM、Microsoft 以及其他许多公司。诸如 XML、Java、LDAP、CORBA 3.0 等现代标准都要求使用 Unicode,它还是实现ISO/IEC 10646 的官方方式。图 展示了 Unicode 编码模式的简化表示。

DB2 UDB 字符转换过程

通过前面的讨论,应该已经清楚“代码页”的概念对于理解字符转换至关重要。可以在不同级别上定义代码页:

  • 在应用程序所运行的操作系统上
  • 在应用程序级别上,根据编程语言使用特定语句
  • 在数据库所运行的操作系统上
  • 在数据库级别上

在应用程序所运行的操作系统上定义代码页

 

在 Windows 上,代码页派生于 Windows 注册表中的 ANSI 代码页设置。您可以从 Regional Settings 控制面板查看您的设置。图 展示了一台 Windows XP 机器上的区域设置。

图 4. Windows XP 机器上的区域设置

 

在基于 UNIX 的环境中,代码页派生于地区(locale)设置。命令 locale 可以用于确定该值,如图 中所示。命令 localedef 可以编译新的 locale 文件,而/etc/environment 中的 LANG 变量可以用新的 locale 进行更新。

图 5. UNIX 机器上使用 locale 的区域设置

 

对于 iSeries 和 z/OS,请与您的系统管理员联系。

 

在应用程序级别上定义代码页

 

本文没有详细讨论应用程序代码页的设置,因为重点主要是在数据库方面。然而,本文提到了一些可能十分有用的概念。

默认情况下,应用程序代码页派生于它所运行的操作系统。对于嵌入式 SQL 程序而言,应用程序代码页在预编译/绑定时以及执行时确定。在预编译和绑定时,代码页派生于用于预编译语句和 SQLCA 中返回的所有字符数据的数据库连接。在执行时,当建立数据库连接时,确定用户应用程序代码页,而且它只在连接期间有效。所有数据,包括动态SQL 语句、用户输入数据、用户输出数据以及 SQLCA 中的字符字段,都是基于该代码页进行解释的。因此,如果程序包含常量字符串,您就应该使用相同的代码页预编译、绑定、编译和执行该应用程序。

对于 Unicode 数据库,您应使用主机变量来代替字符串常量。该建议的理由就是服务器可能在绑定和执行阶段都进行数据转换;如果程序中使用了常量字符串,这可能是一个忧虑。在绑定时,将基于在绑定期间有效的代码页转换这些嵌入的字符串。7 位 ASCII 字符通用于 DB2 Universal Databas 所支持的所有代码页,并且不会产生问题。对于非ASCII 字符,用户应该确保具有相同活动代码页的绑定和执行使用相同的转换表。

对于 ODBC 或 CLI 应用程序,您或许可以在 odbc.ini 文件或 db2cli.ini 文件中使用不同的关键字来调整应用程序代码页。例如,Windows ODBC 应用程序可以使用关键字 TRANSLATEDLL 来表示DB2TRANS.DLL 的位置,该文件包含代码页映射表,以及使用关键字 TRANSLATEOPTION 来定义数据库的代码页数目。DISABLEUNICODE 关键字可以用于显式地启用或禁用Unicode。默认情况下,没有设置该关键字,这意味着如果目标数据库支持 Unicode,DB2 CLI 应用程序就将使用Unicode 进行连接。否则,DB2 CLI 应用程序将使用应用程序代码页进行连接。当您显式地设置 DISABLEUNICODE=0 时,DB2 CLI 应用程序将总是用 Unicode 进行连接,无论目标数据库是否支持 Unicode。当DISABLEUNICODE=1 时,DB2 CLI 应用程序则总是用应用程序代码页进行连接,无论目标数据库是否支持Unicode。

使用 Java Universal Type 4 驱动程序的 Java 应用程序不需要在客户端机器上安装 DB2 UDB 客户机。通用 JDBC 驱动程序客户端将数据作为 Unicode 发送给数据库服务器,而数据库服务器则将数据从 Unicode 转换成所支持的代码页。从数据库服务器发送给客户端的字符数据是使用诸如 sun.io.* 转换例程的 Java 的内置字符转换器(converter)进行转换的。DB2 Universal JDBC Driver 支持的转换限于底层 Java Runtime Environment(JRE)实现所支持的那些。对于 CLI 和遗留(legacy)JDBC 驱动程序,要使用代码页转换表。

z/OS 上运行的应用程序使用 DB2 UDB for z/OS 安装面板上指定的应用程序编码 CCSID 值。此外,应用程序编码绑定(bind)选项也可以为程序中的主机变量定义 CCSID。对于动态 SQL 应用程序而言,就要使用APPLICATION ENCODING 专用寄存器来覆盖 CCSID。还可以通过在 DECLARE VARIABLE 语句中使用CCSID 子句,在更细粒度的级别上指定 CCSID。(例如:EXEC SQL DECLARE :TEST VARIABLE CCSID UNICODE;)

 

在数据库所运行的操作系统上定义代码页

本小节的讨论与上面的阐述完全相同。

在数据库级别上定义代码页

根据 DB2 UDB 平台,按不同方式定义代码页。

在 DB2 UDB for Linux, UNIX, and Windows 上

数据库可以只有一个代码页,它是在您通过 CREATE DATABASE 命令首次创建数据库时使用 CODESET 和 TERRORITY 子句设置的。例如,下列命令创建了数据库“spaindb”,使用代码集 1252 和地域 ID 34,这确定了 Windows 平台上用于西班牙语的代码页。(关于不同国家的代码集和地域 ID 列表,请查阅支持的地域代码和代码页。)

CREATE DATABASE spaindb USING CODESET 1252 TERRITORY es

创建数据库之后,您就可以通过发出图 6 中所示的命令 get db cfg for spaindb 查看该代码页的设置。

图 6. 查看 DB2 UDB for Linux, UNIX, and Windows 数据库的代码页

表 1 提供了图 6 中每个字段的描述。

表 1. 代码页数据库配置参数描述

字段名称

描述

Database territory

确定一个国家的地域标识符。

Database code page

指定用于创建该数据库的代码页。

Database country/region code

指定用于创建该数据库的地域代码。

Database collating sequence

指定用于进行字符数据排序的方法。

Alternate collating sequence

指定在非 Unicode 数据库中用于 Unicode 表的排序序列。在设置该参数以前,都无法在非 Unicode 数据库中创建 Unicode 表和例程。

排序序列将在小节其他注意事项中进行更详细的讨论。如果您使用默认值创建数据库,那么所使用的代码页就来自于操作系统的信息。一旦用给定的代码页创建了数据库,您就无法修改它,除非导出数据、删除数据库后,再用正确的代码页重新创建该数据库并导入数据。

 

字符转换示例

 

下列例子说明了字符转换的过程。让我们假定您具有下列配置:

  • 一个 ODBC 应用程序运行在 Windows 机器上,默认情况下,该应用程序使用操作系统的代码页,本例子中就是1252(Windows,English)。
  • 将运行 DB2 Connect 的 AIX 服务器设置为使用 Unicode。
  • iSeries 服务器使用代码页 66535,并具有一个包含表 DEPARTMENT 的数据库,表 DEPARTMENT 的定义如下:
CREATE TABLE DEPARTMENT
(DEPTNO    CHAR(3)              NOT NULL,
DEPTNAME  VARCHAR(36) CCSID 37 NOT NULL,
PRENDATE DATE                  DEFAULT NULL)

 

列 DEPTNO 和 PRENDATE 将使用 iSeries 代码页 66535 作为默认值。

当 Windows 应用程序发出诸如下面的请求时: SELECT DEPTNO FROM DEPARTMENT

将发生下列转换:

1.       Windows 通过代码页 1252 向 DB2 Connect 服务器发送请求。

2.       DB2 Connect 服务器将之转换成代码页 1208(Unicode),然后发送给 iSeries 服务器。

3.       iSeries 服务器将之转换成 CCSID 66535,并访问 DEPARTMENT 表中的数据。

4.       因为从表中获得的数据是在 CCSID 66535 中,所以将在该代码页中,将数据发送给请求者(本例中,DB2 Connect 服务器)。

5.       DB2 Connect 服务器将数据转换成代码页 1208,然后发送给 Windows 应用程序。

6.       Windows 操作系统将代码页 1208 转回到 1252。

其他注意事项

强制子集转换

在代码页转换期间,源代码页 X 中的字符也许不存在于目标代码页 Y 中。例如,让我们假定一家跨国公司存储了日语和德语这两种语言的信息。一个相应的日语应用程序将数据插入这个使用 German 代码页创建的DB2 UDB 数据库中。在这样的情形中,许多字符在 DB2 所使用的 CCSID 中没有代码点。这种情形下,解决该问题的一种方法就是通过仅将源 CCSID 中的字符映射到目标 CCSID 中的相应字符。那些没有映射的字符将通过预留的代码点来代替。(每一个代码页至少预留了一个代码点,以便进行替代。)那些无法映射到目标代码页的字符将永远丢失。该方法称作强制子集转换。

 

往返转换

 

另一种转换方法称作往返转换。两个 CCSID 之间的往返转换确保所有进行“往返”的字符都以初始的样子到达,即使接收 CCSID 不支持某一给定字符。往返转换确保从 CCSID X 转换成 CCSID Y,并返回为 CCSID X 的代码点的过程得到保护,即使CCSID Y 无法完全表示这些代码点。这是通过使用转换表实现的。

 

使用其他 DB2 UDB for Linux, UNIX, and Windows 转换代码页表

 

当您需要使用转换表的不同版本时,如 Microsoft 版本,就必须手工替换默认的转换表(.cnv)文件,该文件驻留在 UNIX 和 Linux 平台的 .../sqllib/conv 目录或 Windows 上的 .../sqllib/conv 中。这些表是用于在不同代码页之间转换值的外部代码页转换表。在替换 sqllib/conv 目录中现有的代码页转换表之前,您应该备份该文件。

 

DB2 UDB Unicode 支持

 

在所有平台上,DB2 UDB 都支持 International Standards Organization(ISO)/International Electrotechnical Commission(IEC)标准10646(ISO/IEC 标准 10646)Universal 2-Octect Coded Character Set(UCS-2)。UCS-2 是用 Unicode Transformation Format 8 位编码格式(UTF-8)实现的。UTF-8 是为了易于使用现有基于 ASCII 的系统而设计的。UTF-8 格式中数据的代码页/CCSID 值是 1208。用于 UCS-2 的 CCSID 值是 1200。UTF-8 被选择为字符数据列的默认格式,其中 UTF-16 用于图形数据列。

使用默认值创建的 DB2 UDB for Linux, UNIX, and Windows 数据库将在 ASCII 中创建表。为了创建 Unicode 数据库,要使用带有 CODESET 和 TERRITORY 子句的 CREATE DATABASE,如下: CREATE DATABASE unidb USING CODESET UTF-8 TERRITORY US

这个 Unicode 数据库中的表都将默认为代码页 1208。您无法在 Unicode 数据库中用 ASCII 代码页定义表。然而,反过来则可以;即您可以在非 Unicode 的数据库中创建 Unicode 表。这可以通过调用使用了 CCSID UNICODE 子句的 CREATE TABLE 语句来执行。例如: CREATE TABLE unitbl (col1 char(10), col3 int) CSSID UNICODE

为了使之工作,您首先需要激活数据库配置参数 alt_collate。一旦设置好了,该参数就无法进行修改或重新设置。 UPDATE DB CFG FOR nonunidb USING ALT_COLLATE IDENTITY_16BIT

在 DB2 UDB for iSeries 中,CCSID 子句可以用于单独的列上。例如,下列 SQL 语句创建表U_TABLE。U_TABLE 包含一个名为 EMPNO 的字符列,以及两个 Unicode 图形列。NAME 是一个固定长度的Unicode 图形列,而 DESCRIPTION 是一个可变长度的 Unicode 图形列。EMPNO 字段仅仅包含数字,且不需要Unicode 支持。NAME 和 DESCRIPTION 字段则都是 Unicode 字段。这两个字段可能包含不止一个 EBCDIC 代码页中的数据。

CREATE TABLE U_TABLE (EMPNO CHAR(6) NOT NULL,
NAME GRAPHIC(30) CCSID 1200,
DESCRIPTION VARGRAPHIC(500) CCSID 1200)

 

关于 iSeries 中有效的 CCSID 值列表,请查阅Supported CCSID mappings。

与 DB2 UDB for Linux, UNIX, and Windows 相似,在 DB2 UDB for z/OS 中,如果您在对象定义上使用了 CCSID UNICODE 子句,就可以存储并检索 Unicode 数据,例如:

CREATE TABLE DBTBDWR.WBMTEBCD
(CUSTNO  CHAR(8),
CUSTBU  CHAR(6),
CUSTEXT CHAR(3),
CNAME   VARCHAR(80) FOR MIXED DATA)
IN TEST.CUSTTS
CCSID UNICODE

 

DB2 UDB for z/OS 使用 LE(语言环境)的 ICONV 执行字符转换,除非安装了 z/OS Unicode Conversion Services。为了了解如何设置用于 DB2 的 z/OS Unicode Conversion Services,请查看信息 APAR II13048 和 II13049。为了查看是否安装了转换,请通过控制台使用命令 /d uni, all,如图 10 中所示。

图 10. 查看z/OS 上安装的字符转换

例如,图 10 展示了有从 1252 到 1208 以及 1208 到 1252(即从 Windows English 到Unicode 以及反向进行)的转换。

在所有系统之间使用 Unicode 编码模式将避免进行字符转换,并提高性能。

 

排序序列

排序序列是字符集的一种次序,确定每个字符与另一字符相比排列更高、更低或相同。排序序列将代码点映射至每个字符在已排序序列中的期望位置。例如,ASCII 中的排序序列是:空格、数字值、大小字符、小写字符。另一方面,EBCDIC 中的排序序列则是:空格、小写字符、大写字符和数字值。

如果在 EBCDIC 数据库上使用设计于操作 ASCII 数据库的应用程序,就可能产生问题,因为其排序序列不同。您可以创建定制的排序序列。关于更多细节,请查阅 Application Development Guide: Programming Client Applications 手册(参见参考资料)。

 

对于联邦系统的特殊注意事项

联邦系统不支持某些数据映射。例如,DB2 UDB 联邦服务器不支持数据类型 LONG VARCHAR 的映射。因此,所讨论的场景可能不起作用。关于更多细节,请查看 Federated Systems Guide(参见参考资料)。

在不同的代码页中移动数据

您无法用给定的代码页备份数据库,并用不同的代码页将之恢复为另一种。在 DB2 UDB for Linux, UNIX, and Windows 上,您应使用 export 或 db2move 实用程序,用新要求的代码页创建新的数据库,并对数据进行 import 或反向 db2move。在使用该方法时,DB2 UDB 将正确地执行字符转换。

 

处理二进制数据

通过 BLOB 数据类型或使用 FOR BIT DATA 子句定义的列将以二进制形式从源传递到目标,所使用的代码页是零。这表示不会发生任何代码页转换。

 

字符转换问题的确定与问题来源的识别

当您碰到字符转换方面的问题时,首先要识别应用程序和所涉及的 DB2 UDB 数据库服务器使用哪些代码页。

 

识别 DB2 UDB 环境中的代码页

除了前面所讨论的用于确定操作系统或 DB2 UDB 数据库服务器的代码页值的方法之外,下列方法将向您展示目标和源中的代码页。本小节中的讨论适用于 DB2 UDB for Linux, UNIX, and Windows。

使用带有“-a”选项的 CLP 来显示 SQLCA 信息 当您使用 CLP 的“-a”选项来显示 SQLCA 信息时,将展示源应用程序(本例中,CLP)和目标数据库的代码页。图 11 展示了一个从 Windows 上 CLP 连接到Windows 上数据库的例子。

图 11. 显示 SQLCA 信息以查看代码页值

在图 11 中,请注意下行: sqlerrmc: 1 1252 ARFCHONG SAMPLE QDB2/NT 557 557 0 1252 1

第一个实例“1252”指明目标上所使用的代码页,即 Windows US-English。第二个实例指明源上所使用的代码页,同样也是 Windows US-English。

图 12 展示了另一例子,这次是从 Windows CLP(其中安装了 DB2 Connect)连接到 DB2 UDB for z/OS 目标数据库。

图 12. 显示 SQLCA 信息以查看代码页值 —— 另一例子

在图 12,请注意下行: sqlerrmc: 1208 TS56692 HOSTV8 QDB2 1252

1208 指明目标 DB2 UDB for z/OS 子系统使用 Unicode。1252 指明源 CLP 应用程序使用Windows US-English。

为 CLI、ODBC 或 JDBC Type 2 应用程序使用CLI 跟踪 为了标识 CLI 和 JDBC type 2 应用程序的代码页,您可以使用 DB2 CLI on Linux, UNIX, and Windows 所提供的跟踪工具。默认情况下,该跟踪工具是禁用的,并且不使用任何附加的计算资源。启用时,无论应用程序何时访问 CLI 驱动程序,都将生成文本日志文件。您可以通过在 db2cli.ini 文件中添加下列 DB2 CLI 关键字来打开 CLI 跟踪,如下:

[COMMON]
trace=1
TraceFileName=/temp/clitrace.txt
TraceFlush=1

/temp/clitrace.txt 可以是任意的名称,用来做为跟踪存储的目录和文件名。

您可以在跟踪输出文件中从 SQLConnect() 或 SQLDriverConnectW() 调用找到应用程序和数据库代码页。例如,应用程序和数据库服务器在下列消息中使用相同的代码页(1252):

(Application Codepage=1252, Database Codepage=
1252, Char Send/Recv Codepage=1252, Graphic
Send/Recv Codepage=1200, Application Char
Codepage=1252, Application Graphic Codepage=1200

 

验证转换表或定义可用

在大多数情况下,没有定义源和目标代码页之间的转换表或定义。DB2 UDB for Linux, UNIX, and Windows 中的转换表存储在 sqllib/conv 目录下,它们通常处理大多数转换场景。

在 iSeries 上,IBM 提供的表可以在 QUSRSYS 库中找到。您还可以使用 Create Table(CRTTBL)命令创建自己的转换表。iSeries Information Center 中的 Globalization 主题包括了转换表列表(参见参考资料)。

您还可以运行下列查询来查看字符集名称列表: SELECT character_set_name from sysibm.syscharsets

在 DB2 UDB for z/OS 上,如图 中所示,您可能需要发出 /d uni, all 命令来显示已经安装了的转换。如果该命令的输出没有列出您所需要的转换,例如从 1252 到 1208,您就要向 Unicode Conversion Services 添加一个转换条目(示例 JCL hlq.SCUNJCL(CUNJIUTL)),例如:CONVERSION 1252,1208,ER

您还要在 DB2 UDB for z/OS 目录表 SYSIBM.SYSTRINGS 中验证存在给定的转换条目。例如,查看该表中的条目列表,就要发出该查询: SELECT inccsid, outccsid FROM sysibm.sysstrings

如果这些还没有解决您的问题,请联系。您可能被要求收集 DB2 跟踪,并用 fmt -c 选项(以前称作DDCS 跟踪)将其格式化。该跟踪将展示源和目标之间传递和检索了什么。

 

结束语

本文提供了对于 DB2 UDB 数据库内部以及数据库之间发生的字符转换过程的概述。它首先解释了诸如代码页、代码点和编码模式等关键概念,并说明了如何查看它们的当前值以及如何更新该值。接着,提供了通用场景以展示字符转换的过程。本文阐述了转换期间的一些特殊注意事项,并提供了一个两步确定导致转换问题原因的方法。

理想情况下,您应通过在转换的源和目标之间使用相同的代码页,尽力避免字符转换以提高性能。但是如果您的数据库场景十分复杂,您无法避免字符转换,那么本文的知识将帮助您尽可能顺利地完成该过程。