Séparer les chaînes séparées par des virgules dans un PL / SQL stocké proc
j'ai la chaîne CSV 100.01,200.02,300.03 que je dois passer à une procédure PL/SQL stockée dans Oracle. À l'intérieur du proc,je dois insérer ces valeurs dans une colonne de nombre dans le tableau.
pour ceci, j'ai eu une approche de travail d'ici:
comment diviser au mieux les chaînes csv dans oracle 9i
[2] utilisant SQL's connect par niveau.].
maintenant, j'ai une autre exigence. J'ai besoin de passer 2 chaînes CSV[égales en longueur] comme entrée à PL / SQL stocké proc.Et, je dois briser cette chaîne de caractères et insérer chaque valeur de deux chaînes CSV dans deux colonnes différentes dans la table.Pourriez-vous s'il vous plaît laissez-moi savoir comment vous y prendre?
exemple d'entrées CSV: myString varchar2(2000):='0.75, 0.64, 0.56, 0.45';
myAmount varchar2(2000):= '0.25, 0.5, 0.65, 0.8';
les valeurs mystrantes iraient dans la colonne A et les valeurs myAmount dans la Colonne B du tableau.
pourriez-vous me dire comment y parvenir?
Merci.
8 réponses
cela devrait faire ce que vous cherchez.. Il suppose que votre liste sera toujours seulement des chiffres. Si ce n'est pas le cas, changez simplement les références à DBMS_SQL.NUMBER_TABLE à un type de table qui fonctionne pour toutes vos données:
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;
Voici une bonne solution:
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;
Source: CSV - valeurs séparées par des virgules - et PL/SQL (lien non valide)
j'utilise apex_util.string_to_table analyser les chaînes, mais vous pouvez utiliser un autre analyseur si vous le souhaitez. Ensuite, vous pouvez insérer les données comme dans cet exemple:
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 est disponible à partir D'Oracle 10G. Avant cela, il était appelé htmldb_util et n'était pas installé par défaut. Si vous ne pouvez pas l'utiliser, vous pouvez utiliser l'analyseur de cordes que j'ai écrit il y a de nombreuses années et posté ici .
Je ne suis pas sûr que cela corresponde à votre version oracle. Sur mon 10g je peux utiliser des fonctions de table pipelined:
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
;
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;
créer ou remplacer procedure pro_ss(v_str varchar2)
par
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
beaucoup de bonnes solutions ont déjà été fournies. Cependant, si le texte est fourni dans un format délimité par une virgule très simple ou similaire, et que la vitesse est importante, alors j'ai pour vous une solution avec une fonction TABLE
(en PL/SQL). J'ai également fourni une liste de quelques autres solutions.
s'il vous Plaît voir plus sur le Entrée de Blog sur l'Analyse d'un fichier CSV en plusieurs colonnes .
comme pour le cas d'utilisation connect by
, cette approche devrait fonctionner pour vous:
select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level)
from dual
connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null;