Reputation: 5
I have below values in table, and need to set valid_values =6 when found >6
ID VALUE VALID_VALUES
---------- --------------- ---------------------------------------------
555 OFF OFF,1,2,3,4,5,6,7,8,9,10
So after change desired output would be as below,
SQL> /
FIS_ID VALUE VALID_VALUES
---------- --------------- ---------------------------------------------
417 OFF OFF,1,2,3,4,5,6,6,6,6,6
Upvotes: 0
Views: 417
Reputation: 168051
You do not need to split and aggregate; instead you can use a regular expression to find either 2-or-more-digit numbers (i.e. [1-9]\d+
) or 1-digit values higher than 6 (i.e. [789]
) and could include leading zeroes if these may appear in your data set (since you are storing numbers as text):
SELECT id,
value,
REGEXP_REPLACE(
valid_values,
'0*[1-9]\d+|0*[789]',
'6'
) AS valid_values
FROM table_name
Which, for the sample data:
CREATE TABLE table_name ( ID, VALUE, VALID_VALUES ) AS
SELECT 555, 'OFF', 'OFF,1,2,3,4,5,6,7,8,9,10' FROM DUAL UNION ALL
SELECT 666, 'OFF', 'OFF,1,2,3,4,5,6,42,05,0123' FROM DUAL;
Outputs:
ID | VALUE | VALID_VALUES --: | :---- | :---------------------- 555 | OFF | OFF,1,2,3,4,5,6,6,6,6,6 666 | OFF | OFF,1,2,3,4,5,6,6,05,6
db<>fiddle here
Upvotes: 2
Reputation: 1555
For this solution, you need to split using LAG analytic function, before replacing and aggregating as below :
select ID, VALUE
, listagg(
case when regexp_like(separate_value, '^\d+$')
then case when separate_value > 6
then '6'
else separate_value
end
else separate_value
end
, ',') within group (order by lvl) VALID_VALUES
from (
select ID, VALUE
, lvl, substr(VALID_VALUES, lag(pos, 1, 0)over(order by lvl)+1, pos - lag(pos, 1, 0)over(order by lvl)-1) separate_value
from (
select ID, VALUE, VALID_VALUES||','VALID_VALUES, level lvl, instr(VALID_VALUES||',', ',', 1, level)pos
from your_table
connect by level <= length(VALID_VALUES||',')-length(replace(VALID_VALUES||',', ','))
)
)
group by ID, VALUE
;
Upvotes: 0
Reputation: 35910
You need to split, replace and aggregate as follows:
Select id, value,
Listagg(case when to_number(vals default null on conversion error) is not null
then case when to_number(vals) > 6 then 6 else vals end
else vals end) Within group (order by lvl) as valid_values
From
(Select id, value,
REGEXP_SUBSTR( t.valid_values, '[^,]+', 1, column_value ) ) , ',' ) as vals,
column_value as lvl
from your_table t,
TABLE(CAST(MULTISET(
SELECT level as lvl
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.valid_value, '[^,]+' )
AS SYS.ODCIVARCHAR2LIS ) v
) group by id, value;
Upvotes: 0