split column value into multiple columns in oracle sql - sql

I have data into table like this:
I want the output like this:
how we can achive this with orace sql statment.Kindly assist.

instr(comp,':')
yields the first colon occurrence
instr(comp,':',1,2)
yields the second colon occurrence, and so forth
After that, its just math with combinations of INSTR on : and |
For example
substr(comp,2,instr(comp,':')-2)
for the first column before the colon
substr(comp,instr(comp,':')+1,instr(comp,'|')-instr(comp,':')-1)
for the first element after the colon and before the bar
and so forth.

This is one option; not dynamic at all as you'll have to know how many items there are so that you could adjust the final query. Read comments within code.
SQL> with test (id, component) as
2 -- sample data
3 (select 1, '|TD-2-2720A-NVE-C:2|TD-2-2720A-TPM-C:2|TD-PREM-NLSAS-01-PR:480|TD-ONTAP-NLSAS-01-PR:480' from dual union all
4 select 2, '|TD-2-A220A-TPM-C:2|DD-FLASH-PREM-01-PR:115|DD-FLASH-ONTAP-01-PR:115' from dual union all
5 select 5, '|TD-2-2650A-NVE-C:2|TD-2-2650A-TPM-C:2' from dual
6 ),
7 temp as
8 -- split each component into rows, separated by the pipe character
9 (select id,
10 column_value cv,
11 regexp_substr(component, '[^|]+', 1, column_value) comp
12 from test cross join
13 table(cast(multiset(select level from dual
14 connect by level <= regexp_count(component, '\|')
15 ) as sys.odcinumberlist))
16 )
17 -- final result
18 select id,
19 max(case when cv = 1 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_1,
20 max(case when cv = 1 then regexp_substr(comp, '\d+$') end) as cnt_1,
21 --
22 max(case when cv = 2 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_2,
23 max(case when cv = 2 then regexp_substr(comp, '\d+$') end) as cnt_2,
24 --
25 max(case when cv = 3 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_3,
26 max(case when cv = 3 then regexp_substr(comp, '\d+$') end) as cnt_3,
27 --
28 max(case when cv = 4 then substr(comp, 1, instr(comp, ':') - 1) end) as comp_4,
29 max(case when cv = 4 then regexp_substr(comp, '\d+$') end) as cnt_4
30 from temp
31 group by id
32 order by id;
ID COMP_1 CNT_1 COMP_2 CNT_2 COMP_3 CNT_3 COMP_4 CNT_4
--- -------------------- ----- -------------------- ----- -------------------- ----- -------------------- -----
1 TD-2-2720A-NVE-C 2 TD-2-2720A-TPM-C 2 TD-PREM-NLSAS-01-PR 480 TD-ONTAP-NLSAS-01-PR 480
2 TD-2-A220A-TPM-C 2 DD-FLASH-PREM-01-PR 115 DD-FLASH-ONTAP-01-PR 115
5 TD-2-2650A-NVE-C 2 TD-2-2650A-TPM-C 2
SQL>

Related

sql grouping grades

I have a table for subjects as follows:
id Subject Grade Ext
100 Math 6 +
100 Science 4 -
100 Hist 3
100 Geo 2 +
100 CompSi 1
I am expecting output per student in a class(id = 100) as follows:
Grade Ext StudentGrade
6 + 1
6 0
6 - 0
5 + 0
5 0
5 - 0
4 + 0
4 0
4 - 1
3 + 0
3 1
3 - 0
2 + 1
2 0
2 - 0
1 + 0
1 1
1 - 0
I would want this done on oracle/sql rather than UI. Any inputs please.
You should generate rows first, before join them with your table like below. I use the with clause here to generate the 18 rows in your sample.
with rws (grade, ext) as (
select ceil(level/3), decode(mod(level, 3), 0, '+', 1, '-', null)
from dual
connect by level <= 3 * 6
)
select r.grade, r.ext, nvl2(t.Ext, 1, 0) studentGrade
from rws r
left join your_table t
on t.Grade = r.Grade and decode(t.Ext, r.Ext, 1, 0) = 1
order by 1 desc, decode(r.ext, null, 2, '-', 3, '+', 1)
You could do something like this. In the WITH clause I generate two small "helper" tables (really, inline views) for grades from 1 to 6 and for "extensions" of +, null and -. In the "extensions" view I also create an "ordering" column to use in ordering the final output (if you are wondering why I included that).
Also in the WITH clause I included sample data - you will have to remove that and instead use your actual table name in the main query.
The idea is to cross-join "grades" and "extensions", and left-outer-join the result to your input data. Count the grades from the input data, grouped by grade and extension, and after filtering the desired id. The decode thing in the join condition is needed because for extension we want to treat null as equal to null - something that decode does nicely.
with
sample_inputs (id, subject, grade, ext) as (
select 100, 'Math' , 6, '+' from dual union all
select 100, 'Science', 4, '-' from dual union all
select 100, 'Hist' , 3, null from dual union all
select 100, 'Geo' , 2, '+' from dual union all
select 100, 'CompSi' , 1, null from dual
)
, g (grade) as (select level from dual connect by level <= 6)
, e (ord, ext) as (
select 1, '+' from dual union all
select 2, null from dual union all
select 3, '-' from dual
)
select g.grade, e.ext, count(t.grade) as studentgrade
from g cross join e left outer join sample_inputs t
on t.grade = g.grade and decode(t.ext, e.ext, 0) = 0
and t.id = 100 -- change this as needed!
group by g.grade, e.ext, e.ord
order by g.grade desc, e.ord
;
OUTPUT:
GRADE EXT STUDENTGRADE
----- --- ------------
6 + 1
6 0
6 - 0
5 + 0
5 0
5 - 0
4 + 0
4 0
4 - 1
3 + 0
3 1
3 - 0
2 + 1
2 0
2 - 0
1 + 0
1 1
1 - 0
It looks like you want sparse data to be filled in as part of joining students and subjects.
Since Oracle 10g the correct way to do this has been with a "partition outer join".
The documentation has examples.
https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6

Get data based on condition in oracle sql

My table
loads(Unique) Value
T123 11
T234 9.5
T456 15
T678 35
T345 3.7
Want I want
count(values<=10) count(values>10 &<=20) count(values>20)
2 2 1
I tried to use CASE but don't know the usage
CASE yes; not with COUNT but with SUM:
SQL> with test (loads, value) as
2 (select 't123', 11 from dual union all
3 select 't234', 9.5 from dual union all
4 select 't456', 15 from dual union all
5 select 't678', 35 from dual union all
6 select 't345', 3.7 from dual
7 )
8 select
9 sum(case when value <= 10 then 1 end) cnt_1,
10 sum(case when value > 10 and value <= 20 then 1 end) cnt_2,
11 sum(case when value > 20 then 1 end) cnt_3
12 from test;
CNT_1 CNT_2 CNT_3
---------- ---------- ----------
2 2 1
SQL>
Use conditional aggregation as
select coalesce(sum(case when value<=10 then 1 end),0) as "values<=10",
coalesce(sum(case when value>10 and value<=20 then 1 end),0) as "values>10value<20",
coalesce(sum(case when value>20 then 1 end),0) as "values>20"
from your_table;

PL Sql Creating Hierarcical ID

i have this table without any hierarcy but its obvious it has a hierarcy like 100,100-01,100-01-01 ..
ID ACCOUNT
3 100
4 100-01
5 100-01-01
6 100-01-01-001
7 100-01-01-006
8 100-01-01-271
9 100-01-01-272
10 100-01-01-273
11 100-01-16
12 100-01-16-420
13 100-02
14 100-02-01
15 100-02-01-001
16 100-02-01-271
17 100-02-01-272
i want to fill like this; 100-01 is child of 100 , 100-01-01 is child of 100-01 so ;
ID H_ID ACCOUNT
3 100
4 3 100-01
5 4 100-01-01
6 5 100-01-01-001
7 5 100-01-01-006
8 5 100-01-01-271
9 5 100-01-01-272
10 5 100-01-01-273
11 4 100-01-16
12 11 100-01-16-420
13 3 100-02
14 13 100-02-01
15 14 100-02-01-001
16 14 100-02-01-271
17 14 100-02-01-272
Thank you for your time...
You can find the 'parent' account with substr and instr:
select id, account, substr(account, 1, instr(account, '-', -1, 1) - 1)
from your_table;
ID ACCOUNT SUBSTR(ACCOUN
---------- ------------- -------------
3 100
4 100-01 100
5 100-01-01 100-01
6 100-01-01-001 100-01-01
...
You can then (outer) join the table to itself using that value:
select t1.id, t2.id as h_id, t1.account
from your_table t1
left join your_table t2
on t2.account = substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1)
order by t1.id;
which with your sample data gives:
ID H_ID ACCOUNT
---------- ---------- -------------
3 100
4 3 100-01
5 4 100-01-01
6 5 100-01-01-001
7 5 100-01-01-006
8 5 100-01-01-271
9 5 100-01-01-272
10 5 100-01-01-273
11 4 100-01-16
12 11 100-01-16-420
13 3 100-02
14 13 100-02-01
15 14 100-02-01-001
16 14 100-02-01-271
17 14 100-02-01-272
There is no parent for ID 3 so it isn't clear why your expected result has 2 for that.
You could also extract the 'branch' and 'leaf' parts of the account if that's useful:
select t1.id, t2.id as h_id, t1.account,
substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1) as branch,
substr(t1.account, instr(t1.account, '-', -1, 1) + 1) as leaf
from your_table t1
left join your_table t2
on t2.account = substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1)
order by t1.id;
ID H_ID ACCOUNT BRANCH LEAF
---------- ---------- ------------- ------------- -------------
3 100 100
4 3 100-01 100 01
5 4 100-01-01 100-01 01
6 5 100-01-01-001 100-01-01 001
7 5 100-01-01-006 100-01-01 006
8 5 100-01-01-271 100-01-01 271
...
db<>fiddle
In principle, if this is a real hierarchy, you only need to store the ID, parent ID and leaf part, and can then reconstruct the whole account with a hierarchical query (or recursive CTE). That may not actually be practical, or sensible, for your situation.
Looking at the edit history of your question, it seems like your table has an empty h_id column that you want to populate. You can use the same mechanism with a correlated update:
update your_table t1
set h_id = (
select t2.id
from your_table t2
where t2.account = substr(t1.account, 1, instr(t1.account, '-', -1, 1) - 1)
);
db<>fiddle

duplicate column during pivot table

I have a table like
select * from myTable
ID Type Prop1 Prop2 Prop3 Prop4 Prop5
-- ------ ------- ------- ------- ------- -------
1 Hot 10 9 23 32 4
1 Cold 2 24 53 34 5
2 Hot 11 9 23 32 4
2 Cold 22 1 53 30 11
I want to pivot my table like that
select * from myPivotTable
ID HotProp1 HotProp2 HotProp3 HotProp4 HotProp5 ColdProp1 ColdProp2 ColdProp3 ColdProp4 ColdProp5
-- ------- ------- ------- ------- -------- --------- --------- --------- --------- ---------
1 10 9 23 32 4 2 24 53 34 5
2 11 9 23 32 4 22 1 53 30 11
How can I convert myTable to myPivotTable using pivot function in oracle sql?
I think conditional aggregation is a much simpler query than a complex pivot:
select id,
sum(case when type = 'Hot' then Prop1 end) as Hot_Prop1,
sum(case when type = 'Hot' then Prop2 end) as Hot_Prop2,
sum(case when type = 'Hot' then Prop3 end) as Hot_Prop3,
sum(case when type = 'Hot' then Prop4 end) as Hot_Prop4,
sum(case when type = 'Hot' then Prop5 end) as Hot_Prop5,
sum(case when type = 'Cold' then Prop1 end) as Cold_Prop1,
sum(case when type = 'Cold' then Prop2 end) as Cold_Prop2,
sum(case when type = 'Cold' then Prop3 end) as Cold_Prop3,
sum(case when type = 'Cold' then Prop4 end) as Cold_Prop4,
sum(case when type = 'Cold' then Prop5 end) as Cold_Prop5
from myPivotTable
group by id;
If your Oracle version 11g and up you can simply "re-pivot" it(You have already pivoted data) - unpivot it first and then pivot again:
with t1(id1, type1, Prop1, Prop2, Prop3, Prop4, Prop5) as(
select 1, 'Hot' , 10 , 9 , 23,32 , 4 from dual union all
select 1, 'Cold', 2 , 24, 53,34 , 5 from dual union all
select 2, 'Hot' , 11 , 9 , 23,32 , 4 from dual union all
select 2, 'Cold', 22 , 1 , 53,30 , 11 from dual
)
select *
from( select *
from t1
unpivot (
val for col in (prop1, prop2, prop3, prop4, prop5)
)
)
pivot(
max(val) for (col, type1) in (('PROP1', 'Hot') as HotProp1,
('PROP2', 'Hot') as HotProp2,
('PROP3', 'Hot') as HotProp3,
('PROP4', 'Hot') as HotProp4,
('PROP5', 'Hot') as HotProp5,
('PROP1', 'Cold') as ColdProp1,
('PROP2', 'Cold') as ColdProp2,
('PROP3', 'Cold') as ColdProp3,
('PROP4', 'Cold') as ColdProp4,
('PROP5', 'Cold') as ColdProp5)
)
Result:
ID1 HOTPROP1 HOTPROP2 HOTPROP3 HOTPROP4 HOTPROP5 COLDPROP1 COLDPROP2 COLDPROP3 COLDPROP4 COLDPROP5
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1 10 9 23 32 4 2 24 53 34 5
2 11 9 23 32 4 22 1 53 30 11
Here is the demo

ORACLE SQL Query Table using criteria from other table

TABLEA contains the data, while TABLEB contains the search criteria
Here is a SQL Fiddle with the data
Tables
TABLEA
visited_states_time
AL= Alabama,2, AK=Alaska,5
AR=Arkansas,6
AZ=Arizona,10
CA=California, 10,CT=Connecticut,20
TABLEB
CRITERIA
AL
HI
CA
CT
AK
Desired Result
visited_states ................................... total_time_spent
AL= Alabama, AK=Alaska ............................ 7
CA=California, CT=Connecticut................... 30
That's a terrible data model. also you didn't say the condition for tableb. if any state matches, or if all?
as we need to split the rows up (to sum()) and then recombine them you can use:
SQL> with v as (select rownum r,
2 ','||visited_states_time||',' visited_states_time,
3 length(
4 regexp_replace(visited_states_time, '[^,]', '')
5 )+1 fields
6 from tablea)
7 select trim(both ',' from visited_states_time) visited_states_time,
8 sum(total_time_spent) total_time_spent
9 from (select *
10 from v
11 model
12 partition by (r)
13 dimension by (0 as f)
14 measures (visited_states_time, cast('' as varchar2(2)) state,
15 0 as total_time_spent, fields)
16 rules (
17 state[for f from 0 to fields[0]-1 increment 2]
18 = trim(
19 substr(visited_states_time[0],
20 instr(visited_states_time[0], ',', 1, cv(f)+1)+1,
21 instr(visited_states_time[0], '=', 1, (cv(f)/2)+1)
22 - instr(visited_states_time[0], ',', 1, cv(f)+1)-1
23 )),
24 visited_states_time[any]= visited_states_time[0],
25 total_time_spent[any]
26 = substr(visited_states_time[0],
27 instr(visited_states_time[0], ',', 1, (cv(f)+2))+1,
28 instr(visited_states_time[0], ',', 1, (cv(f)+3))
29 - instr(visited_states_time[0], ',', 1, (cv(f)+2))-1
30 )
31 ))
32 where state in (select criteria from tableb)
33 group by visited_states_time;
VISITED_STATES_TIME TOTAL_TIME_SPENT
------------------------------------- ----------------
CA=California, 10,CT=Connecticut,20 30
AL=Alabama,2, AK=Alaska,5 7
but seriously, rewrite that data model to store them separately to start with.