oracle根据特定字符拆分字符串的方法

时间:2024-03-11 08:54:48

清洗数据需要将某个字段内以空格分隔的字符串拆分成多行单个的字符串,百度了很多种方法大概归结起来也就这几种方法最为有效,现在把贴出来:

第一种:

select regexp_substr(\'1 2 3\',\'[^ ]+\',1,level,\'i\') from dual

connect by level <= length(\'1 2 3\') -length(regexp_replace(\'1 2 3\',\' \',\'\'))+1;

效果就是这个样子↓

效果

这种方法的核心就是regexp_substr函数,通过正则来拆分字符串,函数用法为:

regexp_substr(str,进行匹配的正则表达式,position,标志第几个匹配组,modifier)

该函数的一个缺点就是只能每次取一个字符串出来,这个就比较头痛了,因为现在我要取分割后所有的字符串,所以现在就要用到connect by命令,来限制取几个匹配组,当然我们是要全部的,所以就通过length来实时确定所取得匹配组数量。

通过这种方法就可以实现开题的需求,但在用的过程中发现一个问题,如果说我要给拆分后的字符串带上ID的话这种方法就貌似失灵了,会无限次取,所以问题没解决,有人知道的话麻烦可以告诉我一下。

第二种

 1 create or replace function split(p_list varchar,p_sep varchar := \' \')两个参数,一个实传入字符串名,第二个是根据什么来分割
 2 
 3 return type_split pipelined
 4 
 5 IS
 6 
 7 l_idx pls_integer;
 8 
 9 v_list varchar2(50) := p_list;
10 
11 begin
12 
13  loop
14 
15    l_idx :=instr(v_list,p_sep);
16 
17    if l_idx = 0then
18 
19      piperow(substr(v_list,1,l_idx-1));
20 
21      v_list :=substr(v_list,l_idx+length(p_sep));
22 
23    else
24 
25      piperow(v_list);
26 
27     exit;
28 
29    end if;
30 
31   end loop;
32 
33 end split;

通过创建函数的方法实现拆分字符串,缺点同一,无法实现取ID

用法:

selelct * from table(split(这里写字符串,’ ’));

与第二种异曲同工的还有接下来这一种

第二种.2

 1 CREATE OR REPLACE 
 2 FUNCTION splitstr (str IN CLOB,
 3                                         i   IN NUMBER := 0,
 4                                         sep IN VARCHAR2 := \',\'
 5  )
 6     RETURN VARCHAR2
 7  /**************************************
 8 52  * Name:        splitstr
 9 53  * Author:      Sean Zhang.
10 54  * Date:        2012-09-03.
11 55  * Function:    返回字符串被指定字符分割后的指定节点字符串。
12 56  * Parameters:  str: 待分割的字符串。
13 57                 i: 返回第几个节点。当i为0返回str中的所有字符,当i 超过可被分割的个数时返回空。
14 58                 sep: 分隔符,默认逗号,也可以指定字符或字符串。当指定的分隔符不存在于str中时返回sep中的字符。
15 59  * Example:     select splitstr(\'abc,def\', 1) as str from dual;  得到 abc
16 60                 select splitstr(\'abc,def\', 3) as str from dual;  得到 空
17 61  **************************************/
18 IS
19 t_i       NUMBER;
20 t_count   NUMBER;
21 t_str     VARCHAR2 (4000);
22 BEGIN
23 IF i = 0
24 THEN
25 t_str   := str;
26 ELSIF INSTR (str, sep) = 0
27 THEN
28 t_str   := sep;
29 ELSE
30 SELECT COUNT ( * )
31 INTO t_count
32 FROM table (split (str, sep));
33 IF i <= t_count
34 THEN
35 SELECT str
36 INTO t_str
37 FROM (SELECT ROWNUM AS item, COLUMN_VALUE AS str
38 FROM table (split (str, sep)))
39 WHERE item = i;
40 END IF;
41 END IF;
42 
43 RETURN t_str;
44 END;

 

第三种:

个人认为第三种才是最有效果的用了with as命令,以下优点解释摘自网络

With查询语句不是以select开始的,而是以“WITH”关键字开头    可认为在真正进行查询之前预先构造了一个临时表TT,之后便可多次使用它做进一步的分析和处理

WITHClause方法的优点:增加了SQL的易读性,如果构造了多个子查询,结构会更清晰;更重要的是:“一次分析,多次使用”,这也是为什么会提供性能的地方,达到了“少读”的目标。

第一种使用子查询的方法表被扫描了两次,而使用WITH Clause方法,表仅被扫描一次。这样可以大大的提高数据分析和查询的效率。

    另外,观察WITH Clause方法执行计划,其中“SYS_TEMP_XXXX”便是在运行过程中构造的中间统计结果临时表。

 1 with temp0 as (select LEVEL lv from dualCONNECT BY LEVEL <= 100)  
 2 
 3 select id,
 4 
 5 substr(t.vals,instr(t.vals, \' \', 1, tv.lv)+ 1,instr(t.vals, \' \', 1, tv.lv + 1)-(instr(t.vals, \' \', 1, tv.lv) + 1)) ASattr4  
 6 
 7 from 
 8 
 9 (select id,\' \' || attr4 || \' \' ASvals,length(attr4 || \' \') - nvl(length(REPLACE(attr4, \' \')), 0) AS cnt
10 
11  fromT_FLIGHT_TLX_TOTAL where rownum < 3) t 
12 
13 join temp0 tv  on  tv.lv <= t.cnt  order by 1;