在 Oracle 中将 1 列转换为 2 列 SQL
Convert 1 column to 2 columns in Oracle SQL
我们获得了以下格式的数据,我可以使用正则表达式查询对其进行转换。数据是用管道连接的任务的开始和结束数据。
数据:
|2020/04/26|2020/05/02|2020/05/03|2020/05/10
查询:
select REGEXP_SUBSTR (:p, '[^|]+', 1, level) as periods from dual
connect by level <= length (regexp_replace(:p, '[^|]+'))
结果:
2020/04/26
2020/05/02
2020/05/03
2020/05/10
我们需要将它与开始日期和结束日期分开。开始日期和结束日期组合的数量是动态的。但是开始日期会有结束日期,我们不会得到空值。
预期结果
START DATE END DATE
2020/04/26 2020/05/02
2020/05/03 2020/05/10
提前致谢。
您可以进行算术运算和条件聚合:
select
max(case when mod(lvl, 2) = 0 then periods end) start_date,
max(case when mod(lvl, 2) = 1 then periods end) end_date
from (
select
regexp_substr (:p, '[^|]+', 1, level) as periods,
level - 1 as lvl
from dual
connect by level <= length (regexp_replace(:p, '[^|]+'))
) t
group by trunc(lvl / 2)
START_DATE | END_DATE
:--------- | :---------
2020/04/26 | 2020/05/02
2020/05/03 | 2020/05/10
如果您有一个或多个输入行,该解决方案将起作用(而如果您向其中输入多行数据,您的分层查询将生成数量呈指数增长的重复行)。
将日期对转换为 XML,然后使用 XMLTABLE
转换:
SELECT id,
x.*
FROM test_data t
CROSS JOIN
XMLTABLE(
( LTRIM(
REGEXP_REPLACE(
t.value,
'\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})',
',<row><start></start><end></end></row>'
),
','
)
)
COLUMNS
start_date DATE PATH '/row/start',
end_date DATE PATH '/row/end'
) x
因此,对于您的测试数据:
CREATE TABLE test_data ( id, value ) AS
SELECT 1, '|2020/04/26|2020/05/02|2020/05/03|2020/05/10' FROM DUAL UNION ALL
SELECT 2, '|2020/06/01|2020/06/02' FROM DUAL
这输出:
ID | START_DATE | END_DATE
-: | :--------- | :--------
1 | 26-APR-20 | 02-MAY-20
1 | 03-MAY-20 | 10-MAY-20
2 | 01-JUN-20 | 02-JUN-20
db<>fiddle here
或者,如果您只有一个输入,则可以成对拆分数据:
SELECT REGEXP_SUBSTR ( :p, '\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})', 1, level, NULL, 1 ) as start_date,
REGEXP_SUBSTR ( :p, '\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})', 1, level, NULL, 2 ) as end_date
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( :p, '\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})' )
输出:
START_DATE | END_DATE
:--------- | :---------
2020/04/26 | 2020/05/02
2020/05/03 | 2020/05/10
db<>fiddle here
或使用:
SELECT *
FROM XMLTABLE(
( LTRIM(
REGEXP_REPLACE(
:p,
'\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})',
',<row><start></start><end></end></row>'
),
','
)
)
COLUMNS
start_date DATE PATH '/row/start',
end_date DATE PATH '/row/end'
)
db<>fiddle here
我们获得了以下格式的数据,我可以使用正则表达式查询对其进行转换。数据是用管道连接的任务的开始和结束数据。
数据:
|2020/04/26|2020/05/02|2020/05/03|2020/05/10
查询:
select REGEXP_SUBSTR (:p, '[^|]+', 1, level) as periods from dual
connect by level <= length (regexp_replace(:p, '[^|]+'))
结果:
2020/04/26
2020/05/02
2020/05/03
2020/05/10
我们需要将它与开始日期和结束日期分开。开始日期和结束日期组合的数量是动态的。但是开始日期会有结束日期,我们不会得到空值。
预期结果
START DATE END DATE
2020/04/26 2020/05/02
2020/05/03 2020/05/10
提前致谢。
您可以进行算术运算和条件聚合:
select
max(case when mod(lvl, 2) = 0 then periods end) start_date,
max(case when mod(lvl, 2) = 1 then periods end) end_date
from (
select
regexp_substr (:p, '[^|]+', 1, level) as periods,
level - 1 as lvl
from dual
connect by level <= length (regexp_replace(:p, '[^|]+'))
) t
group by trunc(lvl / 2)
START_DATE | END_DATE :--------- | :--------- 2020/04/26 | 2020/05/02 2020/05/03 | 2020/05/10
如果您有一个或多个输入行,该解决方案将起作用(而如果您向其中输入多行数据,您的分层查询将生成数量呈指数增长的重复行)。
将日期对转换为 XML,然后使用 XMLTABLE
转换:
SELECT id,
x.*
FROM test_data t
CROSS JOIN
XMLTABLE(
( LTRIM(
REGEXP_REPLACE(
t.value,
'\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})',
',<row><start></start><end></end></row>'
),
','
)
)
COLUMNS
start_date DATE PATH '/row/start',
end_date DATE PATH '/row/end'
) x
因此,对于您的测试数据:
CREATE TABLE test_data ( id, value ) AS
SELECT 1, '|2020/04/26|2020/05/02|2020/05/03|2020/05/10' FROM DUAL UNION ALL
SELECT 2, '|2020/06/01|2020/06/02' FROM DUAL
这输出:
ID | START_DATE | END_DATE -: | :--------- | :-------- 1 | 26-APR-20 | 02-MAY-20 1 | 03-MAY-20 | 10-MAY-20 2 | 01-JUN-20 | 02-JUN-20
db<>fiddle here
或者,如果您只有一个输入,则可以成对拆分数据:
SELECT REGEXP_SUBSTR ( :p, '\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})', 1, level, NULL, 1 ) as start_date,
REGEXP_SUBSTR ( :p, '\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})', 1, level, NULL, 2 ) as end_date
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( :p, '\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})' )
输出:
START_DATE | END_DATE :--------- | :--------- 2020/04/26 | 2020/05/02 2020/05/03 | 2020/05/10
db<>fiddle here
或使用:
SELECT *
FROM XMLTABLE(
( LTRIM(
REGEXP_REPLACE(
:p,
'\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})',
',<row><start></start><end></end></row>'
),
','
)
)
COLUMNS
start_date DATE PATH '/row/start',
end_date DATE PATH '/row/end'
)
db<>fiddle here