how to separate comma separated values in oracle 11G - oracle

I need to separate the value of a column by commas.
Example: BCDEY; I need to convert to a B, C, D, E, Y.
Follows the "select":
SELECT CDGRUPOCONDICAO FROM TBINTCLIENTE;

You can try also this:
with cad as
(select 'BCDEY' cad from dual)
select regexp_replace (regexp_replace(cad,'(.)','\1, '),', $','') cad_comma from cad;

Something like that maybe?
with testdata as (select 'BCDEY' str from dual)
select listagg(c, ', ') within group(order by lvl)
from (
select substr(str, level, 1) c,
level lvl
from testdata
connect by level <= length(str)
)
Producing:
B, C, D, E, Y
Here, the subquery split the string character by character. Then the outer listagg re-assemble the items by joining them with ', '.

An other solution, using recursive subquery factoring (hence, assuming oracle >= 11g release 2):
with testdata as (select 1 id, 'BCDEY' str from dual union all
select 2, 'ABC' from dual),
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- replace that subquery by your actual query
splited(c,r,id,lvl) as (
select '' c, str r, id, 0 lvl from testdata
union all
select substr(r, 1, 1) c,
substr(r, 2) r,
id,
lvl+1
from splited
where r is not null)
select listagg(c, ', ') within group(order by lvl)
from splited
group by (id)
Producing:
LISTAGG(C,',')WITHINGROUP(ORDERBYLVL)
B, C, D, E, Y
A, B, C
See http://sqlfiddle.com/#!4/d41d8/38971

Related

How do i select a coma seperated list of values into a single column

I have a string of comma separated values "'a', 'b', 'c', 'd'".
select 'a', 'b', 'c', 'd' from dual;
How do i select these into a temp table all in a single column rather than a column per value? So my select would return output like this:
value
a
b
c
d
Note: Doing the normal multiple selects and unions is not allowed due to the comma separated values as the input.
I guess you could do something like this, taking the assumption that you can qualify your columns:
SQL> select val from
( select 'a' as c1, 'b' as c2, 'c' as c3, 'd' as c4 from dual ) unpivot include
nulls ( val for col in ( c1,c2,c3,c4 ) );
V
-
a
b
c
d
In this option, it does not matter what values you have in 'a' , 'b' , 'c' 'd' , as basically unpivot is transposing columns into rows.
Hope it helps
You can use hierarchy query as follows:
SQL> with YOUR_DATA as (select q'#'a', 'b', 'c', 'd'#' as str from dual)
2 -- Your query starts from here
3 SELECT TRIM('''' FROM TRIM(REGEXP_SUBSTR(STR,'[^,]+',1,LEVEL))) AS RESULT
4 FROM YOUR_DATA CONNECT BY
5 LEVEL <= 1 + REGEXP_COUNT(STR,',');
RESULT
------------------
a
b
c
d
SQL>
If you have version 12c or later, and if your data has no weird characters, then:
with input(x) as (select q'#'a', 'b', 'c', 'd'#' from dual)
select x_out from input,
json_table(
'['||replace(x, '''', '"')||']', '$[*]'
columns x_out path '$'
);
X_OUT
a
b
c
d

Oracle hierarchy query to find parents and child values From a table

I have a source table which contains values as :
Col1 Col2
A B
B C
E F
F G
G H
X Y
In this scenario A is a parent and b is child of A
And C is a grand child of A, parent and it's child with grand child's should come in one single line.
So the expected output is
Output :
A B C
E F G H
X Y
Oracle 11gR2 Setup:
CREATE TABLE table_name ( Col1, Col2 ) AS
SELECT 'A', 'B' FROM DUAL UNION ALL
SELECT 'B', 'C' FROM DUAL UNION ALL
SELECT 'E', 'F' FROM DUAL UNION ALL
SELECT 'F', 'G' FROM DUAL UNION ALL
SELECT 'G', 'H' FROM DUAL UNION ALL
SELECT 'X', 'Y' FROM DUAL;
Query:
SELECT SUBSTR( SYS_CONNECT_BY_PATH( Col1, ' ' ) || ' ' || Col2, 2 ) AS path
FROM table_name
WHERE CONNECT_BY_ISLEAF = 1
START WITH Col1 NOT IN ( SELECT Col2 FROM table_name )
CONNECT BY PRIOR Col2 = Col1;
Explanation:
Start (line 4) with each Col1 where there is not a parent row identified by a corresponding Col2 value and create a hierarchical query connecting (Line 5) Col1 the the prior parent row.
Filter the output only to those rows which are a leaf of the hierarchical tree (line 3) - i.e. those with no children.
You can then use SYS_CONNECT_BY_PATH to generate a string containing all the Col1 values from the root to the leaf of each branch of the tree generated by the hierarchy and concatenate that with the final Col2 value at the leaf. SUBSTR is used to remove the leading space delimiter that SYS_CONNECT_BY_PATH prepends to each entry in the path.
Output:
PATH
-------
A B C
E F G H
X Y
Is it what you search for?
SQL> with
2 src as (select 'A' p#, 'B' c# from dual union all
3 select 'B' p#, 'C' c# from dual union all
4 select 'E' p#, 'F' c# from dual union all
5 select 'F' p#, 'G' c# from dual union all
6 select 'G' p#, 'H' c# from dual union all
7 select 'X' p#, 'Y' c# from dual)
8 select
9 max(trim(sys_connect_by_path(p#, ' ') || ' ' || c#)) r#
10 from
11 src
12 start with
13 p# not in (select c# from src)
14 connect by p# = prior c#
15 group by connect_by_root(p#);
R#
--------------------------------------------------------------------------------
A B C
X Y
E F G H
May be this code can help you.
WITH t1(id, parent_id) AS (
-- Anchor member.
SELECT id,
PARENT
FROM table
WHERE id = 'A'
UNION ALL
-- Recursive member.
SELECT t2.id,
t2.PARENT
FROM table t2, table t1
WHERE t2.PARENT = t1.id
)
SELECT id, parent_id
FROM t1;

Oracle sql derived table - optional aliasing

I came across a client's query yesterday, it is something like:
select count(*) as countall,
person,
location
from (select custid,
min("Date") as theDate,
person,
data.location
from data join
customer on customer.customerid = custid
where status = 1
and custid <> -1
group by custid,
person,
data.location) --[NO ALIAS]
group by person,location
I don't have a lot of hours in Oracle, but in MS SQL this would not fly, to my knowledge. Any time I have used a derived table it throws an error, so to encounter scenarios such as this piques my interest. I couldn't find anything to explain it on the interwebs, hopefully somebody can explain the scenarios where aliasing for derived tables is optional and when it is not.
You only need to alias, when you are referencing a column that is not uniquely defined. This means that the column exists in more than one table/derived table. A reference could be in the select statement, or a join. If all columns are unique, then you do not need an alias.
I prefer to alias all of the time for clarity, and because it helps with Intellisense in PL/SQL.
--ALIAS needed, because the 'a' column referenced is not unique
--this will throw an error
select a, a, b, c
from (select 'A1' as a, 'B1' as b, 'C1' as c from dual),
(select 'A2' as a from dual);
--this will not throw an error
select t1.a, t2.a, b,c
from (select 'A1' as a, 'B1' as b, 'C1' as c from dual) t1,
(select 'A2' as a from dual) t2;
;
--ALIAS not needed for join, because all referenced columns are unique
select a, b, c, d, e, f
from (select 'A' as a, 'B' as b, 'C' as c from dual)
join (select 'D' as d, 'E' as e, 'F' as f from dual)
on a = d;
--ALIAS needed for join, because the 'x' column referenced is not unique
--this will throw an error
select a
from (select 'A' as a, 'B' as b, 'C' as c, 'X' as x from dual)
join (select 'D' as d, 'E' as e, 'F' as f, 'X' as x from dual)
on x = x;
--this will not throw an error
select a
from (select 'A' as a, 'B' as b, 'C' as c, 'X' as x from dual) t1
join (select 'D' as d, 'E' as e, 'F' as f, 'X' as x from dual) t2
on t1.x = t2.x;
In this instance, you are selecting all columns from your subquery and hence the subquery was written with no alias .
If you were to join this subquery with another table or perhaps another subquery, you would want to alias so that you can reference the join columns using the defined alias.

Converting a string (delimited by consecutive delimiters) to rows using Oracle regular expression

I have a generic string delimited by consecutive delimiter tilde (~) in Oracle. For e.g. the string is 'apple~orange~~mango~~grapes'. It need to be converted into rows but one important thing to be noticed is that the separator is consecutive tilde not single tilde. The output should be like below:
apple~orange
mango
grapes
The workaround is already done using instr and substr oracle functions but I need more cleaner solution using Oracle regular expressins. I have tried using below query but not gettig the correct solution:
WITH str AS (SELECT 'apple~orange~~mango~~grapes' str FROM dual),
cnt AS (SELECT LEVEL sno FROM dual CONNECT BY LEVEL < 5)
SELECT regexp_substr (str, '[^~]+', 1, sno) FROM str CROSS JOIN cnt;
Try this (you can use any character like *, ^, # etc other than , if you expect any of your string will contain , as actual value):
WITH STR AS (SELECT REPLACE('apple~orange~~mango~~grapes','~~',',') STR FROM DUAL),
CNT AS (SELECT LEVEL SNO FROM DUAL CONNECT BY LEVEL < 4)
SELECT REGEXP_SUBSTR (STR, '[^,]+', 1, SNO) FROM STR CROSS JOIN CNT;
You can also use XML with oracle
WITH CTE AS (SELECT '"'
|| REPLACE('apple~orange~~mango~~grapes','~~','","')
|| '"' STR FROM DUAL)
select column_value str from cte, xmltable(str);
Try this,
select
t.str
, regexp_substr (t.str, '[^~(?=~)]+', 1, rn) spl
from YOURTABLE t
cross
join (select rownum rn
from (select max (length (regexp_replace (t.str, '[^~~]+'))) + 1 mx
from YOURTABLE t
)
connect by level <= mx
)
where regexp_substr (t.str,'[^~(?=~)]+' , 1, rn) is not null)

how to replace multiple strings together in Oracle

I have a string coming from a table like "can no pay{1},as your payment{2}due on {3}". I want to replace {1} with some value , {2} with some value and {3} with some value .
Is it Possible to replace all 3 in one replace function ? or is there any way I can directly write query and get replaced value ? I want to replace these strings in Oracle stored procedure the original string is coming from one of my table I am just doing select on that table
and then I want to replace {1},{2},{3} values from that string to the other value that I have from another table
Although it is not one call, you can nest the replace() calls:
SET mycol = replace( replace(mycol, '{1}', 'myoneval'), '{2}', mytwoval)
If there are many variables to replace and you have them in another table and if the number of variables is variable you can use a recursive CTE to replace them.
An example below. In table fg_rulez you put the strings with their replacement. In table fg_data you have your input strings.
set define off;
drop table fg_rulez
create table fg_rulez as
select 1 id,'<' symbol, 'less than' text from dual
union all select 2, '>', 'great than' from dual
union all select 3, '$', 'dollars' from dual
union all select 4, '&', 'and' from dual;
drop table fg_data;
create table fg_Data AS(
SELECT 'amount $ must be < 1 & > 2' str FROM dual
union all
SELECT 'John is > Peter & has many $' str FROM dual
union all
SELECT 'Eliana is < mary & do not has many $' str FROM dual
);
WITH q(str, id) as (
SELECT str, 0 id
FROM fg_Data
UNION ALL
SELECT replace(q.str,symbol,text), fg_rulez.id
FROM q
JOIN fg_rulez
ON q.id = fg_rulez.id - 1
)
SELECT str from q where id = (select max(id) from fg_rulez);
So, a single replace.
Result:
amount dollars must be less than 1 and great than 2
John is great than Peter and has many dollars
Eliana is less than mary and do not has many dollars
The terminology symbol instead of variable comes from this duplicated question.
Oracle 11gR2
Let's write the same sample as a CTE only:
with fg_rulez as (
select 1 id,'<' symbol, 'less than' text from dual
union all select 2, '>', 'greater than' from dual
union all select 3, '$', 'dollars' from dual
union all select 4, '+', 'and' from dual
), fg_Data AS (
SELECT 'amount $ must be < 1 + > 2' str FROM dual
union all
SELECT 'John is > Peter + has many $' str FROM dual
union all
SELECT 'Eliana is < mary + do not has many $' str FROM dual
), q(str, id) as (
SELECT str, 0 id
FROM fg_Data
UNION ALL
SELECT replace(q.str,symbol,text), fg_rulez.id
FROM q
JOIN fg_rulez
ON q.id = fg_rulez.id - 1
)
SELECT str from q where id = (select max(id) from fg_rulez);
If the number of values to replace is too big or you need to be able to easily maintain it, you could also split the string, use a dictionary table and finally aggregate the results
In the example below I'm assuming that the words in your string are separated with blankspaces and the wordcount in the string will not be bigger than 100 (pivot table cardinality)
with Dict as
(select '{1}' String, 'myfirstval' Repl from dual
union all
select '{2}' String, 'mysecondval' Repl from dual
union all
select '{3}' String, 'mythirdval' Repl from dual
union all
select '{Nth}' String, 'myNthval' Repl from dual
)
,MyStrings as
(select 'This is the first example {1} ' Str, 1 strnum from dual
union all
select 'In the Second example all values are shown {1} {2} {3} {Nth} ', 2 from dual
union all
select '{3} Is the value for the third', 3 from dual
union all
select '{Nth} Is the value for the Nth', 4 from dual
)
-- pivot is used to split the stings from MyStrings. We use a cartesian join for this
,pivot as (
Select Rownum Pnum
From dual
Connect By Rownum <= 100
)
-- StrtoRow is basically a cartesian join between MyStings and Pivot.
-- There as many rows as individual string elements in the Mystring Table
-- (Max = Numnber of rows Mystring table * 100).
,StrtoRow as
(
SELECT rownum rn
,ms.strnum
,REGEXP_SUBSTR (Str,'[^ ]+',1,pv.pnum) TXT
FROM MyStrings ms
,pivot pv
where REGEXP_SUBSTR (Str,'[^ ]+',1,pv.pnum) is not null
)
-- This is the main Select.
-- With the listagg function we group the string together in lines using the key strnum (group by)
-- The NVL gets the translations:
-- if there is a Repl (Replacement from the dict table) then provide it,
-- Otherwise TXT (string without translation)
Select Listagg(NVL(Repl,TXT),' ') within group (order by rn)
from
(
-- outher join between strings and the translations (not all strings have translations)
Select sr.TXT, d.Repl, sr.strnum, sr.rn
from StrtoRow sr
,dict d
where sr.TXT = d.String(+)
order by strnum, rn
) group by strnum
If you are doing this inside of a select, you can just piece it together, if your replacement values are columns, using string concatenation.

Resources