如何使用 , 和 : Oracle 中的分隔符将 CLOB 对象拆分为多条记录
Posted
技术标签:
【中文标题】如何使用 , 和 : Oracle 中的分隔符将 CLOB 对象拆分为多条记录【英文标题】:How to split a CLOB object using , and : delimiter in Oracle into multiple records 【发布时间】:2016-07-15 11:38:07 【问题描述】:我有一个如下所示的 CLOB 对象示例。我想先用分隔符“,”将其拆分,并将其保存在临时表中以备后用。
ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0
我想在每一行中以以下格式保存结果。
Column_Name
__________________________
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
我尝试使用 REGEXP_SUBSTR 函数
select
regexp_substr('ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0', '[^,]+', 1, 1) Column_Name
from dual;
上面的查询给了我如下的一条记录
Column_Name
__________________________
ABCDEF:PmId12345RmLn1VlId0
谁能帮我解决这个问题。
【问题讨论】:
【参考方案1】:这是使用递归分解子查询(Oracle 11.2 及更高版本)的解决方案:
with inputs ( str ) as (
select to_clob('ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0')
from dual
),
prep ( s, n, token, st_pos, end_pos ) as (
select ',' || str || ',', -1, null, null, 1
from inputs
union all
select s, n+1, substr(s, st_pos, end_pos - st_pos),
end_pos + 1, instr(s, ',', 1, n+3)
from prep
where end_pos != 0
)
select n as idx, token as column_name
from prep
where n > 0;
IDX COLUMN_NAME
------ ----------------------------
1 ABCDEF:PmId12345RmLn1VlId0
2 ABCDEF:PmId12345RmLn1VlId0
3 ABCDEF:PmId12345RmLn1VlId0
4 ABCDEF:PmId12345RmLn1VlId0
5 ABCDEF:PmId12345RmLn1VlId0
注意事项:
您说的是 CLOB,但在您的示例中,您是从 varchar2 字符串中提取的。我添加了to_clob()
以查看它是否/如何在 CLOB 上工作。
我使用了instr
和substr
,因为它们的性能通常(通常?)比它们的regexp
等效物更好甚至更好。
我保存了输入字符串中每个子字符串的“索引”;在某些情况下,输入字符串中标记的顺序很重要。 (但在您的示例中不是,您只是将相同的标记重复了五次。)
如果您需要更好的性能,特别是如果您的 CLOB 非常大,您最好使用 dbms_lob.substr
和 dbms_lob.instr
- 请参阅 Performance of SUBSTR on CLOB,尤其是 Alex Poole 的答案,以及此处的文档:http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_lob.htm#BABEAJAD。请注意与常规 substr
/ instr
的语法差异。
【讨论】:
感谢您的回复。这很完美,它返回 CLOB 对象。我可以从中返回字符串吗? 这将是使用dbms_lob.substr
的另一个好处;它需要一个CLOB
输入字符串并输出一个VARCHAR2
。有趣的是,这是记录在案的,虽然我无法在 Oracle 文档中快速看到它在 CLOB
上显示标准 substr
返回 CLOB
(但我当然相信你)。
您分享如此强大的 SQL 真是太棒了,节省了我很多时间,因为我们在使用 REGEXP_SUBSTR 拆分大型 varchar 时遇到了字符串太长的问题,谢谢...
如果字符串长度为 10,000 个字符,这将不起作用。还有什么办法吗?【参考方案2】:
以防万一您真的只是想按照您的示例解析一个长字符串。如果您需要在列表中查看值的索引,请在选择中包含“级别”:
SQL> with tbl(str) as (
select 'ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmI
d12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:P
mId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0,ABCDEF:PmId12345RmLn1VlId0'
from dual
)
select regexp_substr(str, '(.*?)(,|$)', 1, level, NULL, 1) column_name
from tbl
connect by regexp_substr(str, '(.*?)(,|$)', 1, level) is not null;
COLUMN_NAME
--------------------------------------------------------------------------------
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
ABCDEF:PmId12345RmLn1VlId0
16 rows selected.
SQL>
【讨论】:
这会在我们使用 connect by 子句时产生性能问题。 这是为了以防你真正使用的是字符串而不是 clob。 如果字符串大于4k,会抛出异常。 嗯,是的,字符串字面量太长了。这并不奇怪。以上是关于如何使用 , 和 : Oracle 中的分隔符将 CLOB 对象拆分为多条记录的主要内容,如果未能解决你的问题,请参考以下文章
如何根据一个字段是不是包含oracle sql中的逗号分隔字符串将单行拆分为多行?