web-dev-qa-db-fra.com

Existe-t-il une fonction permettant de scinder une chaîne en PL / SQL?

Je dois écrire une procédure pour normaliser un enregistrement comportant plusieurs jetons concaténés par un caractère. Je dois obtenir ces jetons qui divisent la chaîne et les insérer comme un nouvel enregistrement dans une table. Oracle a-t-il quelque chose comme une fonction "scindée"?

46
Sam

Vous devez rouler le vôtre. Par exemple.,

/* from :http://www.builderau.com.au/architect/database/soa/Create-functions-to-join-and-split-strings-in-Oracle/0,339024547,339129882,00.htm

select split('foo,bar,Zoo') from dual;
select * from table(split('foo,bar,Zoo'));

pipelined function is SQL only (no PL/SQL !)
*/

create or replace type split_tbl as table of varchar2(32767);
/
show errors

create or replace function split
(
    p_list varchar2,
    p_del varchar2 := ','
) return split_tbl pipelined
is
    l_idx    pls_integer;
    l_list    varchar2(32767) := p_list;
    l_value    varchar2(32767);
begin
    loop
        l_idx := instr(l_list,p_del);
        if l_idx > 0 then
            pipe row(substr(l_list,1,l_idx-1));
            l_list := substr(l_list,l_idx+length(p_del));

        else
            pipe row(l_list);
            exit;
        end if;
    end loop;
    return;
end split;
/
show errors;

/* An own implementation. */

create or replace function split2(
  list in varchar2,
  delimiter in varchar2 default ','
) return split_tbl as
  splitted split_tbl := split_tbl();
  i pls_integer := 0;
  list_ varchar2(32767) := list;
begin
  loop
    i := instr(list_, delimiter);
    if i > 0 then
      splitted.extend(1);
      splitted(splitted.last) := substr(list_, 1, i - 1);
      list_ := substr(list_, i + length(delimiter));
    else
      splitted.extend(1);
      splitted(splitted.last) := list_;
      return splitted;
    end if;
  end loop;
end;
/
show errors

declare
  got split_tbl;

  procedure print(tbl in split_tbl) as
  begin
    for i in tbl.first .. tbl.last loop
      dbms_output.put_line(i || ' = ' || tbl(i));
    end loop;
  end;

begin
  got := split2('foo,bar,Zoo');
  print(got);
  print(split2('1 2 3 4 5', ' '));
end;
/
16
RedFilter

Il y a apex_util.string_to_table - voir ma réponse à ceci question .

En outre, avant l’existence de la fonction ci-dessus, j’avais déjà posté une solution ici sur mon blog .

Mise à jour

Dans les versions ultérieures de APEX, apex_util.string_to_table est obsolète , et une fonction similaire apex_string.split est préférable.

35
Tony Andrews

Si APEX_UTIL N'est pas disponible, vous avez une solution en utilisant REGEXP_SUBSTR().

Inspiré de http://nuijten.blogspot.fr/2009/07/splitting-comma-delimited-string-regexp.html :

DECLARE
  I INTEGER;
  TYPE T_ARRAY_OF_VARCHAR IS TABLE OF VARCHAR2(2000) INDEX BY BINARY_INTEGER;
  MY_ARRAY T_ARRAY_OF_VARCHAR;
  MY_STRING VARCHAR2(2000) := '123,456,abc,def';
BEGIN
  FOR CURRENT_ROW IN (
    with test as    
      (select MY_STRING from dual)
      select regexp_substr(MY_STRING, '[^,]+', 1, rownum) SPLIT
      from test
      connect by level <= length (regexp_replace(MY_STRING, '[^,]+'))  + 1)
  LOOP
    DBMS_OUTPUT.PUT_LINE(CURRENT_ROW.SPLIT);
    MY_ARRAY(MY_ARRAY.COUNT) := CURRENT_ROW.SPLIT;
  END LOOP;
END;
/
16
Frosty Z

Cela ne fonctionne que dans Oracle 10G et supérieur.

En gros, vous utilisez regex_substr pour scinder la chaîne.

https://blogs.Oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement

10
Meower68

S'il vous plaît trouver ensuite un exemple que vous pourriez trouver utile

- 1ère sous-chaîne

select substr('alfa#bravo#charlie#delta', 1,  
  instr('alfa#bravo#charlie#delta', '#', 1, 1)-1) from dual;

--2ème sous-chaîne

select substr('alfa#bravo#charlie#delta', instr('alfa#bravo#charlie#delta', '#', 1, 1)+1,  
  instr('alfa#bravo#charlie#delta', '#', 1, 2) - instr('alfa#bravo#charlie#delta', '#', 1, 1) -1) from dual;

--3ème sous chaîne

select substr('alfa#bravo#charlie#delta', instr('alfa#bravo#charlie#delta', '#', 1, 2)+1,  
  instr('alfa#bravo#charlie#delta', '#', 1, 3) - instr('alfa#bravo#charlie#delta', '#', 1, 2) -1) from dual;

--4ème sous-chaîne

select substr('alfa#bravo#charlie#delta', instr('alfa#bravo#charlie#delta', '#', 1, 3)+1) from dual;

Meilleures salutations

Emanuele

5
Emanuele

Vous pouvez utiliser une combinaison de SUBSTR et INSTR comme suit:

Exemple de chaîne: field = 'DE124028#@$1048708#@$000#@$536967136#@$'

Le séparateur étant # @ $.

Pour obtenir le '1048708' par exemple:

Si le champ est de longueur fixe (7 ici):

substr(field,instr(field,'#@$',1,1)+3,7)

Si le champ est de longueur variable:

substr(field,instr(field,'#@$',1,1)+3,instr(field,'#@$',1,2) - (instr(field,'#@$',1,1)+3)) 

Vous devriez probablement vous pencher sur les fonctions SUBSTR et INSTR pour plus de flexibilité.

5
Mo Chahal

Vous pouvez utiliser regexp_substr (). Exemple:

create or replace type splitTable_Type is table of varchar2(100);

declare
    l_split_table splitTable_Type;
begin
  select
      regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level)
  bulk collect into
      l_split_table
  from dual
  connect by
      regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null;
end;

La requête parcourt la chaîne séparée par des virgules, recherche la virgule (,), puis la scinde en la considérant comme un délimiteur. Il renvoie la chaîne sous forme de ligne chaque fois qu'il frappe un délimiteur.

level dans l'instruction regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) fait référence à une pseudocolonne dans Oracle utilisée dans une requête hiérarchique pour identifier le niveau de hiérarchie au format numérique: niveau de connexion par

5
ferralucho
function numinstr(p_source in varchar2,p_token in varchar2)
return pls_integer
is
    v_occurrence pls_integer := 1;
    v_start pls_integer := 1;
    v_loc pls_integer;
begin
    v_loc:=instr(p_source, p_token, 1, 1);
    while v_loc > 0 loop
      v_occurrence := v_occurrence+1;
      v_start:=v_loc+1;
      v_loc:=instr(p_source, p_token, v_start, 1);
    end loop;
    return v_occurrence-1;
end numinstr;
  --
  --
  --
  --
function get_split_field(p_source in varchar2,p_delim in varchar2,nth pls_integer)
return varchar2
is
    v_num_delims pls_integer;
    first_pos pls_integer;
    final_pos pls_integer;
    len_delim pls_integer := length(p_delim);
    ret_len pls_integer;
begin
    v_num_delims := numinstr(p_source,p_delim);
    if nth < 1 or nth > v_num_delims+1 then
      return null;
    else
      if nth = 1 then
        first_pos := 1;
      else
        first_pos := instr(p_source, p_delim, 1, nth-1) + len_delim;
      end if;
      if nth > v_num_delims then
        final_pos := length(p_source);
      else
        final_pos := instr(p_source, p_delim, 1, nth) - 1;
      end if;
      ret_len := (final_pos - first_pos) + 1;
      return substr(p_source, first_pos, ret_len);
    end if;
end get_split_field;
1
Carl