本文主要结合之前一次oracle迁移达梦的项目,将碰到的问题以及一系列踩过的坑列举出来供大家参考,数据库版本是达梦7。(本文中涉及到的部分对象名已用sch1,tab1等方式替换)
迁移过程中失败任务数低于5%,大部分对象和数据都能够保证正常迁移。其中对于出错的任务,主要有导表和导函数包两部分。其中主要棘手的在与迁移函数包等对象时的问题。
2.1、通用问题
说明:对于直接使用DTS迁移失败的包,最好从源端获取完整的定义,重新核查报错原因 —— 避免编译通过后,报错 xxx 对象未在 xxx 包中定义!(包头和包体不匹配:包头定义了三个函数,在包体中,可能只有两个函数)
2.1.1、关键词问题
a、关键词问题的处理方式
i、在包中手工改写,如,原先有变量名字为dateDiff,手动改写为“dateDiff”——用双引号屏蔽关键字。
ii、在客户端的配置文件(dm_svc.conf)中,配置对应关键字,迁移包时,不做改动,让驱动自动为我们做上述i中的事情。如下图即为配置屏蔽 TRACE和ROWS两个关键字:
达梦中的关键字可以在达梦SQL使用手册中查询,也可以使用下面SQL查询:
select * from V$RESERVED_WORDS where RESERVED='Y';
2.1.2、中文符号的问题
a、达梦的PLSQL中,不支持中文标点、异常空白符。(即不是空格、\r、\n的其他的空白符、不支持全角符号 —— 当成字符串处理的除外)
b、该空白符通过客户端工具,调整编辑器为显示空白字符,可以分别异常空白符
所有正常的空格,均会显示为:虚点;如果是没有虚点的空白符,即为异常的空白符(常见于从邮件中、网页、QQ聊天框等中,直接复制出来的SQL)
2.2、表迁移问题
2.2.1、标度大于精度
问题描述:
/* 任务失败: 创建表"sch1"."tab1" */
/* 第8 行附近出现错误:
标度大于精度 */
/* 相关SQL语句: */
CREATE TABLE "sch1"."tab1"
(
"REGION_NAME" VARCHAR2(20) NULL,
"RATE" NUMBER(3,4) NULL,
"SCORE" VARCHAR2(20) NULL,
"CHECK_MONTH" VARCHAR2(20) NULL,
"INSERT_TIME" TIMESTAMP(0) NULL
);
解决方式:
达梦里面精度必须大于等于标度,精度是总长度,标度是小数位的长度,按道理小数位不可能超过总体长度的(但是oracle中并没有这个限制)。所以将NUMBER(3,4)改成NUMBER(4,4)即可。
2.2.2、局部唯一索引必须包含全部分区列
问题描述:
/* 任务失败: 创建表"sch1"."tab1" */
/* 第61 行附近出现错误:
局部唯一索引必须包含全部分区列 */
/* 相关SQL语句: */
...
解决方法:
该问题是因为达梦分区表要求,如果有主键,必须包含在分区列中,所以在迁移时需要设置映射关系,将分区列映射为主键,使用复合主键的方式。
2.2.3、记录超长
问题描述:
/* 任务失败: 从"sch1"."tab1"迁移数据到"sch1"."tab1" */
/* 记录超长 */
解决方法:
dm7页大小设置成8k时,所有字段加起来支持的最大长度是4k。所以这种问题需要在初始化数据时将页大小设置成16k或者32k。
或者使用STORAGE(USING LONG ROW) ,达梦在建表加上这个存储选项即可突破页大小限制。
2.2.4、Java heap space
问题描述:
/* 任务失败: 从"sch1"."tab1"迁移数据到"sch1"."tab1" */
/* Java heap space */
解决方法:
dts所在的机器内存不足导致。
2.2.5、违反协议
问题描述:
/* 任务失败: 从"sch1"."tab1"迁移数据到"sch1"."tab1" */
/* 违反协议 */
解决方法:
重新单独导入一遍即可。这个是oracle jdbc驱动报的错误,原因未知,可以试试在迁移工具里面指定最新的oracle jdbc驱动。
2.2.6、ORA-08103: 对象不再存在
问题描述:
/* 任务失败: 从"sch1"."tmp_tab1"迁移数据到"sch1"."tmp_tab1" */
/* ORA-08103: 对象不再存在
*/
解决方法:
该问题是因为导入的表是临时表,所以导入数据时会出现这种问题,重新导入即可解决。
2.3、函数包等对象问题
2.3.1、语法分析出错
问题描述:
/* 任务失败: 创建自定义类型TY_SOFT_WORK_EXPERT_OBJ */
/* 第 3 行, 第 3 列[SECTION]附近出现错误:
语法分析出错 */
解决方法:
这类问题是因为在创建自定义类型时使用了DM7中的保留字,只需要加上双引号即可。
2.3.2、无效的链接名
问题描述:
/* 任务失败: 创建视图"JTITSM"."CLOUD_PERF_CURRENT" */
/* 第6 行附近出现错误:
无效的链接名[JTITSM_CLOUD] */
解决方法:
该问题是因为使用到了dblink,而在达梦中dblink未创建导致。
我们可以通过建立到自身的dblink,暂时绕过依赖的问题,但是自身上依然要有dblink对应的表结构,没有的话,就建一个。达梦创建dblink方法可以参考:达梦创建dblink方法。
2.3.3、dbms_job无法使用
解决方法:
达梦中支持dbms_job包,但是使用前要先启用:
SQL> SP_INIT_JOB_SYS(1);
2.3.4、dbms_job.submit使用报错:参数不匹配
问题描述:
解决方法:
因为达梦中dbms_job.submit需要匹配5个参数,详细见达梦系统包使用手册。
2.3.5、无法修改与非键值保存表对应的列。
问题描述:
UPDATE
(
SELECT
A.STOP_PRODUCTION WRITE_STOP_PRODUCTION,
B.STOP_PRODUCTION READ_STOP_PRODUCTION
FROM
E_OUTPUT_HOUR A,
E_REPORT_HOUR B
WHERE
A.PE_ID = B.PE_ID
AND A.OUTPUT_ID = B.OUTPUT_ID
AND A.MEASURE_TIME = B.MEASURE_TIME
)
SET
WRITE_STOP_PRODUCTION = READ_STOP_PRODUCTION;
解决方法:
用A表去更新B表的数据,A表的关联条件必须为主键。因此修改为:
UPDATE
E_OUTPUT_HOUR A
SET
A.STOP_PRODUCTION=B.STOP_PRODUCTION
from
E_REPORT_HOUR B
WHERE
A.PE_ID = B.PE_ID
AND A.OUTPUT_ID = B.OUTPUT_ID
AND A.MEASURE_TIME = B.MEASURE_TIME;
2.3.6、无效的对象名:TAB
问题描述:
SELECT COUNT(*)
INTO V_CNT
FROM TAB
WHERE UPPER(TNAME) = 'HIS_NE_ALARM_MSG'
OR UPPER(TNAME) = 'HIS_NE_ALARM_LIST';
解决方法:
方法1:
SELECT COUNT(*)
INTO V_CNT
FROM all_tables
WHERE UPPER(TABLE_NAME) = 'HIS_NE_ALARM_MSG'
OR UPPER(TABLE_NAME) = 'HIS_NE_ALARM_LIST';
方法2:
SELECT COUNT(*)
INTO V_CNT
FROM sysobjects
WHERE "SUBTYPE$"='UTAB'
and UPPER(NAME) = 'HIS_NE_ALARM_MSG'
OR UPPER(NAME) = 'HIS_NE_ALARM_LIST';
这里建议使用方法二,直接查询sysobjects视图,DM中all_tables视图拼接的东西比较多。
2.3.7、无效的方法名: utl_encode.quoted_printable_encode
问题描述:
utl_raw.cast_to_varchar2(utl_encode.quoted_printable_encode(utl_raw.cast_to_raw(substr(subject,i)))) || '?=';
解决方法:
达梦中utl_encode包 只有BASE64_ENCODE 和BASE64_DECODE两种方法,这里使用 BASE64_DECODE替换掉quoted_printable_encode即可。
2.3.8、CURSOR无法作为查询项
问题描述:
procedure f1(p_staff_id staff.staff_id%type,
p_flow_id flow.flow_id %type,
flowCursor in out refCursor) is
begin
open flowCursor for
select b.tch_id,
b.TCH_NAME,
pkp_flow.getCurStaffName(b.STAFF_ID, b.person) as staff_name,
CURSOR (select d.tch_id, e.staff_name, d.TCH_NAME
from v_path c, v_tache d, staff e
where c.tch_id = d.TCH_ID
and (c.next_tch_id = b.tch_id or c.tch_id = b.TCH_ID)
and c.FLOW_ID = b.FLOW_ID
and d.STAFF_ID = e.staff_id)
from tache_notify_staffs a, v_tache b
where a.tch_id = b.tch_id
and a.flow_id = b.FLOW_ID
and a.staff_id = p_staff_id
and b.flow_path like p_flow_id || '/%'
and a.opinion is null;
end f1;
解决方法:
达梦中无法支持此写法,需在应用程序代码中实现修改,这里先注释掉。
2.3.9、UTL_SMTP包无法使用
解决方法:
这个包在达梦新版本中已经实现,打上补丁包,然后调用系统过程创建该包。
SQL> SP_CREATE_SYSTEM_PACKAGES (1,'UTL_SMTP');
DMSQL executed successfully
used time: 17.984(ms). Execute id is 259.
2.3.10、UTL_I18N包无法使用
解决方法:
达梦中没有这个包,因此无法使用,通过在达梦中自定义一个和oracle中同名的包,然后将需要用到的函数根据功能手动编写。主要用到RAWTOHEX、HEXTORAW这两个函数来实现。
2.3.11、DMBS_CRYPTO包无法使用
问题描述:
function encryptbyAes(input_string VARCHAR2,key VARCHAR2) return varchar2 IS
l_type pls_integer := dbms_crypto.encrypt_aes128 +
dbms_crypto.pad_pkcs5 +
dbms_crypto.chain_cbc;
l_encval raw(2000);
begin
l_encval := dbms_crypto.encrypt(
src=>utl_i18n.string_to_raw(input_string,'AL32UTF8'),
typ=>l_type,
key=>utl_i18n.string_to_raw(key,'AL32UTF8'));
dbms_output.put_line(l_encval);
return l_encval;
end;
解决方法
因为达梦中没有该包,上面oracle中该函数主要是实现将输入的内容进行加密,然后返回加密的结果,可以改写成:
function encryptbyAes(input_string VARCHAR2,key VARCHAR2) return varchar2 IS
ENCRYPTED_STRING VARCHAR2(2048);
l_encval VARCHAR2(2048);
begin
DBMS_OBFUSCATION_TOOLKIT.DESENCRYPT(
input_string, key, ENCRYPTED_STRING );
l_encval := ENCRYPTED_STRING;
dbms_output.put_line(l_encval);
return l_encval;
end;
2.3.12、执行环境堆栈空间不足
解决方法:
修改参数VM_STACK_SIZE = 1024后不会包该错误,但是会报嵌套层次太深的错误,当前尚未解决。
2.3.13、DMBS_SQL包使用问题
解决方法:
达梦DMBS_SQL包要使用DMBS_SQL.describe_columns获取列信息前需要先使用DBMS_SQL.execute将游标执行。
整个迁移过程来看,大部分问题都存在于函数包等对象上,有些可以直接解决,如关键字,而有些涉及到oracle中系统包在达梦中不存在的问题就需要自己想办法去解决,例如修改代码。
除此之外,迁移完成后可能会出现部分SQL在oracle运行速度正常,但是在达梦中运行缓慢的情况,这种情况我们还需要去调整数据库参数和优化SQL。