I have two tables. One contains user state transitions.
create table state (
id serial primary key,
ctime timestamp with time zone not null,
state text not null
);
insert into state (ctime, state) values
('2019-05-01 03:58:40+00', 'Busy')
, ('2019-05-01 03:58:42+00', 'Ready')
, ('2019-05-01 04:00:14+00', 'Busy')
, ('2019-05-01 04:16:26+00', 'Ready')
, ('2019-05-01 04:16:36+00', 'Busy')
;
And the other one contains user’s actions.
create table action (
id serial primary key,
ctime timestamp with time zone not null,
action text not null
);
insert into action (ctime, action) values
('2019-05-01 03:58:42+00', 'vasah')
, ('2019-05-01 03:58:42+00', 'mituh')
, ('2019-05-01 04:00:14+00', 'jumuf')
, ('2019-05-01 04:00:16+00', 'vibaj')
, ('2019-05-01 04:00:16+00', 'sasij')
, ('2019-05-01 04:16:21+00', 'husih')
, ('2019-05-01 04:16:26+00', 'radod')
, ('2019-05-01 04:16:30+00', 'zadub')
, ('2019-05-01 04:16:35+00', 'mimoh')
, ('2019-05-01 04:16:36+00', 'rimoh')
, ('2019-05-01 04:16:37+00', 'zahuf')
, ('2019-05-01 04:16:37+00', 'fisak')
;
It is easy to union these tables and visually see in which state an action was performed.
select *
from (
select ctime, state, null from state
union all
select ctime, null, action from action) x
order by ctime;
Output:
2019-05-01 06:58:40+03 | Busy | 2019-05-01 06:58:42+03 | | mituh 2019-05-01 06:58:42+03 | | vasah 2019-05-01 06:58:42+03 | Ready | 2019-05-01 07:00:14+03 | | jumuf 2019-05-01 07:00:14+03 | Busy | 2019-05-01 07:00:16+03 | | vibaj 2019-05-01 07:00:16+03 | | sasij 2019-05-01 07:16:21+03 | | husih 2019-05-01 07:16:26+03 | Ready | 2019-05-01 07:16:26+03 | | radod 2019-05-01 07:16:30+03 | | zadub 2019-05-01 07:16:35+03 | | mimoh 2019-05-01 07:16:36+03 | Busy | 2019-05-01 07:16:36+03 | | rimoh 2019-05-01 07:16:37+03 | | zahuf 2019-05-01 07:16:37+03 | | fisak
How can I “fill the blanks” so the output will be like this?
2019-05-01 06:58:40+03 | Busy | 2019-05-01 06:58:42+03 | Busy | mituh 2019-05-01 06:58:42+03 | Busy | vasah 2019-05-01 06:58:42+03 | Ready | 2019-05-01 07:00:14+03 | Ready | jumuf 2019-05-01 07:00:14+03 | Busy | 2019-05-01 07:00:16+03 | Busy | vibaj 2019-05-01 07:00:16+03 | Busy | sasij 2019-05-01 07:16:21+03 | Busy | husih 2019-05-01 07:16:26+03 | Ready | 2019-05-01 07:16:26+03 | Ready | radod 2019-05-01 07:16:30+03 | Ready | zadub 2019-05-01 07:16:35+03 | Ready | mimoh 2019-05-01 07:16:36+03 | Busy | 2019-05-01 07:16:36+03 | Busy | rimoh 2019-05-01 07:16:37+03 | Busy | zahuf 2019-05-01 07:16:37+03 | Busy | fisak
Advertisement
Answer
This is a place where lag(ignore nulls) is really useful. But not available in Postgres. So you can do this in two steps. Assign groups based on state. Then spread the value:
select t.*,
max(state) over (partition by grp) as imputed_state
from (select t.*, count(state) over (order by ctime) as grp
from t
) t;
Note that t is actually your union all query. I’ve abstracted it to a single table alias to illustrate the important part of the logic.
Also, you have ties in your data (rows with the same time). That means that the answer is indeterminate — ties can go either before or after the state with the same time.
Here is a db<>fiddle.