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.

22
demandé sur Community 2010-10-23 18:09:32

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; 
8
répondu Craig 2010-10-27 17:06:56

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)

7
répondu Michael Goldshteyn 2015-06-16 10:29:19

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 .

6
répondu Tony Andrews 2010-10-27 11:42:35

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
;
3
répondu christian 2010-10-27 10:32:44
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;
3
répondu Amarender 2012-07-27 10:28:18

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

1
répondu Harikumar.S 2015-08-05 06:27:41

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 .

-1
répondu YoYo 2015-06-16 16:15:34

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;
-1
répondu Ravindra Vemula... 2015-11-12 08:25:10