SQL - Postgresql - Pivot

时间:2017-12-11 09:30:38

标签: sql postgresql pivot crosstab

我需要使用PostgreSQL来转移带有fk的最后2条记录的表:

目前我有一个具有这种结构的表:

id | fk | start_date | end_date   | value 
---------------------|------------|-------
01 | 01 | 2017-10-01 | 2017-11-01 | 1
02 | 01 | 2017-12-10 | 2018-01-10 | 9
03 | 01 | 2018-01-10 | 2018-02-10 | 2
04 | 02 | 2017-11-10 | 2017-12-10 | 1
05 | 02 | 2017-12-10 | 2018-01-10 | 2
06 | 03 | 2017-12-10 | 2018-01-10 | 8

我想用这种结构输出:

fk | start_date_1 | end_date_1 | value_1 | start_date_2 | end_date_2 | value_2 
---|--------------|------------|---------|--------------|------------|---------
01 | 2018-01-10   | 2018-02-10 | 2       | 2017-12-10   | 2018-01-10 | 9
02 | 2017-12-10   | 2018-01-10 | 2       | 2017-11-10   | 2017-12-10 | 1
03 | 2017-12-10   | 2018-01-10 | 8       | NULL         | NULL       | NULL

我需要一个可用于搜索最后2条记录和最后24条记录的代码。

2 个答案:

答案 0 :(得分:0)

您可以在分析/窗口功能的帮助下完成此操作。

具有最后2个记录案例的临时表的工作解决方案:

创建测试数据:

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);

创建一个帮助表,按日期排序,然后从中选择数据透视表:

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;

结果:

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>       

恕我直言,你的方法对于最后24个案例来说是不切实际的。你真的想要处理一个24 * 3 + 1列的表吗?

如果我是你,我会为最后24条记录创建一个排名表/结果,并在应用程序方面进行处理。查询最后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;

答案 1 :(得分:0)

如果我对您的理解正确,也可以使用LATERAL join来实现;

对于t1的fk中的每个值,LATERAL join搜索t2中的第二个值。

select t1.fk,
   t1.start_date as start_date_1,
   t1.end_date as end_date_1,
   t1._value as _value1,
   _second.* 
from
 (
--get first record of last two
select distinct on (dt.fk) dt.fk,dt.start_date,dt.end_date,dt._value
from (
    --last 24 records
    select * from table1 order by start_date DESC limit 24
     ) dt
order by dt.fk,dt.start_date DESC
) t1
LEFT JOIN LATERAL(
--get second record of last two
select start_date as start_date_2,
       end_date as end_date_2,
       _value as _value2
from table1 as t2
where t2.fk = t1.fk and 
t1.start_date > t2.start_date --to get second value
order by t2.start_date desc
limit 1) _second on true;