Reputation: 308
I have a table named student_info in my database.There are multiple columns out of which two columns store comma separated values
class_id student marks
----------------------------------------------
1 tom,jam,tim 55,65,75
2 rim,gum,ram 33,66,77
i want the output to be as follows
class_id student marks
------------------------------------------------
1 tom 55
1 tom 65
1 tom 75
1 jam 55
1 jam 65
1 jam 75
1 tim 55
1 tim 65
1 tim 75
my Query as below
SELECT student_id,TRIM(REGEXP_SUBSTR(student, '[^,]+', 1, level)) student_name
FROM STUDENT_INFO
CONNECT BY level <= REGEXP_COUNT(student, '[^,]+')
AND PRIOR student = student AND marks = marks
AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
with the above query i can get the output as below
class_id student_name marks
------------------------------
1 tom 55,65,75
1 jam 55,65,75
1 tim 55,65,75
How to achieve the desired output ?any suggestions?
Upvotes: 2
Views: 4177
Reputation: 8188
try this
with temp as
(
select 1 class_id , 'tom,jam,tim' student, '55,65,75' marks from dual
union all
select 2 class_id , 'rim,gum,ram' student, '33,66,77' marks from dual
)
select distinct
t.class_id,
trim(regexp_substr(t.student, '[^,]+', 1, students.column_value)) as students,
trim(regexp_substr(t.marks, '[^,]+', 1, marks.column_value)) as marks
from
temp t,
table(cast(multiset(select level from dual connect by level <= length (regexp_replace(t.student, '[^,]+')) + 1) as sys.OdciNumberList)) students,
table(cast(multiset(select level from dual connect by level <= length (regexp_replace(t.marks, '[^,]+')) + 1) as sys.OdciNumberList)) marks
order by class_id;
Upvotes: 0
Reputation: 6366
Using ora:tokenizer
with t(class_id, student, marks) as (
select 1, 'tom,jam,tim', '55,65,75' from dual union all
select 2, 'rim,gum,ram', '33,66,77' from dual
)
select class_id,new_student,new_marks from t
,xmltable('for $i in ora:tokenize($students,",") return $i' passing student as "students" columns new_student varchar2(20) path '.')
,xmltable('for $j in ora:tokenize($marks,",") return $j' passing marks as "marks" columns new_marks varchar2(20) path '.')
Upvotes: 0
Reputation: 39527
It would be lot easier to normalize table than finding complicated methods to convert csv to rows.
In this case, you can use this:
with t(class_id, student, marks) as (
select 1, 'tom,jam,tim', '55,65,75' from dual union all
select 2, 'rim,gum,ram', '33,66,77' from dual
)
select
t.class_id,
regexp_substr(student,'[^,]+',1,x.column_value) student,
regexp_substr(marks,'[^,]+',1,y.column_value) marks
from t, table(
cast(
multiset(
select level
from dual
connect by level <= regexp_count(t.student,',') + 1
) as sys.odcinumberlist
)
)x,table(
cast(
multiset(
select level
from dual
connect by level <= regexp_count(t.marks,',') + 1
) as sys.odcinumberlist
)
)y
Produces:
+----------+------------+-------+
| CLASS_ID | STUDENT | MARKS |
+----------+------------+-------+
| 1 | tom | 55 |
+----------+------------+-------+
| 1 | tom | 65 |
+----------+------------+-------+
| 1 | tom | 75 |
+----------+------------+-------+
| 1 | jam | 55 |
+----------+------------+-------+
| 1 | jam | 65 |
+----------+------------+-------+
| 1 | jam | 75 |
+----------+------------+-------+
| 1 | tim | 55 |
+----------+------------+-------+
| 1 | tim | 65 |
+----------+------------+-------+
| 1 | tim | 75 |
+----------+------------+-------+
| 2 | rim | 33 |
+----------+------------+-------+
| 2 | rim | 66 |
+----------+------------+-------+
| 2 | rim | 77 |
+----------+------------+-------+
| 2 | gum | 33 |
+----------+------------+-------+
| 2 | gum | 66 |
+----------+------------+-------+
| 2 | gum | 77 |
+----------+------------+-------+
| 2 | ram | 33 |
+----------+------------+-------+
| 2 | ram | 66 |
+----------+------------+-------+
| 2 | ram | 77 |
+----------+------------+-------+
Upvotes: 3
Reputation: 168671
Hierarchical Query:
Use correlated-hierarchical queries to find the sub-strings once and then apply the cross product to join them:
SELECT class_id,
s.COLUMN_VALUE AS student,
m.COLUMN_VALUE AS mark
FROM table_name t,
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.students, '[^,]+', 1, LEVEL )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.students, '[^,]+' )
) AS SYS.ODCIVARCHAR2LIST
)
) s,
TABLE(
CAST(
MULTISET(
SELECT TO_NUMBER( REGEXP_SUBSTR( t.marks, '[^,]+', 1, LEVEL ) )
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.marks, '[^,]+' )
) AS SYS.ODCIVARCHAR2LIST
)
) m
Output:
CLASS_ID STUDENT MARKS
-------- ------- -----
1 tom 55
1 tom 65
1 tom 75
1 jam 55
1 jam 65
1 jam 75
1 tim 55
1 tim 65
1 tim 75
2 rim 33
2 rim 66
2 rim 77
2 gum 33
2 gum 66
2 gum 77
2 ram 33
2 ram 66
2 ram 77
Recursive Subquery Factoring:
WITH cte ( class_id, student, marks, s, m, smax, mmax ) AS (
SELECT class_id,
student,
marks,
1,
1,
REGEXP_COUNT( student, '[^,]+' ),
REGEXP_COUNT( marks, '[^,]+' )
FROM table_name
UNION ALL
SELECT class_id,
student,
marks,
CASE WHEN m = mmax THEN s + 1 ELSE s END,
CASE WHEN m = mmax THEN 1 ELSE m + 1 END,
smax,
mmax
FROM cte
WHERE m < mmax OR s < smax
)
SELECT class_id,
REGEXP_SUBSTR( student, '[^,]+', 1, s ) AS student,
TO_NUMBER( REGEXP_SUBSTR( mark, '[^,]+', 1, m ) ) AS mark
FROM cte;
Upvotes: 1