iamP iamP - 7 months ago 8
SQL Question

Rearranging a string value with delimiters in a sql select query

I have a column with string value having delimiters as shown below which I use in the select part of sql query.

0040~0040~0040~0040~0040^00~00~00~01~05^100~001~010~011~015^00~00~00~01~05


individual heads are separated using
'^'
as shown below.

Head1 = 0040~0040~0040~0040~0040

Head2 = 00~00~00~01~05

Head3 = 100~001~010~011~015

Head4 = 00~00~00~01~05


all 4 heads have same number of entries which are
'~'
delimited(entries not necessarily 5).

What I need is merge first entry from all 4 heads into one. As shown below.

0040-00-100-00
delimited by
'-'


then for the second entry and so on for all the entries.

if the number of entries are 4 like my string, the formatted output should look like this(each entry separated by comma):

0040-00-100-00,0040-00-001-00,0040-00-010-00,0040-01-011-01,
0040-05-015-05


I want to do this in the select query where I fetch this column value.

example..

select x,y,z,(this is where I want this changes to be done.) from abc

Answer

The problem you have presented is not one easily solved limiting yourself to the inside of a select statement. Though you said that you didn't, if you knew how many "heads" there were going to be in each string, you could set up a single, complicated, and very long select statement (with your "string value having delimiters" called foobar):

SELECT x,y,z, SUBSTR(foobar, 1, INSTR(foobar, '~') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^') + 1, INSTR(foobar, '~', INSTR(foobar, '^')) - INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) || ',' || SUBSTR(foobar, INSTR(foobar, '~') + 1, INSTR(foobar, '~', 1, 2) - INSTR(foobar, '~', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) 
FROM abc

And that only gets the first two "entries", however it will get each "head", regardless of length and it can be extended to include more entries, but it would not work if there were only two entries and the select statement was extended for five entries, which is impractical since you said that a row may have any number of entries. There is a way to achieve this using a CASE statement, but it is very long, and very complex. Here is a sample query that may be what you are looking for:

SELECT x, y, z,
  CASE (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1)
    WHEN 1 THEN SUBSTR(foobar, 1, INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 1) + 1, INSTR(foobar, '^', 1, 2) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '^', 1, 3) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, LENGTH(foobar) - INSTR(foobar, '^', 1, 3))
    WHEN 2 THEN SUBSTR(foobar, 1, INSTR(foobar, '~') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^') + 1, INSTR(foobar, '~', INSTR(foobar, '^')) - INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) || ',' || SUBSTR(foobar, INSTR(foobar, '~') + 1, INSTR(foobar, '^', 1, 1) - INSTR(foobar, '~', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1), 1) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) 
    ELSE 'Too many entries: ' || (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) || ' entries given of 2 maximum'
  END AS SAMPLE_OUTPUT
FROM abc;

The query above (when extended beyond its current capacity of two entries) will organise your "heads". It is very long and complex, but the process may be simpler if you expand your efforts beyond the select clause. One last note: I recommend using a column alias for the case statement for reasons that become very obvious should you attempt to test the code without one.