Oracle 在PL/SQL中将逗号分隔的字符串转换为数组

Oracle 在PL/SQL中将逗号分隔的字符串转换为数组,oracle,plsql,tokenize,Oracle,Plsql,Tokenize,如何将逗号分隔的字符串转换为数组 我有输入'1,2,3',我需要将其转换为数组。在BBDD上快速搜索后,我找到了一个名为split的函数: 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;AA l_value varch

如何将逗号分隔的字符串转换为数组


我有输入'
1,2,3'
,我需要将其转换为数组。

在BBDD上快速搜索后,我找到了一个名为split的函数:

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;AA 
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;

我不知道它是否有用,但我们找到了

Oracle提供了内置功能

不幸的是,这个不适用于数字:

SQL> declare
  2    l_input varchar2(4000) := '1,2,3';
  3    l_count binary_integer;
  4    l_array dbms_utility.lname_array;
  5  begin
  6    dbms_utility.comma_to_table
  7    ( list   => l_input
  8    , tablen => l_count
  9    , tab    => l_array
 10    );
 11    dbms_output.put_line(l_count);
 12    for i in 1 .. l_count
 13    loop
 14      dbms_output.put_line
 15      ( 'Element ' || to_char(i) ||
 16        ' of array contains: ' ||
 17        l_array(i)
 18      );
 19    end loop;
 20  end;
 21  /
declare
*
ERROR at line 1:
ORA-00931: missing identifier
ORA-06512: at "SYS.DBMS_UTILITY", line 132
ORA-06512: at "SYS.DBMS_UTILITY", line 164
ORA-06512: at "SYS.DBMS_UTILITY", line 218
ORA-06512: at line 6
但有一个小技巧,在元素前面加上“x”,它就可以工作了:

SQL> declare
  2    l_input varchar2(4000) := '1,2,3';
  3    l_count binary_integer;
  4    l_array dbms_utility.lname_array;
  5  begin
  6    dbms_utility.comma_to_table
  7    ( list   => regexp_replace(l_input,'(^|,)','\1x')
  8    , tablen => l_count
  9    , tab    => l_array
 10    );
 11    dbms_output.put_line(l_count);
 12    for i in 1 .. l_count
 13    loop
 14      dbms_output.put_line
 15      ( 'Element ' || to_char(i) ||
 16        ' of array contains: ' ||
 17        substr(l_array(i),2)
 18      );
 19    end loop;
 20  end;
 21  /
3
Element 1 of array contains: 1
Element 2 of array contains: 2
Element 3 of array contains: 3

PL/SQL procedure successfully completed.
问候,,
Rob.

是的,dbms_utility.comma_to_表只支持逗号删除列表,并且仅当列表中的元素是有效的PL/SQL标识时(因此数字会导致错误),这是非常令人沮丧的

我已经创建了一个通用的解析包,它可以做您需要的事情(粘贴在下面)。它是我的“demo.zip”文件的一部分,这个文件库包含了2000多个支持我的培训材料的文件,所有这些文件都可以在PL/sqleposition:www.toadworld.com/SF上找到

问候,, 史蒂文·费尔斯坦 www.plsqlchallenge.com (每日PL/SQL测试)

简单代码

create or replace function get_token(text_is varchar2, token_in number, delim_is varchar2 := ';') return varchar2 is text_ls varchar2(2000); spos_ln number; epos _ln number; begin text_ls := delim_is || text_is || rpad(delim_is, token_in, delim_is); spos_ln := instr(text_ls, delim_is, 1, token_in); epos_ln := instr(text_ls, delim_is, 1, token_in+1); return substr(text_ls, spos_ln+1, epos_ln-spos_ln-1); end get_token; 创建或替换函数get_token(text_为varchar2,token_为number,delim_为varchar2:=';'))返回varchar2为 文本2(2000年); spos_ln编号; epos\u项次编号; 开始 text_ls:=delim_是| | text_是| | rpad(delim_是,token_in,delim_是); spos_ln:=instr(text_ls,delim_is,1,token_in); epos\u ln:=instr(文本,delim\u is,1,符号+1); 返回substr(text_ls,spos_ln+1,epos_ln-spos_ln-1); 结束获取令牌;
我们永远不会用不同的方式做同样的事情,对吗? 我最近发现这非常方便:

DECLARE
   BAR   VARCHAR2 (200) := '1,2,3';
BEGIN
   FOR FOO IN (    SELECT REGEXP_SUBSTR (BAR,
                                         '[^,]+',
                                         1,
                                         LEVEL)
                             TXT
                     FROM DUAL
               CONNECT BY REGEXP_SUBSTR (BAR,
                                         '[^,]+',
                                         1,
                                         LEVEL)
                             IS NOT NULL)
   LOOP
      DBMS_OUTPUT.PUT_LINE (FOO.TXT);
   END LOOP;
END;
产出:

1
2
3

我正在寻找一个类似的解决方案,在逗号分隔的列表中有多字节字符(连字符、空格、下划线)。因此,
dbms\u实用程序。逗号到表
对我不起作用

declare
  curr_val varchar2 (255 byte);
  input_str varchar2 (255 byte);
  remaining_str varchar2 (255 byte);
begin
  remaining_str := input_str || ',dummy';  -- this value won't output
  while (regexp_like (remaining_str, '.+,.+'))
  loop
    curr_val := substr (remaining_str, 1, instr (remaining_str, ',') - 1);
    remaining_str = substr (remaining_str, instr (remaining_str, ',') + 1);
    dbms_output.put_line (curr_val);
  end loop;
end;

这不是一个专家的答案,所以我希望有人能改进这个答案。

另一种可能性是:

create or replace FUNCTION getNth (
  input varchar2,
  nth number
) RETURN varchar2 AS
  nthVal varchar2(80);
BEGIN
  with candidates (s,e,n) as (
      select 1, instr(input,',',1), 1 from dual
      union all
      select e+1, instr(input,',',e+1), n+1
        from candidates where e > 0)
  select substr(input,s,case when e > 0 then e-s else length(input) end) 
    into nthVal
    from candidates where n=nth;
  return nthVal;
END getNth;

运行它有点太贵了,因为每次调用方要求其中一个项目时,它都会计算完整的拆分…

我知道Stack Overflow不赞成不加解释地粘贴URL,但此特定页面有几个非常好的选项:

我特别喜欢这个,它将分隔列表转换为临时表,您可以对其运行查询:

/* Create the output TYPE, here using a VARCHAR2(100) nested table type */

SQL> CREATE TYPE test_type AS TABLE OF VARCHAR2(100);
  2  /

Type created.

/* Now, create the function.*/

SQL> CREATE OR REPLACE FUNCTION f_convert(p_list IN VARCHAR2)
  2    RETURN test_type
  3  AS
  4    l_string       VARCHAR2(32767) := p_list || ',';
  5    l_comma_index  PLS_INTEGER;
  6    l_index        PLS_INTEGER := 1;
  7    l_tab          test_type := test_type();
  8  BEGIN
  9    LOOP
 10      l_comma_index := INSTR(l_string, ',', l_index);
 11      EXIT WHEN l_comma_index = 0;
 12      l_tab.EXTEND;
 13      l_tab(l_tab.COUNT) := SUBSTR(l_string, l_index, l_comma_index - l_index);
 14      l_index := l_comma_index + 1;
 15    END LOOP;
 16    RETURN l_tab;
 17  END f_convert;
 18  /

Function created.


还有一个更简单的选择

select to_number(column_value) as IDs from xmltable('1,2,3,4,5');
使用:

让我们看看输出:

SQL> SELECT *
  2  FROM TABLE(comma_to_table('12 3,456,,,,,abc,def'))
  3  /

COLUMN_VALUE
------------------------------------------------------------------------------
12 3
456




abc
def

8 rows selected.

SQL>

您可以使用Replace函数轻松替换逗号。 为此——

SQL Server(Transact-SQL)中替换函数的语法为:
替换(字符串,字符串到替换,替换字符串)

参数或参数

字符串:源字符串,其中一个字符序列将被另一组字符替换。
要替换的字符串:将在string1中搜索的字符串。
替换\u字符串:替换字符串。所有出现的字符串\u to \u replace将替换为字符串1中的替换字符串。
注意

REPLACE函数执行不正确的替换 区分大小写。因此,所有出现的字符串_to_replace都将 替换为替换字符串,而不考虑 字符串\u到\u替换或替换\u字符串

例如:
从DUAL中选择REPLACE('Kapil,raj,chouhan',',','
结果:Kapil raj chouhan

从DUAL中选择REPLACE('I Live In India','','-'
结果:我住在印度

从DUAL中选择REPLACE('facebook.com',face',friends'
结果:friendsbook.com


我希望它对你有用

下面Stewart Ashton在这个链接中提出的解决方案非常方便。
TYPE string_aa IS TABLE OF VARCHAR2(32767) INDEX BY PLS_INTEGER;

FUNCTION string_to_list(p_string_in IN VARCHAR2)
    RETURN string_aa
    IS
      TYPE ref_cursor IS ref cursor;
      l_cur    ref_cursor;
      l_strlist string_aa;
      l_x      PLS_INTEGER;
    BEGIN      
      IF p_string_in IS NOT NULL THEN 
         OPEN l_cur FOR 
            SELECT regexp_substr(p_string_in,'[^,]+', 1, level) FROM dual
            CONNECT BY regexp_substr(p_string_in, '[^,]+', 1, level) IS NOT NULL;      
         l_x := 1; 
         LOOP
           FETCH l_cur INTO l_strlist(l_x);
           EXIT WHEN l_cur%notfound;
           -- excludes NULL items  e.g.   1,2,,,,5,6,7
           l_x := l_x + 1;
         END LOOP;
      END IF;   
      RETURN l_strlist;
   END string_to_list;
他消除了值列表为整数的需要,因此可以使用字符串列表

在WITH子句中,他用单引号将这些值括起来,然后将其转换为具有VARCHAR2类型的单列的表。


似乎我们两个答案中的基本功能完全相同:)Uops!我没有看到你的答案!实际上,它工作得很好,我将它存储在我的有用函数库中;)非常好的逻辑构建表达式。:-)如果在中间有空字符串(即‘1,2,3’),则不工作,这只对小列表有效,因为它在选择对应的匹配计数(<代码>级别< /代码>)的行相同的情况下产生交叉连接。如果您的列表可能会随着时间的推移而增长,这将带来可扩展性风险。如果您的字符串带有空格和连续逗号(即12 3456、、abc、def),则此解决方案是唯一有效的解决方案。搜索了4个小时,直到我找到这个!!!!!!抢手货它也不适用于特殊角色。您可以通过执行一些额外的特殊“替换”来克服此限制。例如,在输入函数时使用replace(…,'','XYZ'),在检索单个值时使用replace(…,'XYZ','')。From:
COMMA\u TO_TABLE
(以及反向
TABLE\u TO_COMMA
)不是为此目的编写的。。。它们的编写主要是为了在Oracle内部复制中使用,并解析标识符而不是字符串,因此必须是有效的Oracle对象名称。将字符串与“”一起使用时出错例如:
'8.5.17.1,8.5.17.2'
错误是
ORA-20001:逗号分隔列表在x8.5附近无效。
您能帮助解决此问题吗???此处使用的函数仅用于拆分表名或类似有效对象名/标识符的列表。请参阅Oracle文档。不要遵循此解决方案,因为它将导致“随机”/“任意”字符串列表的未定义行为。此函数不回答此问题。帖子要求列表中所有元素的数组。此函数通过索引提供对数组中单个元素的访问。新loc
select to_number(column_value) as IDs from xmltable('1,2,3,4,5');
SQL> CREATE OR REPLACE TYPE test_type
  2  AS
  3    TABLE OF VARCHAR2(100)
  4  /

Type created.

SQL> CREATE OR REPLACE FUNCTION comma_to_table(
  2      p_list IN VARCHAR2)
  3    RETURN test_type PIPELINED
  4  AS
  5    l_string LONG := p_list || ',';
  6    l_comma_index PLS_INTEGER;
  7    l_index PLS_INTEGER := 1;
  8  BEGIN
  9    LOOP
 10      l_comma_index := INSTR(l_string, ',', l_index);
 11      EXIT
 12    WHEN l_comma_index = 0;
 13      PIPE ROW ( TRIM(SUBSTR(l_string, l_index, l_comma_index - l_index)));
 14      l_index := l_comma_index                                + 1;
 15    END LOOP;
 16  RETURN;
 17  END comma_to_table;
 18  /

Function created.
SQL> SELECT *
  2  FROM TABLE(comma_to_table('12 3,456,,,,,abc,def'))
  3  /

COLUMN_VALUE
------------------------------------------------------------------------------
12 3
456




abc
def

8 rows selected.

SQL>
TYPE string_aa IS TABLE OF VARCHAR2(32767) INDEX BY PLS_INTEGER;

FUNCTION string_to_list(p_string_in IN VARCHAR2)
    RETURN string_aa
    IS
      TYPE ref_cursor IS ref cursor;
      l_cur    ref_cursor;
      l_strlist string_aa;
      l_x      PLS_INTEGER;
    BEGIN      
      IF p_string_in IS NOT NULL THEN 
         OPEN l_cur FOR 
            SELECT regexp_substr(p_string_in,'[^,]+', 1, level) FROM dual
            CONNECT BY regexp_substr(p_string_in, '[^,]+', 1, level) IS NOT NULL;      
         l_x := 1; 
         LOOP
           FETCH l_cur INTO l_strlist(l_x);
           EXIT WHEN l_cur%notfound;
           -- excludes NULL items  e.g.   1,2,,,,5,6,7
           l_x := l_x + 1;
         END LOOP;
      END IF;   
      RETURN l_strlist;
   END string_to_list;
declare
seprator varchar2(1):=',';
dosweeklist varchar2(4000):='a,b,c';
begin
for i in (SELECT  SUBSTR(dosweeklist,
                         case when level=1 then 1 else INSTR(dosweeklist,seprator,1,LEVEL-1)+1 end,
                         NVL(NULLIF(INSTR(dosweeklist,seprator,1,LEVEL),0),length(dosweeklist)+1) - case when level=1 then 1 else INSTR(dosweeklist,seprator,1,LEVEL-1)+1 end) dat 
          FROM dual
          CONNECT BY LEVEL <= LENGTH(dosweeklist) - LENGTH(REPLACE(dosweeklist,seprator,'')) +1)
loop
dbms_output.put_line(i.dat);
end loop;
end;
/
a

b

c
with data as (
  select '"'||replace(:txt, ',', '","')||'"' str from dual
)
select xmlcast(column_value as varchar2(4000)) subs
from data, xmltable(str);