Oracle PL/SQL 中有分割字符串的函数吗?

发布于 2024-09-19 10:21:02 字数 92 浏览 7 评论 0原文

我需要编写一个过程来规范化具有由一个字符连接的多个标记的记录。我需要获取这些标记来分割字符串,并将每个标记作为新记录插入表中。 Oracle有类似“split”的功能吗?

I need to write a procedure to normalize a record that have multiple tokens concatenated by one char. I need to obtain these tokens splitting the string and insert each one as a new record in a table. Does Oracle have something like a "split" function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

梦巷 2024-09-26 10:21:02

apex_util.string_to_table - 请参阅我对此的回答 问题

另外,在上述功能存在之前,我曾经发布过一个解决方案 在我的博客上

更新

在 APEX 的更高版本中,apex_util.string_to_table已弃用,以及类似的函数首选 apex_string.split

There is apex_util.string_to_table - see my answer to this question.

Also, prior to the existence of the above function, I once posted a solution here on my blog.

Update

In later versions of APEX, apex_util.string_to_table is deprecated, and a similar function apex_string.split is preferred.

染墨丶若流云 2024-09-26 10:21:02

如果 APEX_UTIL 不可用,您可以使用 REGEXP_SUBSTR() 解决方案。

灵感来自 http://nuijten.blogspot.fr/2009/07/splitting-逗号分隔字符串-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;
/

If APEX_UTIL is not available, you have a solution using REGEXP_SUBSTR().

Inspired from 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;
/
听闻余生 2024-09-26 10:21:02

你必须自己动手。例如,

/* 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;
/

You have to roll your own. E.g.,

/* 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;
/
笑红尘 2024-09-26 10:21:02

您可以使用 regexp_substr()。示例:

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;

查询迭代逗号分隔的字符串,搜索逗号 (,),然后通过将逗号视为分隔符来分割字符串。每当遇到分隔符时,它都会将字符串作为一行返回。

语句regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level)中的level指的是Oracle中使用的伪列在分层查询中以数字格式标识层次结构级别:连接方式中的级别

You can use regexp_substr(). Example:

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;

The query iterates through the comma separated string, searches for the comma (,) and then splits the string by treating the comma as delimiter. It returns the string as a row, whenever it hits a delimiter.

level in statement regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) refers to a pseudocolumn in Oracle which is used in a hierarchical query to identify the hierarchy level in numeric format: level in connect by

朦胧时间 2024-09-26 10:21:02

这仅适用于 Oracle 10G 及更高版本。

基本上,您使用 regex_substr 对字符串进行分割。


已存档链接: http://web. archive.org/web/20170304121704/https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement

代码:

select * from emp where ename in (
  select regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) from dual
  connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null 
);

This only works in Oracle 10G and greater.

Basically, you use regex_substr to do a split on the string.

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


Edit:

Archived link: http://web.archive.org/web/20170304121704/https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement

The code:

select * from emp where ename in (
  select regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) from dual
  connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null 
);
憧憬巴黎街头的黎明 2024-09-26 10:21:02

您可以使用 SUBSTR 和 INSTR 的组合,如下所示:

示例字符串: field = 'DE124028#@$1048708#@$000#@$536967136#@$'

分隔符为 #@$。

例如,要获取“1048708”:

如果该字段具有固定长度(此处为 7):

substr(field,instr(field,'#@

如果该字段具有可变长度:

substr(field,instr(field,'#@

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,1)+3,7)

如果该字段具有可变长度:


您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,1)+3,instr(field,'#@

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,1)+3,7)

如果该字段具有可变长度:

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,2) - (instr(field,'#@

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,1)+3,7)

如果该字段具有可变长度:

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,1)+3))

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

,1,1)+3,7)

如果该字段具有可变长度:

您可能应该研究 SUBSTR 和 INSTR 函数以获得更大的灵活性。

You could use a combination of SUBSTR and INSTR as follows :

Example string : field = 'DE124028#@$1048708#@$000#@$536967136#@$'

The seperator being #@$.

To get the '1048708' for example :

If the field is of fixed length ( 7 here ) :

substr(field,instr(field,'#@

If the field is of variable length :

substr(field,instr(field,'#@

You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,1)+3,7)

If the field is of variable length :


You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,1)+3,instr(field,'#@

You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,1)+3,7)

If the field is of variable length :

You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,2) - (instr(field,'#@

You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,1)+3,7)

If the field is of variable length :

You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,1)+3))

You should probably look into SUBSTR and INSTR functions for more flexibility.

,1,1)+3,7)

If the field is of variable length :

You should probably look into SUBSTR and INSTR functions for more flexibility.

要走干脆点 2024-09-26 10:21:02

请查找下一个您可能会发现有用的示例

--1st 子串

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

--2nd 子串

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;

--3rd 子串

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;

--4th 子串

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

最好的问候

Emanuele

Please find next an example you may find useful

--1st substring

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

--2nd substring

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;

--3rd substring

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;

--4th substring

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

Best regards

Emanuele

街角迷惘 2024-09-26 10:21:02

在 Oracle 中,下面的 SQL 会将 myString 拆分为子字符串:

WITH rws AS (
    SELECT
        'str1,STR2,stR3,StR4' myString
    FROM
        dual
) SELECT
    regexp_substr(
        myString,
        '[^,]+',
        1,
        level
    ) value
  FROM
    rws
CONNECT BY
    level <= length(myString) - length(
        replace(
            myString,
            ','
        )
    ) + 1;

结果是:

str1
STR2
stR3
StR4

In Oracle, below SQL will split myString to substring:

WITH rws AS (
    SELECT
        'str1,STR2,stR3,StR4' myString
    FROM
        dual
) SELECT
    regexp_substr(
        myString,
        '[^,]+',
        1,
        level
    ) value
  FROM
    rws
CONNECT BY
    level <= length(myString) - length(
        replace(
            myString,
            ','
        )
    ) + 1;

Result is:

str1
STR2
stR3
StR4
压抑⊿情绪 2024-09-26 10:21:02
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;
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;
独守阴晴ぅ圆缺 2024-09-26 10:21:02

我需要一个函数来分割 clob 并确保该函数在 sql 中可用。

create or replace type vchar_tab is table of varchar2(4000)
/
create or replace function split(
    p_list in clob,
    p_separator in varchar2 default '|'
) return vchar_tab pipelined is
    C_SQL_VCHAR_MAX constant integer:=4000;
    C_MAX_AMOUNT    constant integer:=28000;
    C_SEPARATOR_LEN constant integer:=length(p_separator);
    l_amount        integer:=C_MAX_AMOUNT;
    l_offset        integer:=1;
    l_buffer        varchar2(C_MAX_AMOUNT);
    l_list          varchar2(32767);
    l_index         integer;
begin
    if p_list is not null then
        loop
            l_index:=instr(l_list, p_separator);
            if l_index > C_SQL_VCHAR_MAX+1 then
                raise_application_error(-20000, 'item is too large for sql varchar2: len='||(l_index-1));
            elsif l_index > 0 then -- found an item, pipe it
                pipe row (substr(l_list, 1, l_index-1));
                l_list:=substr(l_list, l_index+C_SEPARATOR_LEN);
            elsif length(l_list) > C_SQL_VCHAR_MAX then
                raise_application_error(-20001, 'item is too large for sql varchar2: length exceeds '||length(l_list));
            elsif l_amount = C_MAX_AMOUNT then -- more to read from the clob
                dbms_lob.read(p_list, l_amount, l_offset, l_buffer);
                l_list:=l_list||l_buffer;
            else -- read through the whole clob
                if length(l_list) > 0 then
                    pipe row (l_list);
                end if;
                exit;
            end if;
        end loop;
    end if;

    return;
exception
    when no_data_needed then -- this happens when you don't fetch all records
        null;
end;
/

测试:

select *
from table(split('ASDF|IUYT|KJHG|ASYD'));

I needed a function that splits a clob and makes sure the function is usable in sql.

create or replace type vchar_tab is table of varchar2(4000)
/
create or replace function split(
    p_list in clob,
    p_separator in varchar2 default '|'
) return vchar_tab pipelined is
    C_SQL_VCHAR_MAX constant integer:=4000;
    C_MAX_AMOUNT    constant integer:=28000;
    C_SEPARATOR_LEN constant integer:=length(p_separator);
    l_amount        integer:=C_MAX_AMOUNT;
    l_offset        integer:=1;
    l_buffer        varchar2(C_MAX_AMOUNT);
    l_list          varchar2(32767);
    l_index         integer;
begin
    if p_list is not null then
        loop
            l_index:=instr(l_list, p_separator);
            if l_index > C_SQL_VCHAR_MAX+1 then
                raise_application_error(-20000, 'item is too large for sql varchar2: len='||(l_index-1));
            elsif l_index > 0 then -- found an item, pipe it
                pipe row (substr(l_list, 1, l_index-1));
                l_list:=substr(l_list, l_index+C_SEPARATOR_LEN);
            elsif length(l_list) > C_SQL_VCHAR_MAX then
                raise_application_error(-20001, 'item is too large for sql varchar2: length exceeds '||length(l_list));
            elsif l_amount = C_MAX_AMOUNT then -- more to read from the clob
                dbms_lob.read(p_list, l_amount, l_offset, l_buffer);
                l_list:=l_list||l_buffer;
            else -- read through the whole clob
                if length(l_list) > 0 then
                    pipe row (l_list);
                end if;
                exit;
            end if;
        end loop;
    end if;

    return;
exception
    when no_data_needed then -- this happens when you don't fetch all records
        null;
end;
/

Test:

select *
from table(split('ASDF|IUYT|KJHG|ASYD'));
半寸时光 2024-09-26 10:21:02

我喜欢那个顶级实用程序的外观。不过,了解可用于此目的的标准 Oracle 函数也是有好处的:subStr 和 inStr
http://download.oracle.com/docs/cd /B19306_01/server.102/b14200/functions001.htm

I like the look of that apex utility. However its also good to know about the standard oracle functions you can use for this: subStr and inStr
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/functions001.htm

飘然心甜 2024-09-26 10:21:02

大家有一个简单的方法。使用替换功能。以下是准备传递给 IN 子句的逗号分隔字符串的示例。

在 PL/SQL 中:

StatusString :=   REPLACE('Active,Completed', ',', ''',''');

在 SQL Plus 中:

Select  REPLACE('Active,Completed', ',', ''',''') from dual;

There is a simple way folks. Use REPLACE function. Here is an example of comma separated string ready to be passed to IN clause.

In PL/SQL:

StatusString :=   REPLACE('Active,Completed', ',', ''',''');

In SQL Plus:

Select  REPLACE('Active,Completed', ',', ''',''') from dual;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文