如何使用 , 和 : 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 上工作。

我使用了instrsubstr,因为它们的性能通常(通常?)比它们的regexp 等效物更好甚至更好。

我保存了输入字符串中每个子字符串的“索引”;在某些情况下,输入字符串中标记的顺序很重要。 (但在您的示例中不是,您只是将相同的标记重复了五次。)

如果您需要更好的性能,特别是如果您的 CLOB 非常大,您最好使用 dbms_lob.substrdbms_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中的行?

如何根据一个字段是不是包含oracle sql中的逗号分隔字符串将单行拆分为多行?

如何遍历 Oracle PLSQL 中的分隔列表

如何根据oracle plsql中列中的逗号分隔值拆分选择查询行

拆分分隔符分隔的字符串并插入到oracle 11中的表中

Oracle如何获取逗号分隔字符串中的出现次数