线性扩展 oracle table 个字段

expand oracle table fields linearly

我在 oracle 中有以下 table:

ID    field_1    field_2
1       1-5        1-5
1      20-30      55-65
2       1-8       10-17
2      66-72      80-86

我需要将此 table 转换为以下格式,其中 field_1 和 field_2 必须线性匹配:

   ID   field_1  field_2
    1      1       1
    1      2       2
    1      3       3
    1      4       4
    1      5       5
    1      20      55
    1      21      56
    1      22      57
    1      23      58
    1      24      59
    1      25      60
    1      26      61
    1      27      62
    1      28      63
    1      29      64
    1      30      65
    2      1       10
    2      2       11
    2      3       12
    2      4       13
    2      5       14
    2      6       15
    2      7       16
    2      8       17
    2      66      80
    2      67      81
    2      68      82
    2      69      83
    2      70      84
    2      71      85
    2      72      86

知道原始 table 包含数千条记录

,什么是完成此操作最简单和最快的方法

一个选项使用递归查询。从 11gR2 开始,Oracle 支持标准递归通用 table 表达式,因此您可以:

with cte(id, field_1, field_2, max_field_1, max_field_2) as (
    select 
        id, 
        to_number(regexp_substr(field_1, '^\d+')), 
        to_number(regexp_substr(field_2, '^\d+')),
        to_number(regexp_substr(field_1, '\d+$')), 
        to_number(regexp_substr(field_2, '\d+$'))
    from mytable
    union all
    select
        id,
        field_1 + 1,
        field_2 + 1,
        max_field_1,
        max_field_2
    from cte
    where field_1 < max_field_1
)
select id, field_1, field_2 from cte order by id, field_1   

这假定同一行上的间隔始终具有相同的长度,如示例数据中所示。如果不是这种情况,你会解释你想如何处理它。

Demo on DB Fiddle:

ID | FIELD_1 | FIELD_2
-: | ------: | ------:
 1 |       1 |       1
 1 |       2 |       2
 1 |       3 |       3
 1 |       4 |       4
 1 |       5 |       5
 1 |      20 |      55
 1 |      21 |      56
 1 |      22 |      57
 1 |      23 |      58
 1 |      24 |      59
 1 |      25 |      60
 1 |      26 |      61
 1 |      27 |      62
 1 |      28 |      63
 1 |      29 |      64
 1 |      30 |      65
 2 |       1 |      10
 2 |       2 |      11
 2 |       3 |      12
 2 |       4 |      13
 2 |       5 |      14
 2 |       6 |      15
 2 |       7 |      16
 2 |       8 |      17
 2 |      66 |      80
 2 |      67 |      81
 2 |      68 |      82
 2 |      69 |      83
 2 |      70 |      84
 2 |      71 |      85
 2 |      72 |      86

下面使用的 lateral 子句从 Oracle 12.1 开始可用。对于旧版本,connect by 分层查询可能仍然是最快的,但需要更加小心地编写它(并且 比使用 connect by 在横向连接中)。

当然,最大的假设是输入始终采用数字-破折号-数字的形式,并且对于每一行,上下限之间的差异在两列中是相同的。我什至没有尝试检查。

select t.id, l.field_1, l.field_2
from   mytable t, 
       lateral (select to_number(substr(field_1, 1, instr(field_1, '-') - 1))
                              + level - 1 as field_1,
                       to_number(substr(field_2, 1, instr(field_2, '-') - 1))
                              + level - 1 as field_2
                from   dual
                connect by level <= 
                   to_number(substr(field_1, instr(field_1, '-') + 1))
                 - to_number(substr(field_1, 1, instr(field_1, '-') - 1)) + 1
               ) l
;

您可以将 cross join 与生成的值一起使用,如下所示:

SELECT ID, 
       to_number(regexp_substr(field_1, '[0-9]+',1,1)) + column_value - 1 AS FIELD_1, 
       to_number(regexp_substr(field_2, '[0-9]+',1,1)) + column_value - 1 AS FIELD_2
  FROM your_table
cross join
     table(cast(multiset(select level from dual
                         connect by level <= 
                                 to_number(regexp_substr(field_1, '[0-9]+',1,2)) 
                                 - to_number(regexp_substr(field_1, '[0-9]+',1,1)) 
                                 + 1
                        ) as sys.odcinumberlist))
ORDER BY 1,2