Sql进阶:字段中包含CSV,如何通过Sql解析CSV成多行多列?

Sql进阶

一、问题描述

Oracle数据库中某个字段value是CLOB类型,存的是csv格式的数据,如下所示

classno value
1 name,age,sex,... '李世民',20,'M',...' '李治',18,'M',... '武则天',16,'F',... '李隆基',14,'M',...
2 ...

需要把上述clob类型的csv字段用Sql的方式展开,如上述csv字段有四行三列,就需要把上述字段转成实际的四行三列,如下所示

classno name age sex ...
1 李世民 20 M
1 李治 18 M
1 武则天 16 F
1 李隆基 14 M
2 ... ... ...

二、解决思路

<一>、拆成多行

  • 按照换行符拆分一个个的列表,上述换行符是\n,按照\n进行拆分比较难写,考虑先把\n替换成其它符号,如分号
sql 复制代码
换行符在oracle中用chr(10)表示
select replace(value,chr(10),';') as value from table
  • 按照换行符进行拆分字符串
sql 复制代码
select to_char(regexp_substr(value,'[^;]+',1,level) as split_value
  from table
connect by level <= regexp_count(value,'[^;]+',1)
    and prior class_no = class_no
    and prior sys_guid() is not null

regexp_substr()函数为拆分字符串,若没有connect by语句,只是

sql 复制代码
select to_char(regexp_substr(value'[^;]+',1) as split_value
  from table

则不会循环进行拆分,只会拆分第一段,比如我那个例子,只会获取到

classno value
1 '李世民',20,'M'
2 ...

CONNECT BY是Oracle SQL中的一个子句,用于定义层次结构或递归关系,从而进行层次结构数据的查询。

LEVEL是Oracle SQL中的一个伪列,用于在层次结构或递归查询中获取当前行的级别。

REGEXP_COUNT 用于计算字符串中正则表达式匹配的次数

上述level <= regexp_count(value,'[^;]+',1)就是递归停止的条件

prior条件指的是当前递归在哪个层级下运行,比如上述例子一个csv字段描述的是一个班级的事情,递归是在这个班级下运行,所以prior条件要加上prior class_no = classno,不然会造成数据重复

需要注意prior后接的条件需要能够限制某个递归层级,不然可能会造成数据不断的循环

若是有多个prior条件,可以

and prior col1 = col1

and prior col2 = col2

而不是

and prior col1 = col1 and col2 = col2

经过上述处理之后,得到的结果应该是

classno value
1 name,age,sex,...
1 '李世民',20,'M',...
1 ' '李治',18,'M',...
1 '武则天',16,'F',...
1 '李隆基',14,'M',
2 ...

已经拆成多行了,剩下的是拆成多列

<二>、拆成多列

  • 根据列的分隔符来拆分,以逗号为例
sql 复制代码
select regexp_substr(split_value,'[^,]+',1,1) as name,
       regexp_substr(split_value,'[^,]+',1,2) as age,
       regexp_substr(split_value,'[^,]+',1,3) as sex
   from table
  • 还是用regexp_substr函数来拆分,只不过不进行递归查询,

三、代码实现

sql 复制代码
with tmp as (
	select classno,replace(value,chr(10),';') as value
	  from table
),tmp1 as (
	select to_char(regexp_substr(value,'[^;]+',1,level)) as split_value,classno
	  from tmp
   connect by level <= regexp_count(value,'[^;]+',1)
	   and prior classno = classno
	   and prior sys_guid() is not null
),tmp2 as (
	select classno,
	       regexp_substr(value,'[^,]+',1,1) as name,
	       regexp_substr(value,'[^,]+',1,2) as age,
	       regexp_substr(value,'[^,]+',1,3) as sex
	  from tmp1
)
	select classno,
	       name,
	       age,
	       sex
	  from tmp2 
	 where name != 'name'
相关推荐
m0_613856294 小时前
mysql如何利用事务隔离级别解决特定业务冲突_mysql隔离方案选型
jvm·数据库·python
Adios7945 小时前
VPR:Pitts50K和Norland数据集下载
数据库
东风破1375 小时前
DM用户权限、表、约束等对象的基本操作,SQL日志的开启介绍
数据库·sql·dm达梦数据库
收获不止数据库5 小时前
达梦9发布会归来:AI 时代,我们需要一款什么样的数据库?
数据库·人工智能·ai·语言模型·数据分析
小宇的天下5 小时前
Virtuoso GUI 界面中的关键模块定义
数据库
bqq198610265 小时前
MySQL 5.7 与 MySQL 8.0 的主要区别
数据库·mysql
juniperhan5 小时前
Flink 系列第21篇:Flink SQL 函数与 UDF 全解读:类型推导、开发要点与 Module 扩展
java·大数据·数据仓库·分布式·sql·flink
Elastic 中国社区官方博客6 小时前
Elastic-caveman : 在不损失 Elastic 最佳效果的情况下,将 AI 响应 tokens 减少64%
大数据·运维·数据库·人工智能·elasticsearch·搜索引擎·全文检索
互联网推荐官6 小时前
上海软件定制开发全流程拆解:需求分析、技术选型与交付管理的工程实践
大数据·数据库·需求分析
专注API从业者7 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库