线性扩展 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
这假定同一行上的间隔始终具有相同的长度,如示例数据中所示。如果不是这种情况,你会解释你想如何处理它。
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
我在 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
这假定同一行上的间隔始终具有相同的长度,如示例数据中所示。如果不是这种情况,你会解释你想如何处理它。
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