Assuming you have a limit of 10 skills per row, you can normalize the column skillswith the following query:
select distinct
s.fname, s.lname,
replace(
substring(substring_index(s.skills, ',', p.pos),
char_length(substring_index(s.skills, ',', p.pos -1)) + 1),
',', ''
) as skill
from
skills s
cross join
(select 1 pos
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9
union all select 10
) p on p.pos <= (char_length(s.skills) - char_length(replace(s.skills, ',', ''))) + 1
Result:
| fname | lname | skill |
|-------|-------|--------|
| Jane | Doe | php |
| Jane | Doe | java |
| Jane | Doe | mongo |
| Jane | Doe | mysql |
| Jane | Doe | oracle |
sqlfiddle
(char_length(s.skills) - char_length(replace(s.skills, ',', ''))) + 1
will return the number of skills (counting commas).
replace(
substring(substring_index(s.skills, ',', p.pos),
char_length(substring_index(s.skills, ',', p.pos -1)) + 1),
',', ''
)
will extract the skill in the given position.
Instead of creating a table of positions in a row, you can use any existing table with a continuous sequence of numbers.
To denormalize the result again, you can use GROUP_CONCAT(DISTINCT skill):
select
s.fname, s.lname,
group_concat(distinct replace(
substring(substring_index(s.skills, ',', p.pos),
char_length(substring_index(s.skills, ',', p.pos -1)) + 1),
',', ''
)) as skills
from skills s
cross join (
select 1 pos
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9
union all select 10
) p on p.pos <= (char_length(s.skills) - char_length(replace(s.skills, ',', ''))) + 1
group by s.fname, s.lname
Result:
| fname | lname | skills |
|-------|-------|-----------------------------|
| Jane | Doe | php,mysql,mongo,oracle,java |
sqlfiddle
Thus, no skill appears twice in the list.