如何将逗号分隔值放入oracle中的列

Posted

技术标签:

【中文标题】如何将逗号分隔值放入oracle中的列【英文标题】:How to put comma separated values to a column in oracle 【发布时间】:2018-04-07 08:20:34 【问题描述】:

我有一个 JSON 响应,处理响应后我的输出如下所示:

column_variable := 'col1,col2,col3';
data_clob := 
"2017-10-14,abc,1,
2019-10-13,abc,12,
2019-10-12,abc,,
"
;

由于原始响应包含 new line 的转义字符,data_clob 也已相应转换。

如何在 oracle 表中转换这个逗号分隔值:

我的输出应该是这样的:

col1            col2     col3 
2017-10-14      abc      1
2019-10-13      abc      12
2019-10-12      abc      null

我正在查看类似的问题,但我不想使用REGEXP_SUBSTR,因为我不知道我会在回复中获得多少列。 例如:column_variable 可能有 'col1,col2,col3,col4,col5,col6';

我正在使用oracle 12.1.0.2.0

请帮忙!

【问题讨论】:

能否在问题中显示原始 JSON 响应?也许直接从 JSON 解析数据会更容易? 嗨 Krokodilko ..刚刚添加了有问题的原始 JSON 响应 这个 JSON 结构不好,如果它看起来像这样会更好更容易:data: [ "col1":"2017-10-14","col2":"abc","col3":1, "col1":"2019-10-13","col2":"abc","col3":12, "col1":"2019-10-12","col2":"abc" ]。也许这个 API 的作者可以改变响应的格式? 是的,我同意它的结构不好,但我无法控制它 所以,您希望我们简单地编写一个查询/过程来神奇地 转换您的数据并为您提供一个数据库Table,而无需事先了解数据类型、数量表将包含的列等? 【参考方案1】:

使用Polymorphic Table Functions (Oracle 18c) 可以很简单地实现它:

Dynamic CSV to Columns Converter: Polymorphic Table Function Example:

create or replace package csv_pkg as  
  /* The describe function defines the new columns */  
  function describe (  
    tab in out dbms_tf.table_t,  
    col_names varchar2  
  ) return dbms_tf.describe_t;  

  /* Fetch_rows sets the values for the new columns */  
  procedure fetch_rows (col_names varchar2);  
end csv_pkg;  

和身体:

create or replace package body csv_pkg as  
  function describe(  
    tab in out dbms_tf.table_t,  
    col_names varchar2  
  )   
    return dbms_tf.describe_t as  
    new_cols dbms_tf.columns_new_t;  
    col_id   pls_integer := 2;  
  begin   

    /* Enable the source colun for reading */  
    tab.column(1).pass_through := FALSE;  
    tab.column(1).for_read     := TRUE;  
    new_cols(1) := tab.column(1).description;  

    /* Extract the column names from the header string,  
       creating a new column for each   
     */  
    for j in 1 .. ( length(col_names) - length(replace(col_names,',')) ) + 1 loop   
      new_cols(col_id) := dbms_tf.column_metadata_t(  
        name=>regexp_substr(col_names, '[^,]+', 1, j),--'c'||j,   
        type=>dbms_tf.type_varchar2  
      );  
      col_id := col_id + 1;  
    end loop;  

    return dbms_tf.describe_t( new_columns => new_cols );  
  end;  

  procedure fetch_rows (col_names varchar2) as   
    rowset    dbms_tf.row_set_t;  
    row_count pls_integer;  
  begin  
    /* read the input data set */  
    dbms_tf.get_row_set(rowset, row_count => row_count);  

    /* Loop through the input rows... */  
    for i in 1 .. row_count loop  
      /* ...and the defined columns, extracting the relevant value   
         start from 2 to skip the input string  
      */  
      for j in 2 .. ( length(col_names) - length(replace(col_names,',')) ) + 2 loop  
        rowset(j).tab_varchar2(i) :=   
          regexp_substr(rowset(1).tab_varchar2(i), '[^,]+', 1, j - 1);  
      end loop;  
    end loop;  

    /* Output the new columns and their values */  
    dbms_tf.put_row_set(rowset);  

  end;  

end csv_pkg;  

--function
create or replace function csv_to_columns(  
  tab table, col_names varchar2  
) return table pipelined row polymorphic using csv_pkg; 

然后你就简单地通过了:

select *   
from   csv_to_columns( data_clob, column_variable );

【讨论】:

嗨 lad2025 ,感谢您的回答!你知道是否有类似的东西可用于 oracle 12.1.0.2.0 @joe 也许你需要写一个自定义的。 甲骨文 18c?欢迎时间旅行者! ;-) @DavidFaber 是的,Oracle 18c。未来就是现在:)【参考方案2】:

对于低于 18 和 12 的 Oracle 版本,这是一种可能的解决方案,不确定...这并不完美,并且会根据您提供的数据在最后创建一个空列 - 额外的空格、逗号等...这个也可以在“SELECT”和输出的第一列之间创建一个空格。所有这些都可以稍后手动删除或使用更多编码删除。我希望这至少在某些方面有所帮助:

SELECT 'SELECT '''||REPLACE(str, chr(10), ''' FROM dual'||chr(10)||'UNION ALL'||chr(10)||'SELECT ''')||''' FROM dual' str
  FROM
  (
   SELECT TRIM(REPLACE(str, ',', ''''||', ''')) str FROM
   (
    SELECT TRIM(BOTH '"' FROM
     '"2017-10-14,abc,1,
       2019-10-13,abc,12,
       2019-10-12,abc,,"') AS str FROM dual
   )
  )
/

这将构建可以手动或使用动态 SQL 清理和执行的 select 语句:

SELECT '2017-10-14' col, 'abc' col, '1' col, '' FROM dual
UNION ALL
SELECT '2019-10-13' col, 'abc' col, '12' col, '' FROM dual
UNION ALL
SELECT '2019-10-12' col, 'abc' col, '' col, ''  FROM dual

上述select语句的输出:

COL         COL_1   COL_2
2017-10-14  abc         1
2019-10-13  abc        12
2019-10-12  abc      null

【讨论】:

以上是关于如何将逗号分隔值放入oracle中的列的主要内容,如果未能解决你的问题,请参考以下文章

如何将表中列中的逗号分隔值放入 SQL IN 运算符?

如何将逗号分隔值转换为oracle中的行?

如何根据两个逗号分隔值的列中的任何一个值获取记录

如何将在一行中输入的以逗号分隔的数字放入Java中的数组中

如何在 Amazon Redshift 的列中取消嵌套/展开/展平逗号分隔值?

如何根据oracle plsql中列中的逗号分隔值拆分选择查询行