oracle将一行分割后展示为多行

需求场景

我需要统计num列中每一个数字的出现的次数,例如1出现了3次,2出现了两次,5出现了三次。

那么首先我需要将num列通过“,”拆分后展示为多行

sql结果

这里先说答案,随后解释

SELECT
	id,
	REGEXP_SUBSTR(num, '[^,]+', 1,level) as num
from 
numTable
CONNECT BY REGEXP_SUBSTR(num, '[^,]+', 1,level) is not null
and id = prior id
and prior SYS_GUID() is not null
ORDER BY num

在现有结果上对num做分组求和便可以得到每个数字出现的次数了

sql解读

regexp_substr

regexp_substr可以根据标识分割字符串,并取出分割后指定的子字符串

regexp_substr(String, pattern, position,occurrence, modifier)

String: 目标字符串(需要处理的字符串)

pattern: 正则表达式,作为分割的标识

position: 从目标字符串的第几个位置开始解析,默认为1。在oracle中字符串索引也是从1开始的,而不是0

occurrence:取出分割后的字符串数组的第几个子字符串,也是从1开始

modifier:匹配模式,"i"不区分大小写,"c"区分大小写。默认区分大小写

例如:

connect by level

level是树形结构中、表示层级的伪列。从1开始递增,到层级的最深层次结束。与connect by连用

connect by是递归查询的条件

详情查看大佬的文章【精选】Oracle connect by与level的使用_connect by level-优快云博客

对于单独的字符串或单行数据可直接使用如下语法进行拆分

SELECT
	REGEXP_SUBSTR('1,2,4,5', '[^,]+', 1,level) as num
from 
dual
CONNECT BY REGEXP_SUBSTR('1,2,4,5', '[^,]+', 1,level) is not null

但对于多行数据,使用上述语法往往会得到不尽人意的结果集

例如在如下表中:

执行:

SELECT
	id,
	REGEXP_SUBSTR(num, '[^,]+', 1,level) as num1

from 
numTable
CONNECT BY REGEXP_SUBSTR(num, '[^,]+', 1,level) is not null

得到:

明显多了很多数据,并且受行数影响,结果集数量随行数指数型暴增。为什么会出现这样的结果?

问题分析

是因为level本质是表示的层级数,并不是rownum这种单纯的序列。在connect by条件中若没有指定上下级关系,那么每一行都会作为其它行的父级与子级。

如果没有指定任何递归条件和查询条件,那么结果集应是如下的层级结构

但是条件中限制了"CONNECT BY REGEXP_SUBSTR(num, '[^,]+', 1,level) is not null",也就是"5"是没有第二、第三层级的,所以应该把"5"的第一层级以外的层级及其子层级去掉

再由于“REGEXP_SUBSTR(num, '[^,]+', 1,level) as num1”,把对应层级的字符串分割出来,第一层取第一个数字,第二层取第二个数字...

这样便得到了上面的 5-2-4-1-2-4的顺序。

解决问题

知道了多余的结果是重复递归造成的,所以要加上必要的递归条件过滤多余的结果。

既然原因是没有指定父子连接条件,那就指定一个条件,它限制自己只能是自己的父级或子级

and prior id = id

-- prior的位置在此处不影响结果,它只是决定树的生长方向
-- and id = prior id

但是可以看出这是一个死循环的条件,父子关系相互指向,会导致 5->5->5->5......   124->124->124... 死循环

加上如下条件,终止死循环(至于为什么能终止循环,原理尚不清楚,待补充)

and PRIOR DBMS_RANDOM.VALUE() IS NOT NULL

-- 需要一个随机性足够大的随机数
-- 在某些场景或框架中,不允许直接调用DBMS_RANDOM.VALUE()
-- 可以使用SYS_GUID()替代

至此得到了期望的结果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值