开篇介绍
最近经常碰到在 ETL 练习中出现这种转换失败的问题,试了多种方式,同样的代码同样的源结构和表结构但是一直不能成功执行,包报错。一般有这么几种错误:
Error at DST_LOAD_DATA_FROM_FILE [OLE_DST_EMPLOYEE[109]]: The column "FirstName" cannot be processed because more than one code page (1252 and 936) are specified for it.
或者就是
[OLE DB Destination [2]] Error: SSIS Error Code DTS_E_OLEDBERROR. An OLE DB error has occurred. Error code: 0x80004005.
An OLE DB record is available. Source: "Microsoft SQL Server Native Client 11.0" Hresult: 0x80004005 Description:
"OLE DB provider 'STREAM' for linked server '(null)' returned invalid data for column '[!BulkInsert] .FirstName'.".
那么这些问题是如何造成的呢?
不一致的字符集
这些错误与两方面因素有关,一方面是数据源,一方面是目标表。
在我们的测试案例文件中,源案例文件中包含有一些西欧字符,西欧拉丁字符集就是 1252。而我们有些测试环境下目标数据库中的字符集是 GBK - 表示简体中文,那么这种转换要么就是转换失败,要么就是转换成功但是显示为乱码。
下图是我们的源文件中的西欧拉丁字符,这种字符是无法存储在 GBK 字符集格式下的数据库中的,强行存储就是一个乱码。
右键查看我们的目标表,如果它的 Collation 是 SQL_Latin1_General_CP1_CI_AS 的话这种文件数据加到对应的表中是没有任何问题的。
但是如果是这样的 Collation 就会出现错误。
错误还原
创建数据流 Data Flow Task 并设置文件数据源和目标表对象(Collation = Chinese_PRC_CI_AS),文件连接管理器中的 Locale 是英语,Code Page 是 1252。
已经可以看到报错信息了:
The column "FirstName" cannot be processed because more than one code page (1252 and 936) are specified....
如果强制执行,就会看到这种信息:
Error at DST_OAD_DATA_FROM_FILE [OLE_DST_EMPLOYEE[109]]: The column "FirstName" cannot be processed because more than one code page (1252 and 936) are specified for it.
Error at DST_LOAD_DATA_FROM_FILE [OLE_DST_EMPLOYEE[109]]: The column "FirstName" cannot be processed because more than one code page (1252 and 936) are specified for it.
错误的原因在于数据流 DST_LOAD_DATA_FROM_FILE 中的数据流控件 OLE_DST_EMPOYEE 即 OLE DB Destination 目标控件中的列有多于一个以上的 Code Page 1252 和 936。 很显然 1252 指的是自动识别到的文件数据源中的字符集是拉丁西欧字符 1252, 而 936 则表示我们目标表中的字符格式。目标控件不知道如何选择哪一种字符集来处理,不统一就无法处理。
我们将文件链接管理器中的 Locale 改成简体中文 Chinese Simplified, Code Page 自动变为 936 GBK 类型。
这时的包编辑通过。
但执行再次报错。
错误原因:
[OLE DB Destination [2]] Error: SSIS Error Code DTS_E_OLEDBERROR. An OLE DB error has occurred. Error code: 0x80004005.
An OLE DB record is available. Source: "Microsoft SQL Server Native Client 11.0" Hresult: 0x80004005 Description:
"OLE DB provider 'STREAM' for linked server '(null)' returned invalid data for column '[!BulkInsert] .FirstName'.".
注意到错误最后有一个 Bulk Insert 的插入方式,提示了我们在 OLE_DST_EMPLOYEE 控件中使用的是 Bulk Insert 的插入方式。Bulk Insert 又称之为数据拷贝,速度和效率都很高,但是在 Bulk Insert 的过程中是不考虑字符转换的,如果字符不能完成相应的转换,一旦无法存储就会发生报错。
可以想一想,我们的源文件中的拉丁西欧字符是无法转换成对应的 GBK 简体中文的,所以在 OLE_DST_EMPLOYEE 中一定采用了 Bulk Insert 的方式,并且由于没有字符转换这一过程,GBK 的数据类型是无法保存这种西欧拉丁字符的。
为什么是 Bulk Insert 的方式,可以看看 OLE_DST_EMPLOYEE 就明白了,这里采用的是 Table or View - Fast Load 的模式。 Fast Load 只所以效率高,是因为它采用的就是 Bulk Insert 这种方式。
这也就是很多同学将 Table or view - Fast Load 改为 Table or view 就不会发生执行失败的原因。因为在普通的 Table or view 的加载模式下,这种字符类型会强制的变为 GBK 可接受的数据格式。
执行成功了。
但是到数据库中查查就会发现这个西欧字符实际上是没有转换成功的,变成了乱码。而这个乱码是不受我们控制的,因为目标数据库,表的 Collation 就是 GBK 格式。
Code Page 的相关文章
更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server) 如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。