预计阅读时间:4分钟微信
老婆昨儿提了一个问题,一张表中的字段,存储的是用","分隔的字符串,是否能够按照",",拆成多行?函数
举个例子,TBL_ROW表,三个字段,其中POS字段,包含以","分隔的字符串,需求就是将POS中","分隔的字符串,连同NAME和NATIONALITY,拆成多行,spa
CREATE TABLE TBL_ROW
(
NAME VARCHAR2(10),
NATIONALITY VARCHAR2(10),
POS VARCHAR2(200)
);
INSERT INTO TBL_ROW VALUES('张稀哲','中国','前卫,前腰,前锋');
INSERT INTO TBL_ROW VALUES('奥古斯托','巴西','后腰,前腰,前卫,前锋');
INSERT INTO TBL_ROW VALUES('比埃拉','西班牙','前卫,前腰');
SELECT * FROM tbl_row;
NAME NATIONALITY POS
------- ----------- ----------------------
傲骨 巴西 后腰,前腰,前卫,前锋
张稀哲 中国 前卫,前腰,前锋
比埃拉 西班牙 前腰,前卫
第一种写法:.net
使用正则函数,解析","分隔的字符,使用connect by层级查询,distinct进行去重的操做,设计
select DISTINCT NAME, nationality,
regexp_substr(pos, '[^,]+',1,level)
FROM tbl_row
connect by level <= length(decode(substr(pos,-1),',',substr(pos,1,length(pos)-1),pos)) - length(REPLACE(pos,',',''))+1
ORDER BY NAME;
对应的执行计划,code
第二种写法:regexp
使用substr分隔字符串,使用connect by层级查询,orm
SELECT NAME,nationality,substr(a.pos, instr(a.pos, ',', 1, levels.lvl) + 1, instr(a.pos, ',', 1, levels.lvl + 1) -(instr(a.pos, ',', 1, levels.lvl) + 1))
FROM
(SELECT NAME,',' || pos || ',' AS pos,nationality,length(pos) - nvl(length(REPLACE(pos, ',')), 0) + 1 AS cnt
FROM tbl_row) a,
(SELECT rownum AS lvl
FROM (SELECT MAX(length(pos || ',') - nvl(length(REPLACE(pos, ',')), 0)) max_len FROM tbl_row)
CONNECT BY LEVEL <= max_len) levels
WHERE levels.lvl <= a.cnt
ORDER BY NAME;
对应的执行计划,blog
获得的结果相同,ci
NAME NATIONALITY POS
------- ----------- -------
傲骨 巴西 前腰
傲骨 巴西 前卫
傲骨 巴西 前锋
傲骨 巴西 后腰
张稀哲 中国 前锋
张稀哲 中国 前腰
张稀哲 中国 前卫
比埃拉 西班牙 前卫
比埃拉 西班牙 前腰
总结一下,
1. 第二种写法相比第一种写法,略优一些,数据量小,区分不明显。
2. POS字段的设计,很是不合理,从理论上讲,不符合第三范式,不是原子拆分,所以才可能有这种需求。
3. 经验告诉咱们,老婆的问题,不能不重视,以最小的代价,先解决了再说。

本文分享自微信公众号 - bisal的我的杂货铺(gh_e8769c7350b1)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。