在Oracle中将连字符分隔的字符串拆分为行

问题描述

我在表中有一列存储一系列数据。有些数据使用连字符分隔,而另一些则使用逗号分隔。我想将数据分成几行。关键是逗号分隔的值在每个逗号之后都被视为单个值,但是对于连字符来说,这意味着一种数据范围。 例如,如果字符串是类似'A1,A2,A4'的字符串,则表示存在3个值,并将其转换为3行。还有一个类似'A1-A4'的字符串,它表示有4个值,并将转换为4行,因为连字符代表表示起始值和结束值的值范围。

我能够转换逗号分隔的值,但是不确定如何在oracle中拆分连字符分隔的范围。

  SELECT regexp_substr('A1,A4','[^,]+',1,level) as a
  FROM dual
  CONNECT BY regexp_substr('A1,level) is not null

在ddl之上,将提供的字符串转换为3行。

  SELECT regexp_substr('A1-A4','[^-]+',level) as a
  FROM dual
  CONNECT BY regexp_substr('A1-A4',level) is not null

但是上面的查询应该返回4行,但是我不确定如何实现。 有什么想法吗?

解决方法

假设模式将始终是一对具有相同前缀(此处为“ A”)的值,并且每个值后面都有一个数字,则可以使用其他正则表达式来提取前缀,起始数字和结束数字:>

SELECT
  regexp_substr('A1-A4','(.*?)(\d+)-.*?(\d+)',1,null,1) as prefix,to_number(regexp_substr('A1-A4',2)) as start_num,3)) as end_num
FROM dual

PREFIX  START_NUM    END_NUM
------  ---------  ---------
A               1          4

,然后在递归CTE中使用它来获取介于两者之间的值:

WITH rcte (prefix,num,end_num) AS (
  SELECT
    regexp_substr('A1-A4',1),2)),3))
  FROM dual
  UNION ALL
  SELECT prefix,num + 1,end_num
  FROM rcte
  WHERE num < end_num
)
SELECT prefix || num as result
FROM rcte

RESULT
------
A1
A2
A3
A4

db<>fiddle

您可以在一个查询中结合使用这两种方法,进一步假设您在同一字符串中没有混合使用逗号分隔的值和范围; db<>fiddle demo。如果您确实有混合的话,可以将它们串联应用。将以逗号分隔的行转换为行,然后进一步处理那些实际上是连字符范围的新行。

,

带有扩展样本数据的完整示例:

with t(n,str) as (
select 1,'A1,A2,A4' from dual union all
select 2,'B1,B4,B7-B11' from dual union all
select 3,'C1,C3,C5-C7' from dual union all
select 4,'XY1,XT3,ZZ5-ZZ7' from dual 
)
select *
from t,lateral(
        select level part_n,regexp_substr(str,'[^,]+',level) part
        from dual 
        connect by level<=regexp_count(str,]+')
     ),lateral(
        select 
           level sub_part_n,nvl(
              regexp_substr(part,'(\w+)(\d+)[ -]+\1(\d+)',1)
              ||
              (regexp_substr(part,2) + level -1),part
             )
             as subpart
        from dual 
        connect by level<= regexp_substr(part,3)
                         - regexp_substr(part,2)
                         + 1
    )

结果:

         N STR                   PART_N PART       SUB_PART_N SUBPART
---------- ----------------- ---------- ---------- ---------- ----------
         1 A1,A4                 1 A1                  1 A1
         1 A1,A4                 2 A2                  1 A2
         1 A1,A4                 3 A4                  1 A4
         2 B1,B7-B11             1 B1                  1 B1
         2 B1,B7-B11             2 B4                  1 B4
         2 B1,B7-B11             3 B7-B11              1 B7
         2 B1,B7-B11             3 B7-B11              2 B8
         2 B1,B7-B11             3 B7-B11              3 B9
         2 B1,B7-B11             3 B7-B11              4 B10
         2 B1,B7-B11             3 B7-B11              5 B11
         3 C1,C5-C7              1 C1                  1 C1
         3 C1,C5-C7              2 C3                  1 C3
         3 C1,C5-C7              3 C5-C7               1 C5
         3 C1,C5-C7              3 C5-C7               2 C6
         3 C1,C5-C7              3 C5-C7               3 C7
         4 XY1,ZZ5-ZZ7          1 XY1                 1 XY1
         4 XY1,ZZ5-ZZ7          2 XT3                 1 XT3
         4 XY1,ZZ5-ZZ7          3 ZZ5-ZZ7             1 ZZ5
         4 XY1,ZZ5-ZZ7          3 ZZ5-ZZ7             2 ZZ6
         4 XY1,ZZ5-ZZ7          3 ZZ5-ZZ7             3 ZZ7
,

如果应用于具有多行的表,则可以尝试执行以下操作(请参见代码中的注释):

SQL> with test (id,col) as
  2  -- sample data
  3    (select 1,A4' from dual union all
  4     select 2,'BX8-BX11' from dual union all
  5     select 3,C4'    from dual union all
  6     select 4,'D6-D9'    from dual
  7    ),8  temp as
  9  -- split e.g. "BX8-BX11" to "BX",8 and 11
 10    (select id,11            regexp_substr(col,'^[[:alpha:]]+') alp,12            to_number(regexp_substr(col,'\d+',1)) num1,13            to_number(regexp_substr(col,2)) num2
 14     from test
 15     where instr(col,'-') > 0
 16    )
 17  -- trivial - split comma-separated values to rows
 18  select id,19         regexp_substr(col,column_value) val
 20  from test cross join table(cast(multiset(select level from dual
 21                                           connect by level <= regexp_count(col,',') + 1
 22                                          ) as sys.odcinumberlist))
 23  where instr(col,'-') = 0
 24  union all
 25  -- create rows for values that are dash-separated
 26  select id,27         alp || to_char(num1 + column_value - 1) val
 28  from temp cross join table(cast(multiset(select level from dual
 29                                           connect by level <= num2 - num1 + 1
 30                                          ) as sys.odcinumberlist))
 31  order by id,val;

        ID VAL
---------- ------------------------------------------------
         1 A1
         1 A2
         1 A4
         2 BX10
         2 BX11
         2 BX8
         2 BX9
         3 C1
         3 C4
         4 D6
         4 D7
         4 D8
         4 D9

13 rows selected.

SQL>
,

或者:

JLabel label = new JLabel(icon,JLabel.CENTER); AddImages.add(label); AddImages.revalidate(); 用一系列连续的整数输入,并选择第i个连续的非逗号/连续的非连字符...。

CROSS JOIN