 | 级别: 中级 Ganesh R. Gosavi (ganesh_gosavi@yahoo.com), 高级 DB 顾问, IBM
2008 年 2 月 25 日 是否计划将 Oracle 应用程序移植到 IBM® DB2® for Linux®, UNIX® and Windows®?学习如何使用本文描述的步骤完成此任务。本文包含的示例脚本可以使任务更加简单。
简介
本文适用于以下开发人员、管理员或独立软件供应商(Independent Software Vendor,ISV):
- 拥有支持非 IBM 数据库(比如 Oracle Server)的数据库应用程序
- 其客户希望将 Oracle 应用程序迁移到位于分布式平台上的 IBM DB2 — DB2 for Linux, UNIX, and Windows(本文中使用 DB2)
许多想迁移 Oracle 应用程序以支持 DB2 的企业和业务合作伙伴提出了以下问题:
许多使用 Oracle 服务器的客户都希望在 DB2 上运行他们的应用程序。我需要进行哪些更改才能使应用程序支持 DB2 数据访问?
只要逐步执行本文描述的步骤,就很容易将 Oracle 应用程序移植到 DB2 平台。本文指出了在将 Oracle 应用程序迁移到 DB2 时可能遇到的最常见问题,同时也提供了克服这些问题的步骤。我曾经成功对拥有 1 亿行源代码的 Oracle 应用程序进行了概念证明(proof-of-concept)迁移,本文就是建立在这个基础之上。
以下是针对本文的示例迁移的一些假设:
- 示例 Oracle 应用程序是用 C/C++ 编程语言编写的。
- 在 UNIX (AIX® 5.2) 平台上的 DB2 Version V8.2 上进行移植。本文讨论的大多数问题也适用于 DB2 9。
以下是本文将描述的问题:
- DB2 预编译器不能识别声明部分中用户定义的数据类型(typefef,#define macros),位于
EXEC
SQL BEGIN DECLARE SECTION 和 EXEC SQL END DECLARE SECTION 语句之间。
- 由于参数类型不同,使用用户定义函数(UDF)(比如 ||、rawtohex、hextoraw,等等)开发的应用程序不能在 DB2 上编译。
- 使用 DECODE 函数的 Oracle 应用程序不能在 DB2 上正确编译。
- 需要在 DB2 中执行与包含 NOWAIT 的 Oracle SQL 语句类似的行为。
- 需要在 DB2 中处理大小超过 32672 的主机变量。
- 当移植到 DB2 时,需要考虑 Oracle 的 “Select for update” 语句。
本文将移植过程分解成一些主要任务,然后探讨每个任务涉及的问题。
任务 1:标识嵌入式 SQL(.sqC)程序
对于此任务,需要在应用程序中标识所有的 Pro C(SQL + C/C++ 程序组合)程序。首先应该将这些 Pro C 程序转换为 DB2 能用其预编译器解释的嵌入式 SQL 程序。
- SQL 与 C 程序组合:扩展名为 .sqc(在 UNIX 上)
- SQL 与 C++ 程序组合:扩展名为 .sqC(在 UNIX 上)
也许还需要回顾一下用 C/C++ 开发的嵌入式 SQL 程序示例。可以在 DB2 实例目录下的以下路径中找到它们:
- C 程序:sqllib/samples/c
- C++ 程序:sqllib/samples/cpp
这些嵌入式 SQL 程序是用 DB2 的预编译器(db2 prep)编译的。
问题 1
如果在 EXEC SQL BEGIN DECLARE SECTION
和 EXEC SQL END DECLARE SECTION 语句之间使用了用户定义的数据类型(例如 C/C++ 中的 “typedef”)和宏(#define),那么 DB2 预编译器在编译这些程序时会遇到一些问题。
因此,如果将一个包含嵌入式 SQL 语句的文件(.sqC)传递到 db2 precompile 命令,则编译时会提示出错。如清单 1 所示。
 |
注: 此示例只是为了描述问题并在编译之后生成相应的错误,所以其形式非常简单。要重现此场景,可以执行 db2 "create table test_table (dept int)" 命令创建一个 “TEST_TABLE” 表。
|
|
考虑如下的 test1.sqC 文件:
清单 1. test1.sqC
#include <stdio.h>
#define LONGBIG long
/*
Or we can have a definition like:
typedef long LONGBIG
*/
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
LONGBIG col1_val;
EXEC SQL END DECLARE SECTION;
int main()
{
EXEC SQL SELECT DEPT
INTO :col1_val
FROM TEST_TABLE;
return 0;
}
|
当试图使用 db2
prep 编译 test1.sqC 文件时,会得到以下错误:
清单 2. 预编译 test1.sqC 的结果
db2 prep test1.sqC bindfile
LINE MESSAGES FOR test1.sqC
------ --------------------------------------------------------------------
SQL0060W The "C++" precompiler is in progress.
11 SQL0008N The token "LONGBIG" found in a host variable
declaration is not valid.
18 SQL4942N The statement selects an incompatible data type
into host variable ":col1_val". SQLSTATE=42806
SQL0095N No bind file was created because of previous
errors.
SQL0091W Precompilation or binding was ended with "3"
errors and "0" warnings.
[db2inst1]/users/ganesh_gosavi/mig1/issues/NULL_issue>
|
出现这些错误是因为 DB2 预编译器不能解析一些 typedef 和宏,这些 typedef 和宏用于通过 EXEC SQL BEGIN DECLARE SECTION 和 EXEC SQL END
DECLARE SECTION 语句定义的声明部分。
问题 2
第 2 个问题与在代码声明部分中广泛使用的类型定义有关,这些类型定义包括 typedef 结构、嵌入式结构中的 typedef、用户定义的数据类型,以及在各种嵌入式头文件中声明的 typedef。
清单 3 中的示例源代码演示了该问题。
清单 3. test2.sqC
#include <stdio.h>
#include <string.h>
.
.
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
#include "extrndef.h"
EXEC SQL INCLUDE 'UPR_ELEMENTS.H';
EXTERN varchar b2k_amount_host_str[50][35];
static varchar cur_time[20];
static varchar cur_user[16];
static char cur_time[20];
static char cur_user[16];
EXEC SQL END DECLARE SECTION;
|
问题 1 和 2 的解决方案
以上问题的一个常用解决方案是,不要在 EXEC SQL 声明部分中使用 typedef 和宏。但是如果拥有很多这样的文件,手动完成此操作很麻烦,您也许希望进行自动处理。我们在此处提供一个 Perl 脚本(esql_prep),以减少手动操作的麻烦,该脚本使任务变得更加简单。
注:该脚本的主要开发人员是 Knut Stolze (stolze@de.ibm.com)。请谨慎使用此脚本,跟平常一样,在将其应用到源代码中之前,请首先测试其功用。也许有一些特殊条件未包含在脚本中,这可能会产生错误的结果。
使用此 Perl 脚本的语法如下:
清单 4. Perl 脚本的语法
./esql_prep xxxxxxx.SQX "gcc -E -I<path>" |
此处,
- “path” 既可以是头(.h)包含文件的相对路径,也可以是其绝对路径。
- “xxxxxxx.SQX” 是一个含有问题 1 和问题 2 中所描述问题的文件。
- “gcc -E” 选项告诉 C++ 编译器对 “xxxxxxx.SQX” 文件进行预处理。
上面的命令对 xxxxxxx.SQX 文件进行预处理,并生成一个新文件 xxxxxxx.SQC。在 xxxxxxx.SQC 文件中可以看到,预定义的宏和用户定义类型已经被替换了。然后可以将 xxxxxxx.SQC 文件传递给 db2 prep 预编译器执行下一步操作。
问题 3
当将嵌入式 SQL 程序(例如,test3.sqc 或 test3.sqC)传递给 db2 prep 预编译器时,会输出两个文件:
- 一个修改过的 C 或 C++ 程序(test.c 或 test.C)
- 一个绑定文件(test.bnd)
当将 test3.c 或 test3.C 传递给语言编译器(C/C++)时,语言编译器有时会返回一个与清单 5 类似的错误:
清单 5. test3.sqC 编译错误
"test3.i",line 6043.38:1540-0274 (S)The name lookup for "NULL" did not find a declaration.
|
此错误是由于预编译器在创建 C 文件时插入与清单 6 类似的语句引起的:
清单 6. 预编译器输出
.
.
sql_setdlist[0].sqldata = (void*)&col1_val;
#line 6043 "test3.i"
sql_setdlist[0].sqlind = 0L;
#line 6043 "test3.i"
sqlasetdata(3,0,1,sql_setdlist,NULL,0L);
} |
此时发生了如下操作?
- 上面的
esql_prep 通过 C/C++ 预编译器运行源代码(仍包含嵌入式 SQL 语句)。结果,所有的 #include 指令都被解析了,包括 <stdio.h> 中的指令。
- DB2 预编译器插入这些语句(包含 “NULL”),如上面的清单所示。
- 常规的 C/C++ 编译器开始编译。它执行一个常规的 C/C++ 预编译。但是预编译不会做任何事情,因为这些都在步骤 1 中完成了。特别是,不再有 #include 指令。结果,代码中的 “NULL” 未被更改。C/C++ 编译器找到 “NULL” 并报告错误,因为它不知道该怎么做。毕竟 “#define NULL (void *)0” 不在此处,因为没有找到 #include <stdio.h>。
- 因此,必须为 NULL 提供一个常规的 #define。
结果,db2 prep 预编译器返回清单 4 所示的错误。
问题 3 的解决方案
要消除此错误,可以使用编译器的 -D 选项。
例如:
清单 7. test2.sqC
/usr/vacpp/bin/xlC -c -o test3.o -DNULL=0 -I./ -I/db2/db2inst1/sqllib/include
|
任务 2:在 DB2 for Linux, UNIX, and Windows 中实现 Oracle UDF 行为
这是在将 Oracle 应用程序迁移到 DB2 for Linux, UNIX, and Windows 时面临的主要挑战。应用程序调用 Oracle 支持的各种 UDF。对各个文件和目录的所有代码(可能有数百万条)进行检查是非常困难的。找到每个位置并更改相应的源代码,从而让应用程序支持 DB2,这是一项单调、麻烦且容易出错的工作。
本文下载部分的 OracleToDB2UDFs.zip 提供了许多等价的 Oracle UDF。只需在选择的模式下对它们进行注册即可。无需修改应用程序源代码就可使用这些 UDF。
以下是提供的 UDF:
-
|| or CONCAT
此 UDF 接受各种类型的参数,包括:
- ||(varchar, varchar)
- ||(varchar, int)
- ||(int,int)
- ||(int, varchar)
- ||(decimal,varchar)
- ||(varchar, int)
- ||(varchar, timestamp)
- ||(timestamp , varchar)
既可以用符号名称 || 注册此 UDF,也可以用函数名称 concat,或两者兼可,这取决于应用程序源代码中使用的名称。
下面演示了此函数的注册过程。其他 UDF 的注册过程也与此相同。
concat.db2 文件中提供了用于此 UDF 的函数。如果希望支持不同的参数,可以向 CONCAT.db2 文件添加带有合适的参数的函数。
假设希望在选择的模式或用户 TBAADM 下注册 UDF,需要输入以下命令:
-
db2 connect to sample user TBAADM using PASSWORD
-
db2 -td/ -vf concat.db2
注:此处,“/” 是 CONCAT.db2 文件内部的分隔符,用于将函数分开(在 DB2 中通常不使用 / 作为分隔符。在 DB2 中,分隔符 “!”、“@”、“#” 和 “;” 通常用作语句结束符。但是由于 Oracle 使用 “/”,我在此处的示例和脚本中使用 “/” 作为语句结束符)。
这一步在模式 TBAADM 下注册所有函数(|| 带有各种参数)。然后用户应用程序开始使用新注册的带有合适参数的函数(假设已经连接到合适的用户,或者设置了正确的 CURRENT SCHEMA)。
清单 8. 示例 1: || 带有输入参数 (int, int)
>db2 values "1234 || 4567"
1
------------------------------
12344567
1 record(s) selected. |
清单 9. 示例 2: || 带有输入参数 (int, varchar)
>db2 "values 1234 || ' Ganesh'"
1
------------------------------
1234 Ganesh
1 record(s) selected. |
清单 10. 示例 3: || 带有输入参数 (varchar, varchar)
>db2 "values 'ganesh_gosavi' || '@yahoo.com'"
1
-----------------------
ganesh_gosavi@yahoo.com
1 record(s) selected. |
在 DB2 下需要对接下来讨论的等价 Oracle 函数执行同样的注册过程,以便应用程序可以直接使用这些函数,无需对调用它们的源代码语句进行修改。
注:
IBM Migration Toolkit (MTK) 也提供了这些函数。在此处提供这些函数是为了支持不同类型的参数,以及函数能接受的不同参数组合。合适的函数调用取决于参数的数量和类型。
-
CHR
此 UDF 接受整数、浮点类型的参数:
-
HEXTORAW
此 UDF 接受单个 varchar 参数:
-
RAWTOHEX
此 UDF 接受单个参数:
- RAWTOHEX (varchar for bit data)
-
INSTR
此 UDF 接受以下类型的参数:
- INSTR (varchar, varchar, integer, integer)
- INSTR (varchar, varchar, integer)
- INSTR (varchar, varchar)
-
LAST_DAY
此 UDF 接受以下两种类型的参数:
- LAST_DAY (date)
- LAST_DAY (timestamp)
-
RPAD 和 LPAD
此 UDF 接受以下类型的参数:
- RPAD (integer, integer)
- RPAD (integer, integer, varchar)
- RPAD (varchar, integer)
- RPAD (varchar, integer, varchar)
- LPAD (integer, integer)
- LPAD (integer, integer, varchar)
- LPAD (varchar, integer)
- LPAD (varchar, integer, varchar)
-
TRIM
此 UDF 接受以下类型的参数:
- TRIM (varchar, varchar, varchar)
- TRIM (varchar, varchar)
- TRIM (varchar)
-
RTRIM and LTRIM
此 UDF 接受以下类型的参数:
- LTRIM (varchar, varchar)
- RTRIM (varchar, varchar)
-
MONTHS_BETWEEN
此 UDF 接受以下类型的参数:
- MONTHS_BETWEEN (date, date)
- MONTHS_BETWEEN (timestamp, timestamp)
-
NEXT_DAY
此 UDF 接受以下类型的参数:
- NEXT_DAY (date, varchar)
- NEXT_DAY (timestamp, varchar)
-
NVL
此 UDF 接受以下类型的参数:
- NVL (varchar, varchar)
- NVL (bigint, bigint)
- NVL (varchar, bigint)
- NVL (bigint, varchar)
- NVL (decimal, varchar)
- NVL (decimal, decimal)
- NVL (float, float)
- NVL (timestamp, timestamp)
注: DB2 Viper 2 (DB2 9.5) 也引入了这个 NVL 函数。
-
TO_CHAR
此 UDF 接受以下类型的参数:
- TO_CHAR (decimal, varchar)
- TO_CHAR (float)
-
TO_DATE
此 UDF 接受以下类型的参数:
- TO_DATE (varchar, varchar)
- TO_DATE (varchar)
- TO_DATE (varchar,varchar, timestamp)
-
TO_NUMBER
此 UDF 接受以下类型的参数:
- TO_NUMBER (varchar, varchar)
- TO_NUMBER (varchar)
-
TRUNC
此 UDF 接受以下类型的参数:
- TRUNC (timestamp, varchar)
- TRUNC (timestamp)

 |

|
任务 3:在 DB2 for Linux, UNIX, and Windows 中实现 Oracle DECODE 行为
Oracle 的 DECODE() 函数包含 IF-THEN-ELSE 语句的功能。
DB2 实际上不支持 DECODE 函数。DB2 拥有 CASE 表达式,它与 Oracle 的 DECODE 函数是等价的。因此,如果应用程序(嵌入式 SQL 程序)使用 DECODE 函数,则需要将所有的 DECODE 语句转换为等价的(语句结构正确)DB2 CASE 结构。完成此操作之后,您的程序和应用程序将支持 DB2。
现在,面对遍布多个文件和多重目录的数百万行源代码,您一定一筹莫展。如何查找每个独立的文件并将所有 DECODE 函数调用替换为等价的 DB2 CASE 表达式呢?工作量太大了吧?
不用担心!我们已经将此任务进行了分解,并提供了一个 Perl 脚本(replace_decode),不但减少了工作量,而且使任务变得更加简单。
此 Perl 脚本(replace_decode)带有一个源代码(具体来说是 C++)文件或目录(假设为 .PXX、.CXX 或 .SQL,这是一个嵌入式 SQL 程序)。可以修改此 Perl 脚本以支持所选择的扩展名。此脚本用于查找源代码中所有的 DECODE 函数调用,并自动将其替换为 DB2 的等价的 CASE 表达式。
例如:
清单 11. 嵌入式 SQL 程序中原始的 SQL 语句
SELECT DECODE(MAX(TO_NUMBER(SRL_NUM)),NULL,0,MAX(TO_NUMBER(SRL_NUM))) FROM DSACH WHERE
DSA_ID = '%s' AND PRODUCT_CODE = '%s' AND CRNCY_CODE = '%s' AND COMM_EVENT_ID = '%s'
|
清单 12. 使用 Perl 脚本进行转换
SELECT CASE when MAX(TO_NUMBER(SRL_NUM)) is NULL THEN 0 ELSE MAX(TO_NUMBER(SRL_NUM)) END
FROM DSACH WHERE DSA_ID = '%s' AND PRODUCT_CODE = '%s' AND CRNCY_CODE = '%s' AND
COMM_EVENT_ID = '%s'
|
使用 replace_decode.pl
使用以下语法调用此脚本:
清单 13. 调用 perl 脚本
perl replace_decode.pl --dir <source dir> --file <source file> --help
|
注:
- 需要指定 --dir 或 --file。
- 可以同时指定目录和文件。目录和文件中的 - .cxx & .pxx 文件将被处理。
- 如果跳过了 --dir 和 --file,或者如果指定了 --help,那么将会显示用法。
- 重复 --file 选项可以指定多个文件:
清单 14. 重复 --file 选项
perl replace_decode.pl --dir dir1 --file src_file1 --file src_file2 --file src_file3
|--10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
|
- 可以使用绝对路径或与当前目录相对的路径指定目录和文件名称。
注:此脚本的主要开发人员是 Renu Pinky Sumam(renusuma@in.ibm.com)。请谨慎使用此脚本,在将其应用到源代码中之前,请跟平常一样首先测试其功能。也许有一些特殊情形未包含在脚本中,这可能会导致错误的结果。
任务 4:转换包含 NOWAIT 的 SQL 语句
如果应用程序不能获得需要的锁,ORACLE select-for-update 语句使用 NOWAIT 子句避免应用程序被阻塞。
例如,当关键字 NOWAIT 被用于 FOR UPDATE 子句时,如果表被另一个用户锁定,该关键字就会告诉 Oracle 不要等待。
DB2 不支持 NOWAIT 子句,因为 DB2 中所有的读访问(除了未提交的读事务)都会获得所选行上的一个共享锁。DB2 还有一个死锁检测器,只要发生死锁,它就会自动选择一个事务进行回滚, 并继续执行其他事务。
在 DB2 中,可以将数据库配置参数 LOCKTIMEOUT 设置为 0,这样就可以模拟 NOWAIT 子句。LOCKTIMEOUT 是死锁检测器在系统中检查死锁的时间间隔。通过这种方法,如果锁定资源被阻塞,DB2 就会立即返回到应用程序,并出现一个 sqlcode -911 和
sqlstate 40001 提示。
在数据库层,可以这样实现:
清单 15. 在数据库层将 LOCKTIMEOUT 设置为 0
DB2 UPDATE DB CONFIGURATION FOR <DBNAME> USING LOCKTIMEOUT 0 |
在事务连接层,可以使用以下命令之一来实现:
清单 16. 在事务层模拟 NOWAIT
SET CURRENT LOCK TIMEOUT NOT WAIT |
or
清单 17. 在事务层设置锁定超时
SET CURRENT LOCK TIMEOUT <time in seconds> |
要获得一个锁,DB2 至多等待 30 秒。如果在这个时间内没能获得锁,就会出现 SQLCODE -911,以通知用户或应用程序重试。
任务 5:修改大小超过 32672 的主机变量
如果应用程序声明了大小超过 32672 的 CHAR 或 VARCHAR 主机变量,db2 prep 预编译器将不会编译嵌入式 SQL
程序并返回一个错误。
例如,考虑下面的 test4.sqc 程序
清单 18. 大小超过 32672 的主机变量示例
#include <stdio.h>
EXEC SQL INCLUDE SQLCA;
int test_ArraySize(void)
{
EXEC SQL BEGIN declare SECTION;
char ResumeData[65533];
EXEC SQL END declare SECTION;
EXEC SQL select RESUME
INTO :ResumeData
FROM EMP_RESUME
WHERE RESUME_FORMAT = 'ascii';
return SUCCESS;
}
|
如果试图像清单 19 这样用 db2 prep 预编译器编译此程序,将会返回一个错误。
清单 19. 编译上述程序之后返回的错误:
db2 prep test4.sqc
LINE MESSAGES FOR test4.sqc
------ --------------------------------------------------------------------
SQL0060W The "C" precompiler is in progress.
8 SQL0314N The host variable "ResumeData" is incorrectly
declared.
SQL0092N No package was created because of previous
errors.
SQL0091W Precompilation or binding was ended with "2"
errors and "0" warnings.
|
发生错误是因为 DB2 字符串不应该大于 32672 字节。任何更长的数据都必须表示为一个 CLOB 数据类型。
要解决这个错误,需要对大于 32672 的主机变量使用以下语法:
清单 20. 用于较大主机变量的语法
EXEC SQL BEGIN declare SECTION;
SQL TYPE IS CLOB (65533) ResumeData;
EXEC SQL END declare SECTION;
|
然后可以继续操作:
清单 21. 正确声明主机变量之后程序被成功编译
db2 prep test4.sqc
LINE MESSAGES FOR test4.sqc
------ --------------------------------------------------------------------
SQL0060W The "C" precompiler is in progress.
SQL0091W Precompilation or binding was ended with "0"
errors and "0" warnings.
|
警告:现在,尽管 DB2 预编译成功了,但是还会碰到编译问题,因为最初的
char *ResumeData
已经被转换为 struct { int length; char *data; } ResumeData;。
必须管理数据和长度属性,这很可能需要更改代码。
任务 6:修改带有 SELECT FOR UPDATE 子句的 SQL 语句
如果应用程序包含使用 SELECT FOR UPDATE 子句的 SQL 语句,则可能获得一个错误:
清单 22. SELECT FOR UPDATE 错误
SQL0511N The FOR UPDATE clause is not allowed because the table specified by the cursor
cannot be modified.
SQLSTATE=42829 |
例如,考虑以下 SQL 语句:
清单 23. SELECT FOR UPDATE 示例
EXEC SQL declare MY_CUR CURSOR for select
col1_main_system,
col2_sub_system,
TO_CHAR(col3_system_date, 'DD-MM-YYYY HH24:MI:SS'),
col4_mainclass_user,
col5_subclass_user,
TO_CHAR(col6_userclass_date, 'DD-MM-YYYY HH24:MI:SS'),
col7_chnageUserId,
TO_CHAR(col8_Change_Time, 'DD-MM-YYYY HH24:MI:SS'),
col9_create_userid,
TO_CHAR(col10_create_time, 'DD-MM-YYYY HH24:MI:SS'),
NVL(col11_count,0),
col12_reasoncode,
col13_createflag,
col14_deleteflag,
col15_maxSerialNo,
col16_training_type,
col17_training_id
FROM CLASS_DETAIL_TABLE
WHERE col18_type = :HIS_type
AND col19_id = :HIS_id
for update;
|
这将返回以下错误代码:
清单 24. 示例错误
23819 SQL0511N The FOR UPDATE clause is not allowed because the
table specified by the cursor cannot be modified.
SQLSTATE=42829
|
DB2 不知道如何处理 SELECT 列表中的函数调用,而且如果想要更新结果集中的列,DB2 也不能决定如何做。在这种情况下,也许需要对 SQL 语句进行以下修改:
清单 25. 修改后的语句
EXEC SQL declare MY_CUR CURSOR for select
col1_main_system,
col2_sub_system,
TO_CHAR(col3_system_date, 'DD-MM-YYYY HH24:MI:SS'),
col4_mainclass_user,
col5_subclass_user,
TO_CHAR(col6_userclass_date, 'DD-MM-YYYY HH24:MI:SS'),
col7_chnageUserId,
TO_CHAR(col8_Change_Time, 'DD-MM-YYYY HH24:MI:SS'),
col9_create_userid,
TO_CHAR(col10_create_time, 'DD-MM-YYYY HH24:MI:SS'),
NVL(col11_count,0),
col12_reasoncode,
col13_createflag,
col14_deleteflag,
col15_maxSerialNo,
col16_training_type,
col17_training_id
FROM CLASS_DETAIL_TABLE
WHERE col18_type = :HIS_type
AND col19_id = :HIS_id
for update of
col1_main_system,
col2_sub_system,
col3_system_date,
col4_mainclass_user,
col5_subclass_user,
col6_userclass_date,
col7_chnageUserId,
col8_Change_Time,
col9_create_userid,
col10_create_time,
col11_count,
col12_reasoncode,
col13_createflag,
col14_deleteflag,
col15_maxSerialNo,
col16_training_type,
col17_training_id;
|
任务 7:将数据从 Oracle 对象移动到 DB2 对象的命令行实用工具
本文的下载部分提供了 OracleToDB2_DataMigrationScripts.zip 命令行实用工具和脚本,可以用来将 Oracle 数据迁移到 DB2。
此工具支持将表数据从 Oracle 数据库移动到 DB2 数据库。因此,可以运行此工具针对包含有效数据的等价 DB2 数据库测试移植后的 DB2 应用程序。此工具主要提供了两个实用程序:
-
geninput:该工具或脚本针对 Oracle 数据库运行,并提取对象(表)名称,然后形成一个对象名称列表。unload 实用程序稍后可以使用该列表从对象列表中指定的对象提取数据。
-
unload:此脚本为对象列表中的对象从指定数据库提取数据(该对象列表是由第一个实用程序 — geninput 准备的)。
因为此工具使用的是 JDBC 通用驱动程序,所以它可以用于任何其他数据库,只要用该数据库对它进行了测试。对于本文,我们使用 Oracle 数据库对其进行了成功测试。关于此工具的更多信息,请参阅 Migrate
from MySQL or PostgreSQL to DB2 Express-C(developerWorks,2006 年 9 月)。
简短来说,使用此工具的步骤如下:
- 确保 IBMExtract.jar 和 ojdbc14.jar(Oracle JDBC 驱动程序)位于类路径中。例如(在 UNIX 上):
清单 26. 类路径
export CLASSPATH=/home/db2inst1/java/IBMExtract.jar:/home/db2inst1/
java/ojdbc14.jar:$CLASSPATH
|
-
确保在目标计算机上使用了正确的 IBM JRE 版本(1.4.2 或 1.5)并导出其路径,以让工具知道如何定位到 Java™。
清单 27. 导出路径
export PATH=/opt/IBMJava2-142/jre/bin:$PATH
|
- 以下是运行此工具的两个步骤。第一步(运行 geninput),创建一个文件,作为工具上传所需表数据的输入。此文件包含 Oracle 数据库表的所有条目,因此需要手动删除不需要迁移的条目。如果未这样做,将会迁移所有表,包括 Oracle 系统表。
清单 28. 创建用于迁移的输入
[db2inst1@student java]$ cat geninput
#!/bin/bash
if [ "$1" = "" ] ; then
echo Usage : geninput dbname
exit 1
fi
DBVENDOR=oracle
DB2SCHEMA=$1
SERVER=192.69.79.92
DATABASE=$1
PORT=1521
DBUID=ilmsdb
DBPWD=pass
java -DINPUT_DIR=/work/java -cp $CLASSPATH ibm.GenInput $DBVENDOR $DB2SCHEMA $SERVER
$DATABASE $PORT $DBUID $DBPWD
|
- 第二步就是实际运行该工具(运行 unload)。
清单 29. 运行 unload 脚本
[db2inst1@student java]$ cat unload
#!/bin/bash
if [ "$1" = "" ] ; then
echo Usage : unload dbname
exit 1
fi
TABLES=/work/java/input/$1.tables
COLSEP=\~
DBVENDOR=oracle
NUM_THREADS=5
SERVER=192.69.79.92
DATABASE=$1
PORT=1521
DBUID=ilmsdb
DBPWD=pass
GENDDL=true
UNLOAD=true
FETCHSIZE=100
java -DOUTPUT_DIR=/work/java/output/$1 -cp $CLASSPATH ibm.GenerateExtract $TABLES $COLSEP
|--10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
$DBVENDOR $NUM_THREADS $SERVER $DATABASE $PORT $DBUID $DBPWD $GENDDL $UNLOAD $FETCHSIZE
|--10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
|
上传数据之后,可以转到输出目录,您将看到创建的所有脚本,包括可以实现数据迁移的 shell 脚本。
此工具只适用于数据迁移,不适用于其他对象的迁移,比如存储过程和触发器。要迁移其他对象,可以使用 Migration Toolkit。此工具并不能替代 Migration Toolkit。
注:此工具并不受 IBM 支持,因此不要期望获得对此工具的完整或专门支持。
可以在任何平台上运行此工具,只要拥有针对此平台的 IBM Java,并使用合适的 shell 脚本运行此工具。此处给出的示例脚本是针对 Linux 的。对于 Windows,请查阅 Windows shell 脚本的链接。
结束语
在迁移基于 Oracle 的应用程序以支持 DB2 for Linux, UNIX, and Windows 时会遇到一些技术问题,本文概述了这些技术问题,并提供了解决这些问题的工具。本文提供的 UDF 并未针对实际生产场景进行性能测试。可以使用 C/C++ 语言来编写这些 UDF,这样可以潜在地提升性能。理想情况下,应该将这些 UDF 用于概念证明,以及在应用程序中使用它们与 DB2 协作。如果想支持各种输入和输出参数,可以根据需要向各个 UDF 文件添加更多的函数。
免责声明
本文包含示例代码。IBM 授予您(“被许可方”)非专有的、版权免费的许可证使用该示例代码。然而,该示例代码是按原样提供的,并不承担任何形式的(不论明确还是隐含的)保证,包括对适销性、适用于特定用途或非侵权性的默示保证。IBM 及其许可方不对由于被许可方使用该软件而遭受的任何损失负责。任何情况下,无论损失如何发生,也不管责任条款怎样,IBM 或其许可方都不对因使用该软件或不能使用该软件所引起的收入减少、利润损失或数据丢失,或者直接的、间接的、特殊的、由此产生的、附带或惩罚性的损失负责,即使 IBM 已经被明确告知此类损害的可能性,也是如此。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 处理 DECODE 的脚本 | replace_decode.zip | 4KB | HTTP |
|---|
| 声明修正脚本 | esql_prep.zip | 6KB | HTTP |
|---|
| 数据迁移脚本 | OracleToDB2_DataMigrationScripts.zip | 1.4MB | HTTP |
|---|
| UDF 迁移脚本 | OracleToDB2UDFs.zip | 17KB | HTTP |
|---|
参考资料 学习
获得产品和技术
-
IBM Migration
Toolkit:将数据从各种源数据库迁移到 DB2 或者 Informix® Dynamic Server。
-
下载 DB2 9 的免费试用版本。
-
现在可以免费使用 DB2。下载 DB2 Express-C,这是 DB2 Express Edition 针对社区的免费版本,其中提供了与 DB2 Express Edition 相同的核心数据特性,是构建和部署应用程序的坚实基础。
- 下载
IBM 产品评估版 并
开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
关于作者  | 
|  | Ganesh R Gosavi 在 C、C++、UNIX、VC++,以及 DB2 for Linux, UNIX, and Windows 等领域具有广泛的开发经验。他在 IBM Software Labs, India 任高级 DB 顾问(资深软件工程师)时开始研究 DB2,并为所有亚太地区客户和业务合作伙伴提供支持。他与业务合作伙伴进行了广泛的合作,主要研究任务关键型 DB2 for Linux, UNIX, and Windows 解决方案、解决方案设计、应用程序性能调优、迁移、应用程序移植和开发。在为 IBM 工作之前,Ganesh 为 BMC Software 针对 DB2 UDB, V3.0 的 SmartDBA Performance Solution 做出了杰出贡献。 |
对本文的评价
|  |