We get the data in the following format which I am able to convert using Regular expression query. The data is start and end data of tasks concatenated with pipe.
Data:
|2020/04/26|2020/05/02|2020/05/03|2020/05/10
Query:
select REGEXP_SUBSTR (:p, '[^|]+', 1, level) as periods from dual
connect by level <= length (regexp_replace(:p, '[^|]+'))
Result:
2020/04/26
2020/05/02
2020/05/03
2020/05/10
We need to separate it with start date and end date. The number of start date and end date combination would is dynamic. But there will be end date for start date and we won't get null.
Expected Result
START DATE END DATE
2020/04/26 2020/05/02
2020/05/03 2020/05/10
Thanks in advance.
You could do arithmetics and conditional aggregation:
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
A solution that will work if you have one or more input rows (whereas your hierarchical query will generate exponentially increasing numbers of duplicate rows if you input more than one row of data to it).
Convert pairs of dates to XML and then use XMLTABLE
to convert:
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>\1</start><end>\2</end></row>'
),
','
)
)
COLUMNS
start_date DATE PATH '/row/start',
end_date DATE PATH '/row/end'
) x
So, for your test data:
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
This outputs:
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
Or, if you only have a single input, you can split your data on pairs:
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})' )
Which outputs:
START_DATE | END_DATE:--------- |:--------- 2020/04/26 | 2020/05/02 2020/05/03 | 2020/05/10
db<>fiddle here
Or use:
SELECT *
FROM XMLTABLE(
( LTRIM(
REGEXP_REPLACE(
:p,
'\|(\d{4}/\d{2}/\d{2})\|(\d{4}/\d{2}/\d{2})',
',<row><start>\1</start><end>\2</end></row>'
),
','
)
)
COLUMNS
start_date DATE PATH '/row/start',
end_date DATE PATH '/row/end'
)
db<>fiddle here
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.