【问题标题】:Oracle- Split string comma delimited (string contains spaces and consecutive commas)Oracle- 拆分字符串逗号分隔(字符串包含空格和连续逗号)
【发布时间】:2016-05-28 10:08:03
【问题描述】:

我找不到有关如何在 ORACLE 中拆分逗号分隔字符串的解决方案。搜索了很多,对我的情况没有任何帮助

代码

DECLARE
  TYPE T_ARRAY_OF_VARCHAR IS TABLE OF VARCHAR2(2000) INDEX BY BINARY_INTEGER;
  MY_ARRAY T_ARRAY_OF_VARCHAR;
  MY_STRING VARCHAR2(2000) := '12 3,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 || '<');
   --DBMS_OUTPUT.PUT_LINE(CURRENT_ROW.SPLIT);
    MY_ARRAY(MY_ARRAY.COUNT) := CURRENT_ROW.SPLIT;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Array Size:' || MY_ARRAY.COUNT);
END;

/

输出是:

>12 3<
>456<
>abc<
>def<
><
Array Size:5

空值乱码!!!

【问题讨论】:

标签: sql oracle plsql split


【解决方案1】:

试试这个解析列表部分。它处理 NULL:

SQL> select regexp_substr('12 3,456,,abc,def', '(.*?)(,|$)', 1, level, null, 1) SPLIT, level
    from dual
    connect by level <= regexp_count('12 3,456,,abc,def',',') + 1
    ORDER BY level;

SPLIT                  LEVEL
----------------- ----------
12 3                       1
456                        2
                           3
abc                        4
def                        5

SQL>

不幸的是,当您搜索用于解析列表的正则表达式时,您总是会发现这种不处理空值的表单,应该避免使用:'[^,]+'。请参阅此处了解更多信息:Split comma separated values to columns in Oracle

【讨论】:

    【解决方案2】:

    试试 xmltable 和 flwor 表达式。 如果您输入不带逗号的字符串,则以下示例不安全并引发错误。但是更容易理解。

    select xmlcast(column_value as varchar2(2000))  value_list 
      from xmltable('for $val in ora:tokenize($strList,",") 
                      return $val' 
                     passing '12 3,456,,abc,def' as "strList"
                    ); 
    

    和安全版本。

        select xmlcast(column_value as varchar2(2000)) value_list 
          from xmltable('for $val at $index in ora:tokenize(concat(",",$strList),",") 
           where $index > 1 
           return $val' passing '12 3,456,,abc,def' as "strList"
                   );
    

    【讨论】:

    • 或者,您可以通过将ora:tokenize 更改为fn:tokenize 来防止ORA-19176 FORX0003 缺少分隔符错误
    • 第一个 xmltable 表达式 'for $val in ora:tokenize($strList,",") return $val' 可以简化为 ora:tokenize($strList, ",")。第二个 FLWOR 表达式可以更简单地完成为 if (contains($strList, ",")) then ora:tokenize($strList,"\,") else $strList
    【解决方案3】:

    对您的查询稍作修改,假设您可以选择一个不会出现在 MY_STRING 中的字符,例如管道|

       with test as    
      (select '12 3,456,,,,abc,def' MY_STRING from dual)
      select trim('|' from regexp_substr(regexp_replace(MY_STRING,',,',',|,|'),'[^,]+',1,level)) SPLIT
      from test
      connect by level <= length (regexp_replace(MY_STRING, '[^,]+'))  + 1;
    

    输出:

     SPLIT                 
    -----------------------
    12 3                    
    456                     
    (null)                        
    (null)                         
    (null)                         
    abc                     
    def  
    

    【讨论】:

    • 不。如果我有三个逗号怎么办?这不起作用!
    【解决方案4】:

    不需要PL/SQL,你可以用普通的SQL来做。见Split comma delimited strings in a table in Oracle

    使用MODEL子句:

    WITH sample_data AS (
    SELECT '12 3,456,,,,,abc,def' str FROM dual
    )
    -- end of sample_data mimicking real table
    ,
    model_param AS (
    SELECT str AS orig_str ,
           ','
           || str
           || ','                                 AS mod_str ,
           1                                      AS start_pos ,
           Length(str)                           AS end_pos ,
           (LENGTH(str) -
           LENGTH(REPLACE(str, ','))) + 1        AS element_count ,
           0                                      AS element_no ,
           ROWNUM                                 AS rn
           FROM   sample_data )
           SELECT trim(Substr(mod_str, start_pos, end_pos-start_pos)) str
           FROM (
                 SELECT *
                 FROM   model_param
                 MODEL PARTITION BY ( rn, orig_str, mod_str)
                 DIMENSION BY (element_no)
                 MEASURES (start_pos, end_pos, element_count)
                 RULES ITERATE (2000)
                 UNTIL (ITERATION_NUMBER+1 = element_count[0])
               ( start_pos[ITERATION_NUMBER+1] =
                         instr(cv(mod_str), ',', 1, cv(element_no)) + 1,
                 end_pos[ITERATION_NUMBER+1] =
                         instr(cv(mod_str), ',', 1, cv(element_no) + 1) )
               )
            WHERE    element_no != 0
       ORDER BY      mod_str ,
                     element_no
       /
    

    输出

    STR
    ----------------------
    12 3
    456
    
    
    
    
    abc
    def
    
    8 rows selected.
    

    如果您想在 PL/SQL 中执行此操作,则可以使用 流水线表函数:

    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 1970-01-01
      • 2023-01-03
      • 2015-03-07
      • 1970-01-01
      • 2012-11-12
      • 1970-01-01
      相关资源
      最近更新 更多