oracle逗号分割列并转成多行

这篇博客介绍了如何在Oracle数据库中使用SQL处理逗号分隔的列,并将其转换为多行数据。通过示例展示了REGEXP_SUBSTR函数的用法,以及如何结合LEVEL函数进行关联查询,以达到与业务单元表关联并获取详细信息的目的。同时,文章提到了在大量数据下避免查询延迟的优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用场景介绍:

业务表A中一个字段存放用逗号分割的多个业务单元,现在需要将数据转成一个业务单元对应一个数据。然后关联出业务单元表B,获取更加详细的业务单元信息。

1、业务表A

在这里插入图片描述

2、业务单元表B

在这里插入图片描述

3、预期结果

1、按逗号分割后结果
在这里插入图片描述
2、与业务单元表 B关联结果
在这里插入图片描述

4、实现的sql

1、按逗号分割


-- 写法一
SELECT
	A.*,
	REGEXP_SUBSTR( A.L2UNIT_NAME, '[^,]+', 1, L ) AS L2UNIT_NAME_B,
	L 
FROM
	JYZB_DATA_PERM A,
	( SELECT LEVEL L FROM DUAL CONNECT BY LEVEL <= 100 ) 
WHERE
	L ( + ) <= LENGTH( A.L2UNIT_NAME ) - LENGTH( REPLACE ( A.L2UNIT_NAME, ',' ) ) + 1 
ORDER BY
	id
	
	
-- 写法二 
SELECT
	A.*,
	REGEXP_SUBSTR( A.L2UNIT_NAME, '[^,]+', 1, L ) AS L2UNIT_NAME_B,
	L 
FROM
	JYZB_DATA_PERM A,
	( SELECT LEVEL L FROM DUAL CONNECT BY LEVEL <= 100 ) --限制一下level数量,提高查询速度。否则数据较多的情况下,会一直查询不出结果
WHERE
	L ( + ) <= regexp_count ( A.L2UNIT_NAME, ',' ) + 1 -- 计算需要分割后的数量
ORDER BY
	id

2、关联查询

SELECT
	jj.*,
	ij.* 
FROM
	(
	SELECT
		cp.*,
		regexp_count ( CP.L2UNIT_NAME, ',' ) + 1 L2UNIT_NAME_count,
		REGEXP_SUBSTR( CP.L2UNIT_NAME, '[^,]+', 1, L ) AS L2UNIT_NAME_B,
		L 
	FROM
		JYZB_DATA_PERM CP,
		( SELECT LEVEL L FROM DUAL CONNECT BY LEVEL <= 100 ) 
	WHERE
		L ( + ) <= LENGTH( CP.L2UNIT_NAME ) - LENGTH( REPLACE ( CP.L2UNIT_NAME, ',' ) ) + 1 
	) jj
	LEFT JOIN invalid_JYZB_BASE_L2UNIT ij ON jj.L2UNIT_NAME_B = ij.L2UNIT_NAME 
ORDER BY
	id

5、sql分析

5.1 函数 REGEXP_SUBSTR

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

string:需要进行正则处理的字符串
pattern:进行匹配的正则表达式
position:起始位置,从字符串的第几个字符开始正则表达式匹配(默认为1) 注意:字符串最初的位置是1而不是0
occurrence:获取第几个分割出来的组(分割后最初的字符串会按分割的顺序排列成组)
modifier:模式(‘i’不区分大小写进行检索;‘c’区分大小写进行检索。默认为’c’)针对的是正则表达式里字符大小写的匹配

5.2 案列分析

5.2.1 案例1
SELECT REGEXP_SUBSTR('11a22A33a','[^A]+',1,1,'i') AS STR FROM DUAL;

说明:
在这里插入图片描述

结果: 11
分析: 正则表达式是以A为标识进行分割,而'i'标识不区分大小写,所以结果是11,而不是11a22

5.2.1 案例2
SELECT REGEXP_SUBSTR('11a22A33a','[^A]+',1,1,'c') AS STR FROM DUAL;

说明:在这里插入图片描述

结果: 11a22
分析: 正则表达式是以A为标识进行分割,而'c'标识区分大小写,所以结果是11a22,而不是11

5.3 关联REGEXP_SUBSTR函数

SELECT REGEXP_SUBSTR('11a22A33a','[^A]+',1,LEVEL,'c') AS STR FROM DUAL CONNECT BY LEVEL<=5;

结果:在这里插入图片描述

分析: 把要输出来的第几个子串,通过一个变量level转换成输出多少个子串。level<=5代表的是输出5个,没有的为null。这样显然会输出多余null值,这不是我们想要的。
优化:

SELECT
	REGEXP_SUBSTR( '11a22A33a', '[^A]+', 1, LEVEL, 'c' ) AS STR
FROM
	DUAL CONNECT BY LEVEL <= length( '11a22A33a' ) - length( REGEXP_REPLACE( '11a22A33a','A','' ) ) + 1;

或者:

SELECT
	REGEXP_SUBSTR( '11a22A33a', '[^A]+', 1, LEVEL, 'c' ) AS STR 
FROM
	DUAL CONNECT BY LEVEL <= ( regexp_count ( '11a22A33a', 'A' ) + 1 )

结果在这里插入图片描述

level扩展

Length函数

返回字符的个数

Select length('你好') from dual;  2
REGEXP_REPLACE 函数
-- 在字符串中搜索正则表达式模式并将该模式的每个匹配项替换为指定字符串
VARCHAR REGEXP_REPLACE(VARCHAR str, VARCHAR pattern, VARCHAR replacement)

入参
在这里插入图片描述
示例

Select REGEXP_REPLACE( '11a22A33a','A','' ) from dual;  

结果 — 11a2233a
在这里插入图片描述

REGEXP_COUNT函数

REGEXP_COUNT用于为正则表达式搜索字符串,且返回正则表达式出现次数

INTEGER REGEXP_COUNT
(
  srcstr    TEXT,
  pattern   TEXT,
  position  DEFAULT 1
  modifier  DEFAULT NULL
)      

参数
在这里插入图片描述

示例

-- regexp_count函数计算指定字符串在字符串中出现的个数
SELECT
	regexp_count ( '11a22A33a', 'A' ) AS count 
FROM
	dual;
-- 结果 1

6、注意

如果粗暴的使用逗号分割数据量比较大的情况下可能会出现查询时间很长。

SELECT
	A.*,
	REGEXP_SUBSTR( A.L2UNIT_NAME, '[^,]+', 1, LEVEL ) AS L2UNIT_NAME_B 
FROM
	JYZB_DATA_PERM A CONNECT BY LEVEL <= regexp_count ( A.L2UNIT_NAME, ',' ) + 1

在实际使用时,使用了此方式导致sql一直在执行,查询不出结果。
正确方式

SELECT
	A.*,
	REGEXP_SUBSTR( A.L2UNIT_NAME, '[^,]+', 1, L ) AS L2UNIT_NAME_B,
	L 
FROM
	JYZB_DATA_PERM A,
	( SELECT LEVEL L FROM DUAL CONNECT BY LEVEL <= 100 ) --限制一下level数量,提高查询速度。否则数据较多的情况下,会一直查询不出结果
WHERE
	L ( + ) <= regexp_count ( A.L2UNIT_NAME, ',' ) + 1  -- 计算需要分割后的数量
ORDER BY
	id

扩展连接

1、Oracle数据库中某个表中的某个字段的值是用逗号隔开的多个值,根据逗号拆分并从关联表中查出数据返回
2、mysql数据库中某个表中的某个字段的值是用逗号隔开的多个值,根据逗号拆分并从另一个表中查出数据返回

### Oracle SQL 多行转单行并用逗号分隔 在Oracle数据库中,可以使用 `LISTAGG` 函数将多行数据聚合到一行,并通过指定的分隔符连接这些值。此方法适用于需要汇总特定字段的情况。 对于早期版本的Oracle数据库,在不支持 `LISTAGG` 的情况下,则可以通过自定义方式实现相同功能,比如利用 `SYS_CONNECT_BY_PATH` 和层次查询来达到目的[^1]。 下面展示两种不同场景下的解决方案: #### 使用 LISTAGG 函数 (推荐) 当目标是获取某的不同值组合单一字符串时,可以直接调用 `LISTAGG` 来完操作。这里给出一个简单的例子说明如何应用该函数: ```sql SELECT department_id, LISTAGG(employee_name, ', ') WITHIN GROUP (ORDER BY employee_name) AS employees_list FROM employees GROUP BY department_id; ``` 这段代码会按照部门编号(`department_id`)进行分组,并把每个部门内的员工名字(`employee_name`)以逗号加空格的形式拼接起来形新的字段`employees_list`。 #### 对于旧版 Oracle 数据库或特殊情况 如果所在环境不允许使用 `LISTAGG` 或者有更复杂的需求,那么可以考虑采用如下替代方案之一: - **使用 SYS_CONNECT_BY_PATH** 这种方法依赖于构建一棵树形结构的数据集并通过路径表达式提取所需的结果。需要注意的是要设置合适的伪作为根节点以及调整排序逻辑确保最终输出顺序正确。 ```sql WITH emp_hierarchy AS ( SELECT e.department_id, e.employee_name, ROW_NUMBER() OVER(PARTITION BY e.department_id ORDER BY e.employee_name) rn, COUNT(*) OVER(PARTITION BY e.department_id) cnt FROM employees e ) SELECT DISTINCT eh.department_id, LTRIM(MAX(SYS_CONNECT_BY_PATH(eh.employee_name, ',')) KEEP(DENSE_RANK LAST ORDER BY eh.rn), ',') AS employees_list FROM emp_hierarchy eh CONNECT BY PRIOR eh.rn = eh.rn - 1 AND PRIOR eh.department_id = eh.department_id START WITH eh.rn = 1 GROUP BY eh.department_id; ``` 上述脚本首先计算每一行在其所属分区中的相对位置 (`ROW_NUMBER()`),接着基于这个序号建立父子关系链路最后再做一次最大值保留去除多余前缀得到期望格式化的表[^2].
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值