Home > other >  PL sql substring issue
PL sql substring issue

Time:07-20

I have a column as below. there are multiple columns like this with different data. enter image description here

I need output like this

enter image description here

I need values that inside | | symbols. I tried like below But not successes. I am new to PL SQL. Please give some suggestions to this. Do i need a cursor or loop for do this

SELECT 
           regexp_substr(profile_value,'\|([^|] )\|',1,1,NULL,1) 
FROM table1

As a result of my query i am getting only WO_NO=1^

CodePudding user response:

This is how I understood it.

SQL> with
  2  test (col) as
  3    (select '9D49|WO_NO=1^|WO no: 1;D972|WO_NO=60003^|WO no: 60003;AED1' from dual union all
  4     select '1234|WO_NO=25^|WO no:25;797|WO_NO=26^'                      from dual
  5    ),
  6  temp as
  7    (select regexp_substr(col, '[^|] ', 1, column_value) val,
  8            column_value cv,
  9            col
 10     from test cross join
 11       table(cast(multiset(select level from dual
 12                           connect by level <= regexp_count(col, '\|')   1
 13                          ) as sys.odcinumberlist))
 14    )
 15  select listagg(val, ' or ') within group (order by cv) result
 16  from temp
 17  where substr(val, 1, 5) = 'WO_NO'
 18  group by col ;

RESULT
--------------------------------------------------------------------------------
WO_NO=25^ or WO_NO=26^
WO_NO=1^ or WO_NO=60003^

SQL>

CodePudding user response:

Splitting the string into items

Assuming for now that Table1 has a single row, you can write a recursive query and a regular expression to split the string in a single select.

level denotes the level of recursion. regexp_substr lets you specify the occurrence in the string. connect by provides the exit condition: taking the next item, until there is none.

select
  level as ItemNr,
  regexp_substr(profile_value, '[^|] ', 1, level) as Item 
from
  Table1
connect by
  regexp_substr(profile_value, '[^|] ', 1, level) is not null

Combining the wanted items into a new string

You could use ListAgg with a case condition, to combine only the items where level is an even number back into a single string, with the string ' or ' as a separator.

select
  listagg(
    case when mod(level, 2) = 0 then
      regexp_substr(profile_value, '[^|] ', 1, level)
    end, ' or ') as Items 
from
  Table1
connect by
  regexp_substr(profile_value, '[^|] ', 1, level) is not null

For multiple rows

The query above doesn't work well by itself if Table1 has multiple rows. It assumes a single input value. Using yet another trick, cross apply, you can make this a subquery that does this transformation for each of the rows of your table.

The query below shows the original profile_value column, and the transformed items for each of the rows in Table1.

select
  t.*, x.*
from
  Table1 t
  cross apply (
    select
      listagg(
        case when mod(level, 2) = 0 then
          regexp_substr(profile_value, '[^|] ', 1, level)
        end, ' or ') as Items
    from 
      dual -- just selecting a single profile_value
    connect by
      regexp_substr(profile_value, '[^|] ', 1, level) is not null
  ) x

In the end this works okay for a not to large set of data, but you're storying structured data into a string, and querying this is not very efficient.

If you need to repeat this query a lot, and the number of rows in Table1 is growing, it's probably better to store it in a more structured way. If this data is coming from an external source, you could dump it in a temporary table, and transform it using the queries of this answer, to write it to a final, better structured table (or tables) for repeated use.

  • Related