Reputation: 45
I have table with structure like this, which represents status changes.
+-----------+----------+----------------+-----------------+-------+
| record_id | group_id | attribute type | change date | value |
+-----------+----------+----------------+-----------------+-------+
| 1 | 1 | status | 4/16/2008 18:59 | s1 |
| 2 | 1 | details | 4/16/2008 18:59 | d5 |
| 3 | 1 | details | 8/7/2008 18:31 | d2 |
| 4 | 1 | details | 2/5/2009 22:15 | d1 |
| 5 | 1 | status | 4/3/2009 21:27 | s2 |
| 6 | 1 | details | 4/3/2009 21:27 | d7 |
| 7 | 2 | status | 4/3/2009 21:46 | s1 |
| 8 | 2 | details | 4/3/2009 21:46 | d1 |
+-----------+----------+----------------+-----------------+-------+
I'd like to query status changes and status details changes in two columns, grouped by time stamp (actually any status change makes change to details, so only details change timestamp could be used for easier grouping) and status propagated to related details, like this:
+-----------+-----------------+--------+---------+
| object id | change date | status | details |
+-----------+-----------------+--------+---------+
| 1 | 4/16/2008 18:59 | s1 | d5 |
| 1 | 8/7/2008 18:31 | s1 | d2 |
| 1 | 2/5/2009 22:15 | s1 | d1 |
| 1 | 4/3/2009 21:27 | s2 | d3 |
| 2 | 4/3/2009 21:46 | s1 | d1 |
+-----------+-----------------+--------+---------+
this is what I've started with, but it leaves me with NULLs
SELECT history.record_id,
history.group_id,
history.changedate,
status_chages.value AS status,
history.value AS details
FROM history
LEFT JOIN (SELECT
history.group_id,
history.changedate,
history.value
FROM history
WHERE history.attribute_type = 'status') status_chages
ON status_chages.group_id = history.group_id AND
status_chages.changedate = history.changedate
WHERE history.attribute = 'details'
First thing which came to my mind is to fill NULLs with previous row data.
But is there a better approach for querying the result listed above?
Upvotes: 0
Views: 72
Reputation: 121774
The query gives desired layout:
select
group_id,
change_date,
max(case attr_type when 'status' then value else null end) as status,
max(case attr_type when 'status' then null else value end) as detail
from history
group by 1, 2
order by 1, 2;
group_id | change_date | status | detail
----------+---------------------+--------+--------
1 | 2008-04-16 18:59:00 | s1 | d5
1 | 2008-08-07 18:31:00 | | d2
1 | 2009-02-05 22:15:00 | | d1
1 | 2009-04-03 21:27:00 | s2 | d7
2 | 2009-04-03 21:46:00 | s1 | d1
(5 rows)
You can fill nulls with previous values in such a way:
select
group_id,
change_date,
max(status) over (partition by group_id, part) status,
detail
from (
select *, count(status) over (partition by group_id order by change_date) part
from (
select
group_id,
change_date,
max(case attr_type when 'status' then value else null end) as status,
max(case attr_type when 'status' then null else value end) as detail
from history
group by 1, 2
) s
) s
order by 1, 2;
group_id | change_date | status | detail
----------+---------------------+--------+--------
1 | 2008-04-16 18:59:00 | s1 | d5
1 | 2008-08-07 18:31:00 | s1 | d2
1 | 2009-02-05 22:15:00 | s1 | d1
1 | 2009-04-03 21:27:00 | s2 | d7
2 | 2009-04-03 21:46:00 | s1 | d1
(5 rows)
Upvotes: 2