Reputation: 5165
I have data
col1
[{value -> Apple, key -> Fruit}, {value -> White, key -> Colour}]
[{value -> Mango, key -> Fruit}, {value -> Black, key -> Colour}]
and I wanted to extract a value
value -> White
value -> Black
select col1, instr('key -> Colour' in col1), substr(col1, instr('key -> Colour')
from
(SELECT '[{value -> Apple, key -> Fruit}, {value -> White, key -> Colour}]' as col1 from dual
union all
SELECT '[{value -> Mango, key -> Fruit}, {value -> Black, key -> Colour}]' as col1 from dual
) tbl;
I have tried using position and substring, but I am unable to get. What can I try next?
Basically I need to search for a value key -> Colour and extract the value by reverse substring till { which will get me the value value -> White and value -> Black
Upvotes: 1
Views: 65
Reputation: 168470
You can use REGEXP_SUBSTR
to find the match:
SELECT REGEXP_SUBSTR(
col1,
'\{(key -> Colour, )?(value -> [^},]*?)(, key -> Colour)?\}',
1,
1,
NULL,
2
) AS colour
FROM tbl
Which, for the sample data:
CREATE TABLE tbl (col1) AS
SELECT EMPTY_CLOB() || '[{value -> Apple, key -> Fruit}, {value -> White, key -> Colour}]' from dual union all
SELECT EMPTY_CLOB() || '[{value -> Mango, key -> Fruit}, {value -> Black, key -> Colour}]' from dual;
Outputs:
COLOUR |
---|
value -> White |
value -> Black |
Alternatively, do not try to encode your own storage format. If you are going to use something that bears a passing resemblance to the JSON format then actually use the JSON format rather that an approximation.
UPDATE tbl
SET col1 = REGEXP_REPLACE(
col1,
'\{value -> (.*?), key -> (.*?)\}',
'{"value": "\1", "key": "\2"}'
)
Then you can use a proper JSON parser:
SELECT j.*
FROM tbl t
CROSS APPLY JSON_TABLE(
t.col1,
'$[*]'
COLUMNS (
key_name VARCHAR2(20) PATH '$.key',
value VARCHAR2(20) PATH '$.value'
)
) j
WHERE key_name = 'Colour'
Which outputs:
KEY_NAME | VALUE |
---|---|
Colour | White |
Colour | Black |
or:
SELECT j.*
FROM tbl t
CROSS APPLY JSON_TABLE(
t.col1,
'$'
COLUMNS (
fruit VARCHAR2(20) PATH '$[*]?(@.key=="Fruit").value',
colour VARCHAR2(20) PATH '$[*]?(@.key=="Colour").value'
)
) j
Which outputs:
FRUIT | COLOUR |
---|---|
Apple | White |
Mango | Black |
Upvotes: 2
Reputation: 4640
If you want to be less dependent on the order of "k -> v" pairs, you need to go further in the parsing with regexp:
with data(id, str) as (
SELECT 1, '[{value -> Apple, key -> Fruit}, {value -> White, key -> Colour}]' as col1 from dual
union all
SELECT 2, '[{value -> Mango, key -> Fruit}, {value -> Black, key -> Colour}]' as col1 from dual
union all
SELECT 3, '[{key -> Fruit, value -> Mango}, {key -> Colour, value -> Black}]' as col1 from dual
),
keyvalues as (
select id, level as grp,
regexp_replace(replace(regexp_substr(lst, '[^}]+', 1, level),'{',''), '^,\s+', '')
as keyvalues
from (
select
id, regexp_replace(str, '\[(.*)\]', '\1') as lst
from data
)
connect by id = prior id and level <= regexp_count(lst, '}') and prior sys_guid() is not null
),
pairs as (
select id, idx, grp, keyval,
substr(keyval, 1, instr(keyval,' ->') - 1) as key,
substr(keyval, instr(keyval,' -> ') + length(' -> ')) as value
from (
select id, level as idx,grp, trim( ' ' from regexp_substr(keyvalues, '[^,]+', 1, level)) as keyval
from keyvalues
connect by id = prior id and grp = prior grp and
regexp_substr(keyvalues, '[^,]+', 1, level) is not null and prior sys_guid() is not null
)
)
select v.id, v.keyval
from pairs k
join pairs v on k.id = v.id and k.grp = v.grp and k.idx <> v.idx
and v.key = 'value'
where k.value = 'Colour'
;
Upvotes: 1
Reputation: 3505
You can start the search with value -> <value>
ending with key -> Colour
using REGEXP_SUBSTR
, and then replace the ,key -> Colour
using REGEXP_REPLACE
select col1,
REGEXP_REPLACE
(REGEXP_SUBSTR
(col1, 'value -> [^,]*, key -> Colour'), ', key -> Colour') AS new_val
from
(SELECT '[{value -> Apple, key -> Fruit}, {value -> White, key -> Colour}]' as col1 from dual
union all
SELECT '[{value -> Mango, key -> Fruit}, {value -> Black, key -> Colour}]' as col1 from dual
) tbl;
COL1 | NEW_VAL |
---|---|
[{value -> Apple, key -> Fruit}, {value -> White, key -> Colour}] | value -> White |
[{value -> Mango, key -> Fruit}, {value -> Black, key -> Colour}] | value -> Black |
Upvotes: 1