简体   繁体   中英

Convert 1 column to 2 columns in Oracle SQL

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)

Demo on DB Fiddle :

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM