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

需求场景

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

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

在现有结果上对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”区分大小写。默认区分大小写

例如:

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

connect by level

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

connect by是递归查询的条件

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

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

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

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

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

例如在如下表中:

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

执行:

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

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

得到:

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

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

问题分析

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

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

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

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

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

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

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

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

这样便得到了上面的 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()替代

oracle将一行分割后展示为多行至此得到了期望的结果

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/7040ce4299.html