PostgreSQL,从行中选择值作为列名,不为空

时间:2018-07-31 08:43:08

标签: sql postgresql

我有一张桌子:

id;    user_id;    field_name;    value;    timestamp
1      1           user_status    PENDING   2018-07-31 14:20:28.632838
2      1           selfie         APPROVED  2018-07-31 14:20:29.632838
3      2           user_status    PENDING   2018-07-31 14:20:31.632838
4      1           passport       APPROVED  2018-07-31 14:20:33.632838
5      3           user_status    PENDING   2018-07-31 14:20:44.632838
6      2           selfie         REJECTED  2018-07-31 14:20:52.632838
7      1           user_status    ACTIVATED 2018-07-31 14:20:54.632838

我尝试从以下值中共同选择列:user_status的最新消息,selfie的最新消息,userid的护照组的最新消息:

预期表:

user_id;    user_status;    selfie;    passport
1           ACTIVATED       APPROVED   APPROVED
2           PENDING         REJECTED   [null]
3           PENDING         [null]     [null]

我用了这个sql:

select 
    user_id, 
    case when field_name='user_status' then value end as user_status,
    case when field_name='selfie' then value end as selfie,
    case when field_name='passport' then value end passport
from user_history
group by user_id, field_name, value

但是它提供了一个具有空值且没有不同user_id的表。

user_id;    user_status;    selfie;    passport
1           PENDING         [null]     [null]
3           PENDING         [null]     [null]
1           ACTIVATED       [null]     [null]
2           [null]          REJECTED   [null]
1           [null]          APPROVED   [null]
2           PENDING         [null]     [null]
1           [null]          [null]     APPROVED

有什么主意吗?谢谢。

2 个答案:

答案 0 :(得分:1)

第一个获得最新值的CTE:

db=# select distinct user_id, field_name, first_value(value) over (partition by user_id, field_name order by timestamp desc) from user_history;
 user_id | field_name  | first_value
---------+-------------+-------------
       1 | selfie      | APPROVED
       1 | passport    | APPROVED
       2 | selfie      | REJECTED
       3 | user_status | PENDING
       1 | user_status | ACTIVATED
       2 | user_status | PENDING
(6 rows)

第二次(从您的尝试开始)以获取人工列:

db=# with c as (select distinct user_id, field_name, first_value(value) over (partition by user_id, field_name order by timestamp desc) from user_history)
select distinct user_id
, case when field_name = 'user_status' then first_value end
, case when field_name = 'selfie' then first_value end
, case when field_name = 'passport' then first_value end
from c
order by user_id;
 user_id |   case    |   case   |   case
---------+-----------+----------+----------
       1 | ACTIVATED |          |
       1 |           | APPROVED |
       1 |           |          | APPROVED
       2 | PENDING   |          |
       2 |           | REJECTED |
       3 | PENDING   |          |
(6 rows)

最终崩溃:

db=# with c as (select distinct user_id, field_name, first_value(value) over (partition by user_id, field_name order by timestamp desc) from user_history)
, ct as (
select distinct user_id
, case when field_name = 'user_status' then first_value end user_status
, case when field_name = 'selfie' then first_value end selfie
, case when field_name = 'passport' then first_value end passport
from c
)
select
user_id
, max(user_status) user_status
, max(selfie)selfie
, max(passport) passport
from ct
group by user_id
order by user_id;
 user_id | user_status |  selfie  | passport
---------+-------------+----------+----------
       1 | ACTIVATED   | APPROVED | APPROVED
       2 | PENDING     | REJECTED |
       3 | PENDING     |          |
(3 rows)

答案 1 :(得分:1)

一种方法使用窗口函数-因为first_value()是窗口函数而不是聚合函数。

您可以按照以下步骤进行操作:

select distinct user_id,
       first_value(value) filter (where field_name = 'user_status') over
           (partition by user_id order by timestamp desc) as user_status,
       first_value(value) filter (where field_name = 'selfie') over
           (partition by user_id order by timestamp desc) as selfie,
       first_value(value) filter (where field_name = 'passport') over
           (partition by user_id order by timestamp desc) as passport
from user_history;

您也可以使用数组来做到这一点:

select user_id,
       (array_agg(value order by timestamp desc) filter (where field_name = 'user_status'))[1] as user_status,
       (array_agg(value order by timestamp desc) filter (where field_name = 'selfie'))[1] as selfie,
       (array_agg(value order by timestamp desc) filter (where field_name = 'passport'))[1] as passport
from user_history
group by user_id;