If you know that there are not many actions in your user_action column, you use recursive sub-querying with union all
and therefore avoiding the aux numbers
table.
But it requires you to know the number of actions for each user, either adjust initial table or make a view or a temporary table for it.
Data preparation
Assuming you have something like this as a table:
create temporary table actions
(
user_id varchar,
user_name varchar,
user_action varchar
);
I'll insert some values in it:
insert into actions
values (1, 'Shone', 'start,stop,cancel'),
(2, 'Gregory', 'find,diagnose,taunt'),
(3, 'Robot', 'kill,destroy');
Here's an additional table with temporary count
create temporary table actions_with_counts
(
id varchar,
name varchar,
num_actions integer,
actions varchar
);
insert into actions_with_counts (
select user_id,
user_name,
regexp_count(user_action, ',') + 1 as num_actions,
user_action
from actions
);
This would be our "input table" and it looks just as you expected
select * from actions_with_counts;
id |
name |
num_actions |
actions |
2 |
Gregory |
3 |
find,diagnose,taunt |
3 |
Robot |
2 |
kill,destroy |
1 |
Shone |
3 |
start,stop,cancel |
Again, you can adjust initial table and therefore skipping adding counts as a separate table.
Sub-query to flatten the actions
Here's the unnesting query:
with recursive tmp (user_id, user_name, idx, user_action) as
(
select id,
name,
1 as idx,
split_part(actions, ',', 1) as user_action
from actions_with_counts
union all
select user_id,
user_name,
idx + 1 as idx,
split_part(actions, ',', idx + 1)
from actions_with_counts
join tmp on actions_with_counts.id = tmp.user_id
where idx < num_actions
)
select user_id, user_name, user_action as parsed_action
from tmp
order by user_id;
This will create a new row for each action, and the output would look like this:
user_id |
user_name |
parsed_action |
1 |
Shone |
start |
1 |
Shone |
stop |
1 |
Shone |
cancel |
2 |
Gregory |
find |
2 |
Gregory |
diagnose |
2 |
Gregory |
taunt |
3 |
Robot |
kill |
3 |
Robot |
destroy |