You can do this with the help of analytic / window functions.
A working solution with temporary table for the last 2 records case:
create test data:
drop table testdata;
create table testdata(id integer, fk integer, start_date date, end_date date, value integer);
insert into testdata values(1, 1, '2017-10-01', '2017-11-01', 1);
insert into testdata values(2, 1, '2017-12-10', '2018-01-10', 9);
insert into testdata values(3, 1, '2018-01-10', '2018-02-10', 2);
insert into testdata values(4, 2, '2017-11-10', '2017-12-10', 1);
insert into testdata values(5, 2, '2017-12-10', '2018-01-10', 2);
insert into testdata values(6, 3, '2017-12-10', '2018-01-10', 8);
create a helper table, ranked by date, then select the pivot table from it:
WITH ranked AS (
SELECT id, fk, start_date, end_date,value, RANK() OVER (PARTITION BY fk ORDER BY start_date desc) from testdata
)
select fk,
(select start_date as start_date_1 from ranked where rank=1 and ranked.fk=testdata.fk),
(select end_date as end_date_1 from ranked where rank=1 and ranked.fk=testdata.fk),
(select value as value_1 from ranked where rank=1 and ranked.fk=testdata.fk),
(select start_date as start_date_2 from ranked where rank=2 and ranked.fk=testdata.fk),
(select end_date as end_date_2 from ranked where rank=2 and ranked.fk=testdata.fk),
(select value as value_2 from ranked where rank=2 and ranked.fk=testdata.fk)
from testdata group by fk order by fk;
result:
fk start_date_1 end_date_1 value_1 start_date_2 end_date_2 value_2
---------------------------------------------------------------------------------------------------
1 2018-01-10 2018-02-10 2 2017-12-10 2018-01-10 9
2 2017-12-10 2018-01-10 2 2017-11-10 2017-12-10 1
3 2017-12-10 2018-01-10 8 <null> <null> <null>
IMHO your approach is unpractical for the last 24 records case. Would you really like to process a table with 24*3 + 1 column?
If I were you, I'd create a ranked table / result for the last 24 records and would process that on the application side. Query for the last 24:
select * from (
select id, fk, start_date, end_date, value, rank() OVER (PARTITION BY fk ORDER BY start_date desc) from testdata
) as r where rank < 25;