如何在oracle 9i中最好地分割csvstring
我希望能够在Oracle 9i中分割csvstring
我读过以下文章http://www.oappssurd.com/2009/03/string-split-in-oracle.html
但我不明白如何使这项工作。 这里有一些与我有关的问题
- 这将在Oracle 9i中工作,如果没有,为什么不呢?
- 有没有更好的方法去分裂csvstring,然后提出上面的解决scheme?
- 我需要创build一个新的types? 如果是这样,我需要特定privilages呢?
- 我可以在函数中声明types吗?
这是一个Oracle的string标记器,比这个页面更简单一些,但是不知道它是否一样快:
create or replace function splitter_count(str in varchar2, delim in char) return int as val int; begin val := length(replace(str, delim, delim || ' ')); return val - length(str); end; create type token_list is varray(100) of varchar2(200); CREATE or replace function tokenize (str varchar2, delim char) return token_list as ret token_list; target int; i int; this_delim int; last_delim int; BEGIN ret := token_list(); i := 1; last_delim := 0; target := splitter_count(str, delim); while i <= target loop ret.extend(); this_delim := instr(str, delim, 1, i); ret(i):= substr(str, last_delim + 1, this_delim - last_delim -1); i := i + 1; last_delim := this_delim; end loop; ret.extend(); ret(i):= substr(str, last_delim + 1); return ret; end;
你可以像这样使用它:
select tokenize('hi you person', ' ') from dual; VARCHAR(hi,you,person)
乔伊斯,
这里有三个例子:
1)使用dbms_utility.comma_to_table。 这不是一个通用的例程,因为这些元素应该是有效的标识符。 用一些肮脏的技巧,我们可以使其工作更普遍:
SQL> declare 2 cn_non_occuring_prefix constant varchar2(4) := 'zzzz'; 3 mystring varchar2(2000):='a:sd:dfg:31456:dasd: :sdfsdf'; -- just an example 4 l_tablen binary_integer; 5 l_tab dbms_utility.uncl_array; 6 begin 7 dbms_utility.comma_to_table 8 ( list => cn_non_occuring_prefix || replace(mystring,':',','||cn_non_occuring_prefix) 9 , tablen => l_tablen 10 , tab => l_tab 11 ); 12 for i in 1..l_tablen 13 loop 14 dbms_output.put_line(substr(l_tab(i),1+length(cn_non_occuring_prefix))); 15 end loop; 16 end; 17 / a sd dfg 31456 dasd sdfsdf PL/SQL-procedure is geslaagd.
2)使用SQL的级别连接。 如果你在10g或更高,你可以使用连接级别的方法结合正则expression式,如下所示:
SQL> declare 2 mystring varchar2(2000):='a:sd:dfg:31456:dasd: :sdfsdf'; -- just an example 3 begin 4 for r in 5 ( select regexp_substr(mystring,'[^:]+',1,level) element 6 from dual 7 connect by level <= length(regexp_replace(mystring,'[^:]+')) + 1 8 ) 9 loop 10 dbms_output.put_line(r.element); 11 end loop; 12 end; 13 / a sd dfg 31456 dasd sdfsdf PL/SQL-procedure is geslaagd.
3)再次使用SQL的级别连接,但现在结合良好的旧SUBSTR / INSTR的情况下,你是在版本9,就像你是:
SQL> declare 2 mystring varchar2(2000):='a:sd:dfg:31456:dasd: :sdfsdf'; -- just an example 3 begin 4 for r in 5 ( select substr 6 ( str 7 , instr(str,':',1,level) + 1 8 , instr(str,':',1,level+1) - instr(str,':',1,level) - 1 9 ) element 10 from (select ':' || mystring || ':' str from dual) 11 connect by level <= length(str) - length(replace(str,':')) - 1 12 ) 13 loop 14 dbms_output.put_line(r.element); 15 end loop; 16 end; 17 / a sd dfg 31456 dasd sdfsdf PL/SQL-procedure is geslaagd.
你可以在这篇博文中看到更多这样的技术: http: //rwijk.blogspot.com/2007/11/interval-based-row-generation.html
希望这可以帮助。
关心,罗布。
为了解决您的意见:
将分离值插入标准化表格的示例。
首先创build表格:
SQL> create table csv_table (col) 2 as 3 select 'a,sd,dfg,31456,dasd,,sdfsdf' from dual union all 4 select 'a,bb,ccc,dddd' from dual union all 5 select 'zz,yy,' from dual 6 / Table created. SQL> create table normalized_table (value varchar2(10)) 2 / Table created.
因为您对dbms_utility.comma_to_table方法感兴趣,所以我在这里提到它。 但是,我当然不推荐这个变体,因为标识符怪癖和缓慢的逐行处理。
SQL> declare 2 cn_non_occuring_prefix constant varchar2(4) := 'zzzz'; 3 l_tablen binary_integer; 4 l_tab dbms_utility.uncl_array; 5 begin 6 for r in (select col from csv_table) 7 loop 8 dbms_utility.comma_to_table 9 ( list => cn_non_occuring_prefix || replace(r.col,',',','||cn_non_occuring_prefix) 10 , tablen => l_tablen 11 , tab => l_tab 12 ); 13 forall i in 1..l_tablen 14 insert into normalized_table (value) 15 values (substr(l_tab(i),length(cn_non_occuring_prefix)+1)) 16 ; 17 end loop; 18 end; 19 / PL/SQL procedure successfully completed. SQL> select * from normalized_table 2 / VALUE ---------- a sd dfg 31456 dasd sdfsdf a bb ccc dddd zz yy 14 rows selected.
我build议这个单一的SQL变体:
SQL> truncate table normalized_table 2 / Table truncated. SQL> insert into normalized_table (value) 2 select substr 3 ( col 4 , instr(col,',',1,l) + 1 5 , instr(col,',',1,l+1) - instr(col,',',1,l) - 1 6 ) 7 from ( select ',' || col || ',' col from csv_table ) 8 , ( select level l from dual connect by level <= 100 ) 9 where l <= length(col) - length(replace(col,',')) - 1 10 / 14 rows created. SQL> select * from normalized_table 2 / VALUE ---------- a a zz sd bb yy dfg ccc 31456 dddd dasd sdfsdf 14 rows selected.
关心,罗布。
你可能想要更清楚你想做什么,然后我们可以给你一个具体的答案。 显示你的一些代码总是有帮助:)
如果使用的是参数,要分割一串csv数字(例如:1,2,3,4),那么在IN
语句中使用它可以查看问题670922中的函数str2tbl()
。 通过一些更改,您可以将其更改为VARCHAR2
或任何您需要的内容。
在下面你可以设置:sMyCatagories
等于'1,2,3,4'
create or replace type myTableType as table of number; create or replace function str2tbl( p_str in varchar2 ) return myTableType as l_str long default p_str || ','; l_n number; l_data myTableType := myTabletype(); begin loop l_n := instr( l_str, ',' ); exit when (nvl(l_n,0) = 0); l_data.extend; l_data( l_data.count ) := ltrim(rtrim(substr(l_str,1,l_n-1))); l_str := substr( l_str, l_n+1 ); end loop; return l_data; end;
并使用它在select声明….
SELECT * FROM atable a WHERE a.category in ( select * from INLIST ( select cast(str2tbl(:sMyCatagories) as mytableType) from dual ) );
这实际上只有在使用参数时才有用。 如果在应用程序中一起使用SQL,那么只需使用正常的IN语句即可。
SELECT * FROM atable a WHERE a.category in (1,2,3,4);
我最终使用了这个
create or replace function split ( p_list varchar2 ) return sys.dbms_debug_vc2coll pipelined is l_idx pls_integer; l_list varchar2(32767) := p_list; l_value varchar2(32767); begin loop l_idx := instr(l_list,','); if l_idx > 0 then pipe row(substr(l_list,1,l_idx-1)); l_list := substr(l_list,l_idx+length(',')); else pipe row(l_list); exit; end if; end loop; return; end split;
declare CURSOR c IS select occurrence_num, graphics from supp where graphics is not null and graphics not like ' %'; begin FOR r IN c LOOP insert into image (photo_id,report_id, filename) select image_key_seq.nextval photo_id, r.occurrence_num report_id, t.column_value filename from table(split(cast(r.graphics as varchar2(1000)))) t where t.column_value is not null; END LOOP; end ;
这听起来像你不想添加架构(types,function)。 parsing分隔文本的一种方法是用instr和substr调用“疯狂”。
DECLARE V_CSV_STRING VARCHAR2(100); BEGIN --Create a test delimited list of first_name, last_name, middle_init V_CSV_STRING := 'Brian,Hart,M'; select substr( V_CSV_STRING||',', 1, instr(V_CSV_STRING,',')-1 ) FIRST_NAME, substr( V_CSV_STRING||',,', instr( V_CSV_STRING||',,', ',') +1, instr( V_CSV_STRING||',,', ',', 1, 2 )-instr(V_CSV_STRING||',,',',')-1 ) LAST_NAME, rtrim(substr( V_CSV_STRING||',,', instr( V_CSV_STRING||',,',',',1,2)+1),',') MIDDLE_INIT from dual; END;
如果你正在寻找一个结构并添加适当的应用程序代码(函数,视图,types等等),我会看看Tom Kyte关于这个主题的文章 。