前言
最近有个项目用到了Oracle的数据库,而且有个历史数据需要修复,将A字段相同的B字段值合并为一行并以逗号分隔,但是B字段不能重复,如果单纯使用Oracle的INSTR函数,当出现值“aa”和值“aab”时,值“aa”可能会被判断与“aab”重复,因此我想到一个在java中常用的解决方法是先将字符串按逗号分隔到数组的每个元素,再遍历该数组是否存在指定值即可,并可由此学习一下在Oracle的存储过程中如何实现数组的效果。
程序示例
declare
-- 声明一个数组类型,疑问点:当前行能否抽离让其他存储过程不再需要写这行代码
type TYPE_ARRAY is table of varchar2(20) index by binary_integer;
-- 定义一个数组变量
myArray TYPE_ARRAY;
-- 一个包含逗号的字符串示例
str varchar2(4000);
v_temp_out_str varchar2(100);
-- 判断是否找到值的布尔变量
booleanVar boolean;
findKey varchar2(10);
begin
-- 示例字符串, 将以下字符串转换为数组
str := 'aaa,bbb,ccc,aab,aa';
-- 遍历将str按逗号分割放到数组中
while instr(str, ',') > 0 OR LENGTH(str) > 0 LOOP
-- 已经没有逗号表示只剩下最后一个值
IF instr(str, ',') = 0 THEN
myArray(myArray.count + 1) := str;
-- oracle存储过程的输出语句
-- dbms_output.put_line(str);
str := '';
ELSE
v_temp_out_str := substr(str, 0, instr(str, ',') - 1);
myArray(myArray.count + 1) := v_temp_out_str;
-- dbms_output.put_line(v_temp_out_str);
-- 每次遍历后修改str的值, 防止死循环
str := substr(str, instr(str, ',') + 1);
END IF;
end loop;
dbms_output.put_line('数组长度为:' || myArray.count);
-- 在数组中找到aa字符串
findKey := 'aa';
FOR i IN 1..myArray.count LOOP
IF myArray(i) = findKey THEN
booleanVar := true;
dbms_output.put_line('找到你了:' || myArray(i));
END IF;
END LOOP;
end;
(不知道为什么上面这段程序多了while那一块之后关键字就不变色)
反思
需求是完成了,但是在基于开发的角度来看,每次声明数组都要自定义一遍程序会显得很冗余,能否将声明数组变量的那一行作为所有存储过程都能使用的类型呢?经过各种搜索资料后发现在Oracle中可以自定义类型。
-- 创建的语句
CREATE OR REPLACE TYPE TYPE_ARRAY AS TABLE OF varchar2(20);
-- 删除的语句
drop type TYPE_ARRAY;
这时候在存储过程中定义数组变量就可以这样定义
declare
myArray TYPE_ARRAY:=TYPE_ARRAY();
begin
-- 每次使用时需要先扩展长度后才能使用,每扩展一次myArray.count会+1,需要注意下标越界
myArray.extend();--括号中可传入数值表示一次性扩展指定长度
end;
如果使用这种方式,原来的程序中给数组赋值时就需要作出下列的变化
注:变化点在myArray.count和初始化
declare
-- 初始化一个数组变量
myArray TYPE_ARRAY := TYPE_ARRAY();
-- 一个包含逗号的字符串示例
str varchar2(4000);
v_temp_out_str varchar2(100);
-- 判断是否找到值的布尔变量
booleanVar boolean;
findKey varchar2(10);
begin
-- 示例字符串, 将以下字符串转换为数组
str := 'aaa,bbb,ccc,aab,aa';
-- 遍历将str按逗号分割放到数组中
while instr(str, ',') > 0 OR LENGTH(str) > 0 LOOP
-- 每次循环对数组进行扩展
vvv.extend();
-- 已经没有逗号表示只剩下最后一个值
IF instr(str, ',') = 0 THEN
-- 不再使用原来的myArray.count+1否则会出现下标越界
myArray(myArray.count) := str;
-- oracle存储过程的输出语句
-- dbms_output.put_line(str);
str := '';
ELSE
v_temp_out_str := substr(str, 0, instr(str, ',') - 1);
-- 不再使用原来的myArray.count+1否则会出现下标越界
myArray(myArray.count) := v_temp_out_str;
-- dbms_output.put_line(v_temp_out_str);
-- 每次遍历后修改str的值, 防止死循环
str := substr(str, instr(str, ',') + 1);
END IF;
end loop;
dbms_output.put_line('数组长度为:' || myArray.count);
-- 在数组中找到aa字符串
findKey := 'aa';
FOR i IN 1..myArray.count LOOP
IF myArray(i) = findKey THEN
booleanVar := true;
dbms_output.put_line('找到你了:' || myArray(i));
END IF;
END LOOP;
end;
总结
由于没有专门学习过Oracle存储过程,由此也出现了很多让人哭笑不得的小问题,而在寻找解决方案时也花费了大量的时间在资料搜索上,写这次存储过程又一次发现,书到用时方恨少,现在到了工作时最好的学习方式应该还是查看前人的存储过程边学边写,闲暇时间也应积累这方面的知识才行。
才疏学浅,如文中有错误,感谢大家指出。