达梦存储过程的语法与oracle的高度类似,但有好多细节仍是有差别。我在此次项目迁移中踩过很多小坑,在这里给你们分享一下。sql
说明一下,我用的版本是达梦8,迁移时碰到的问题有些我已经反馈给达梦的官方群管理员,估计之后会有修复。数据库
达梦的rpad函数,计算中文时永远是认为一个中文字符中两个字节,即便数据库设置的字符集是utf8(目前就发现rpad/lpad函数有这个问题,其它字符串函数都能正确识别,当字符集是utf8时能识别出来一个字符中3个字节)编程
测试代码:数组
select rpad('我是hch', 6), lengthb('我是hch') from dual -- 达梦输出"我是hc 9" union all select rpad('我是hch', 5), length('我是hch') from dual -- 达梦输出"我是h 5" union all select rpad('我是hch', 3), length('我是hch') from dual; -- 达梦输出"我 5"
这个问题达梦的工做人员说之后会修复,目前个人解决方法是本身写一个rpad函数oracle
function rpad_dm(string varchar2, padded_length number, pad_string varchar2 := ' ') return varchar2 IS v_len number := lengthb(string); BEGIN dbms_output.put_line('v_len - padded_length = ' ); if padded_length < v_len THEN return substrb(string, 1, padded_length); --若是输入长度小于原字符串长度,则调用substrb截断 elsif padded_length = v_len THEN return string; --若是长度相等直接返回原串便可 else return string || rpad(' ', padded_length - v_len, pad_string); --若是长度大于原字符串,则在后面补空格 end if; END;
通常编程语言都会提供短路功能,在计算与或逻辑时,若是前半段逻辑已经能肯定真假时,后半段逻辑不会执行。编程语言
plsql里面也实现了短路功能,咱们通常会利用这个特性减小一些代码,例如先判断变量是否为空,若是不为空再使用变量作运算:函数
if (var is not null and va.exists('error') ) then dbms_output.put_line('yes'); fi;oop
但在达梦的存储过程,短路却没有实现。上面的代码无论var是否为空,都会进行va.exists('error')这个逻辑。若是不幸var的变量是空的,就会致使运行异常。测试
测试代码1:调试
dbms_output.enable; declare v_flag boolean; begin -- 请问这个存储过程执行异常,报"非法的参数数据" 是否是达梦的bug oracle下是能够正常运行的 -- 仍是有什么设置可让存储过程正常执行 if (to_number('1') != 1) and to_number('abc') = 1 then dbms_output.put_line('yes'); end if; dbms_output.put_line('ok'); end;
测试代码2:
dbms_output.enable; declare TYPE TEST_RPT_LIST IS TABLE OF number INDEX BY PLS_INTEGER; o_demo_list TEST_RPT_LIST; i_report_id number := 17410491; BEGIN -- 验证达梦8 if短路 select 1 BULK COLLECT INTO o_demo_list from dual; dbms_output.put_line('o_demo_list(1) = ' || o_demo_list(1)); if o_demo_list(1) = 1 or o_demo_list(2) = 2 THEN -- or 短路没问题 dbms_output.put_line('or yes'); -- or yes能正常输出 end if; if o_demo_list(1) != 1 and o_demo_list(2) = 2 THEN -- o_demo_list(1) != 1 不成立 为何还要执行o_demo_list(2) = 2判断 dbms_output.put_line('and yes'); -- 这里永远不该该输出 end if; dbms_output.put_line('done'); -- 走不到done EXCEPTION WHEN no_data_found THEN dbms_output.put_line('no_data_found tbl_demo_tab ' || 'ID ' || to_char(i_report_id)); WHEN OTHERS THEN -- RAISE; dbms_output.put_line('err:' || sqlcode || sqlerrm); END;
不仅是if有短路问题,decode,case when等相似的都会有短路问题。
"case 判断 when 表达式1 else 表达式2 end" 在oracle是若是条件成立则执行条件1并返回其值,而在达梦是同时执行表达式1和表达式2,并根据判断结果返回一个值。
解决方法是不要偷懒,不依赖短路实现,多写几个if判断,或者把decode拆成多个if else语句。
oracle的table数组变量的赋值,默认是值复制(即深拷贝),而达梦默认是引用复制(即浅拷贝)。
也就是说在oracle使用 tmpArr := arr (tmpArr 和arr 都是数组),而后对这个tmpArr操做,不会影响arr的值,而在达梦,修改tmpArr数组元素的内容就是在修改arr
测试代码
FOR vv IN 1 .. 5 -- crontab 初始化赋值 LOOP CASE vv WHEN 1 THEN v_obj.minutes := tmpArr; WHEN 2 THEN v_obj.hours := tmpArr; WHEN 3 THEN v_obj.days := tmpArr; WHEN 4 THEN v_obj.months := tmpArr; WHEN 5 THEN v_obj.weeks := tmpArr; END CASE; END LOOP;
在oracle对v_obj这样赋值后,v_obj.minutes和v_obj.hours是两个不一样的变量,分别对两个变量修改,相互之间不会出现干扰。而在达梦8,v_obj下面全部变量都指向同一个数组,对v_obj任意一个成员修改,都会同时影响其它成员的值。
解决方法是本身写一个数组拷贝函数,例如这样:
function copy1kList(v_input t_str_list) return t_str_list IS v_tmplist t_str_list; v_ind PLS_INTEGER; begin --TYPE t_str_list IS TABLE OF VARCHAR2(1024) INDEX BY PLS_INTEGER; if v_input.count > 0 then /* //在v_input里面的元素不连续时,这样会有bug for vv in v_input.first .. v_input.last LOOP v_tmplist(vv) := v_input(vv); end loop; */ v_ind = v_input.first; while v_ind is not null loop v_tmplist(v_ind) := v_input(v_ind); v_ind = v_input.next(v_ind); end loop; end if; return v_tmplist; end;
使用这个函数代替数组变量赋值就能维持代码行为与oracle一致。
今天暂时先分享这三个问题,后面有时间再整理其它坑。这些坑比较隐蔽,花了我很多时间调试才发现,我把它们总结出来,但愿能对你有所帮助。