oracle如何将csv转成表格
Posted
技术标签:
【中文标题】oracle如何将csv转成表格【英文标题】:how to convert csv to table in oracle 【发布时间】:2011-03-09 17:33:19 【问题描述】:如何制作一个在传入 csv 值时以表格格式返回结果的包。
select * from table(schema.mypackage.myfunction('one, two, three'))
应该返回
one
two
three
我尝试了 ask tom 的一些东西,但这只适用于 sql 类型。
我正在使用 oracle 11g。有内置的吗?
【问题讨论】:
【参考方案1】:以下作品 调用它作为 select * from table(splitter('a,b,c,d'))
create or replace function splitter(p_str in varchar2) return sys.odcivarchar2list
is
v_tab sys.odcivarchar2list:=new sys.odcivarchar2list();
begin
with cte as (select level ind from dual
connect by
level <=regexp_count(p_str,',') +1
)
select regexp_substr(p_str,'[^,]+',1,ind)
bulk collect into v_tab
from cte;
return v_tab;
end;
/
【讨论】:
是regexp_substr
函数参数'[^,]+'
允许空字段,如'123,456,,999'
?【参考方案2】:
唉,在 11g 中,我们仍然必须使用 SQL 类型手动处理我们自己的 PL/SQL 标记器。在 11gR2 中,Oracle 给了我们一个聚合函数来将结果连接成一个 CSV 字符串,所以也许在 12i 中它们会提供反向功能。
如果您不想特别创建 SQL 类型,可以使用内置的 SYS.DBMS_DEBUG_VC2COLL,如下所示:
create or replace function string_tokenizer
(p_string in varchar2
, p_separator in varchar2 := ',')
return sys.dbms_debug_vc2coll
is
return_value SYS.DBMS_DEBUG_VC2COLL;
pattern varchar2(250);
begin
pattern := '[^('''||p_separator||''')]+' ;
select trim(regexp_substr (p_string, pattern, 1, level)) token
bulk collect into return_value
from dual
where regexp_substr (p_string, pattern, 1, level) is not null
connect by regexp_instr (p_string, pattern, 1, level) > 0;
return return_value;
end string_tokenizer;
/
它在行动:
SQL> select * from table (string_tokenizer('one, two, three'))
2 /
COLUMN_VALUE
----------------------------------------------------------------
one
two
three
SQL>
致谢:此代码是some code I found on Tanel Poder's blog 的变体。
【讨论】:
【参考方案3】:这是另一种完全在 sql 中使用正则表达式匹配器的解决方案。
SELECT regexp_substr('one,two,three','[^,]+', 1, level) abc
FROM dual
CONNECT BY regexp_substr('one,two,three', '[^,]+', 1, level) IS NOT NULL
【讨论】:
【参考方案4】:为获得最佳性能,最好避免在拆分器函数中使用分层 (CONNECT BY) 查询。
以下拆分器功能在应用于更大的数据量时会表现得更好
CREATE OR REPLACE FUNCTION row2col(p_clob_text IN VARCHAR2)
RETURN sys.dbms_debug_vc2coll PIPELINED
IS
next_new_line_indx PLS_INTEGER;
remaining_text VARCHAR2(20000);
next_piece_for_piping VARCHAR2(20000);
BEGIN
remaining_text := p_clob_text;
LOOP
next_new_line_indx := instr(remaining_text, ',');
next_piece_for_piping :=
CASE
WHEN next_new_line_indx <> 0 THEN
TRIM(SUBSTR(remaining_text, 1, next_new_line_indx-1))
ELSE
TRIM(SUBSTR(remaining_text, 1))
END;
remaining_text := SUBSTR(remaining_text, next_new_line_indx+1 );
PIPE ROW(next_piece_for_piping);
EXIT WHEN next_new_line_indx = 0 OR remaining_text IS NULL;
END LOOP;
RETURN;
END row2col;
/
可以在下面观察到这种性能差异(我使用了本讨论前面给出的函数拆分器)。
SQL> SET TIMING ON
SQL>
SQL> WITH SRC AS (
2 SELECT rownum||',a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'||rownum txt
3 FROM DUAL
4 CONNECT BY LEVEL <=10000
5 )
6 SELECT NULL
7 FROM SRC, TABLE(SYSTEM.row2col(txt)) t
8 HAVING MAX(t.column_value) > 'zzz'
9 ;
no rows selected
Elapsed: 00:00:00.93
SQL>
SQL> WITH SRC AS (
2 SELECT rownum||',a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'||rownum txt
3 FROM DUAL
4 CONNECT BY LEVEL <=10000
5 )
6 SELECT NULL
7 FROM SRC, TABLE(splitter(txt)) t
8 HAVING MAX(t.column_value) > 'zzz'
9 ;
no rows selected
Elapsed: 00:00:14.90
SQL>
SQL> SET TIMING OFF
SQL>
【讨论】:
【参考方案5】:我没有安装 11g 来玩,但是有一个 PIVOT 和 UNPIVOT 操作用于将列转换为行/将行转换为列,这可能是一个很好的起点。
http://www.oracle.com/technology/pub/articles/oracle-database-11g-top-features/11g-pivot.html
(实际上做了一些进一步的调查,这看起来不适合这种情况 - 它适用于实际的行/列,但不适用于列中的数据集)。
还有 DBMS_UTILITY.comma_to_table 和 table_to_comma 用于将 CSV 列表转换为 pl/sql 表。有一些限制(处理换行等),但可能是一个很好的起点。
我倾向于使用 TYPE 方法,使用一个简单的函数来执行 comma_to_table,然后为 comma_to_table 结果中的每个条目进行 PIPE ROW(不幸的是,DBMS_UTILITY.comma_to_table 是一个过程,因此无法从 SQL 调用)。
【讨论】:
以上是关于oracle如何将csv转成表格的主要内容,如果未能解决你的问题,请参考以下文章