我有CSV字符串100.01,200.02,300.03,我需要传递给Oracle中的PL / SQL存储过程。 在proc中,我需要将这些值插入表中的Number列。
为此,我从这里得到了一个有效的方法:
How to best split csv strings in oracle 9i
[2]逐级使用SQL连接。]。
现在,我有另一项要求。 我需要传递2个CSV字符串[长度相等]作为PL / SQL存储过程的输入。我需要打破这个字符串并将两个CSV字符串中的每个值插入表格中的两个不同列。请你让我知道如何去做吗?
CSV输入示例: mystring varchar2(2000):='0.75,0.64,0.56,0.45';
myAmount varchar2(2000):='0.25,0.5,0.65,0.8';
myString值将进入A列,myAmount值进入表中的B列。
你能告诉我如何实现这个目标吗?
感谢。
答案 0 :(得分:8)
这是一个很好的解决方案:
FUNCTION comma_to_table(iv_raw IN VARCHAR2) RETURN dbms_utility.lname_array IS
ltab_lname dbms_utility.lname_array;
ln_len BINARY_INTEGER;
BEGIN
dbms_utility.comma_to_table(list => iv_raw
,tablen => ln_len
,tab => ltab_lname);
FOR i IN 1 .. ln_len LOOP
dbms_output.put_line('element ' || i || ' is ' || ltab_lname(i));
END LOOP;
RETURN ltab_lname;
END;
来源: CSV - comma separated values - and PL/SQL (链接不再有效)
答案 1 :(得分:8)
这应该做你想要的......它假设你的列表永远只是数字。如果不是这种情况,只需将对DBMS_SQL.NUMBER_TABLE的引用更改为适用于所有数据的表类型:
CREATE OR REPLACE PROCEDURE insert_from_lists(
list1_in IN VARCHAR2,
list2_in IN VARCHAR2,
delimiter_in IN VARCHAR2 := ','
)
IS
v_tbl1 DBMS_SQL.NUMBER_TABLE;
v_tbl2 DBMS_SQL.NUMBER_TABLE;
FUNCTION list_to_tbl
(
list_in IN VARCHAR2
)
RETURN DBMS_SQL.NUMBER_TABLE
IS
v_retval DBMS_SQL.NUMBER_TABLE;
BEGIN
IF list_in is not null
THEN
/*
|| Use lengths loop through the list the correct amount of times,
|| and substr to get only the correct item for that row
*/
FOR i in 1 .. length(list_in)-length(replace(list_in,delimiter_in,''))+1
LOOP
/*
|| Set the row = next item in the list
*/
v_retval(i) :=
substr (
delimiter_in||list_in||delimiter_in,
instr(delimiter_in||list_in||delimiter_in, delimiter_in, 1, i ) + 1,
instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i+1) - instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i) -1
);
END LOOP;
END IF;
RETURN v_retval;
END list_to_tbl;
BEGIN
-- Put lists into collections
v_tbl1 := list_to_tbl(list1_in);
v_tbl2 := list_to_tbl(list2_in);
IF v_tbl1.COUNT <> v_tbl2.COUNT
THEN
raise_application_error(num => -20001, msg => 'Length of lists do not match');
END IF;
-- Bulk insert from collections
FORALL i IN INDICES OF v_tbl1
insert into tmp (a, b)
values (v_tbl1(i), v_tbl2(i));
END insert_from_lists;
答案 2 :(得分:6)
我使用apex_util.string_to_table来解析字符串,但如果您愿意,可以使用不同的解析器。然后您可以插入数据,如下例所示:
declare
myString varchar2(2000) :='0.75, 0.64, 0.56, 0.45';
myAmount varchar2(2000) :='0.25, 0.5, 0.65, 0.8';
v_array1 apex_application_global.vc_arr2;
v_array2 apex_application_global.vc_arr2;
begin
v_array1 := apex_util.string_to_table(myString, ', ');
v_array2 := apex_util.string_to_table(myAmount, ', ');
forall i in 1..v_array1.count
insert into mytable (a, b) values (v_array1(i), v_array2(i));
end;
Apex_util可从Oracle 10G开始提供。在此之前,它被称为htmldb_util,默认情况下未安装。如果你不能使用它,你可以使用我多年前写的字符串解析器并发布here。
答案 3 :(得分:3)
我不确定这是否适合你的oracle版本。在我的10g上,我可以使用流水线表功能:
set serveroutput on
create type number_list as table of number;
-- since you want this solution
create or replace function split_csv (i_csv varchar2) return number_list pipelined
is
mystring varchar2(2000):= i_csv;
begin
for r in
( select regexp_substr(mystring,'[^,]+',1,level) element
from dual
connect by level <= length(regexp_replace(mystring,'[^,]+')) + 1
)
loop
--dbms_output.put_line(r.element);
pipe row(to_number(r.element, '999999.99'));
end loop;
end;
/
insert into foo
select column_a,column_b from
(select column_value column_a, rownum rn from table(split_csv('0.75, 0.64, 0.56, 0.45'))) a
,(select column_value column_b, rownum rn from table(split_csv('0.25, 0.5, 0.65, 0.8'))) b
where a.rn = b.rn
;
答案 4 :(得分:3)
CREATE OR REPLACE PROCEDURE insert_into (
p_errcode OUT NUMBER,
p_errmesg OUT VARCHAR2,
p_rowsaffected OUT INTEGER
)
AS
v_param0 VARCHAR2 (30) := '0.25,2.25,33.689, abc, 99';
v_param1 VARCHAR2 (30) := '2.65,66.32, abc-def, 21.5';
BEGIN
FOR i IN (SELECT COLUMN_VALUE
FROM TABLE (SPLIT (v_param0, ',')))
LOOP
INSERT INTO tempo
(col1
)
VALUES (i.COLUMN_VALUE
);
END LOOP;
FOR i IN (SELECT COLUMN_VALUE
FROM TABLE (SPLIT (v_param1, ',')))
LOOP
INSERT INTO tempo
(col2
)
VALUES (i.COLUMN_VALUE
);
END LOOP;
END;
答案 5 :(得分:2)
create or replace procedure pro_ss(v_str varchar2) as
v_str1 varchar2(100);
v_comma_pos number := 0;
v_start_pos number := 1;
begin
loop
v_comma_pos := instr(v_str,',',v_start_pos);
if v_comma_pos = 0 then
v_str1 := substr(v_str,v_start_pos);
dbms_output.put_line(v_str1);
exit;
end if;
v_str1 := substr(v_str,v_start_pos,(v_comma_pos - v_start_pos));
dbms_output.put_line(v_str1);
v_start_pos := v_comma_pos + 1;
end loop;
end;
/
call pro_ss('aa,bb,cc,dd,ee,ff,gg,hh,ii,jj');
outout:aa bb cc dd ee ff gg hh ii jj
答案 6 :(得分:-1)
已经提供了许多好的解决方案。但是,如果他的文本以非常简单的逗号分隔格式或类似方式提供,并且速度很重要,那么我为您提供了一个TABLE
函数的解决方案(在PL / SQL中)。我还提供了一些其他解决方案的概要。
答案 7 :(得分:-1)
至于connect by
用例,这种方法对您有用:
select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level)
from dual
connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null;