选择几个最近的电子邮件作为一行

时间:2017-02-07 11:35:54

标签: sql oracle oracle11g

在我的项目中,我有一个用户电子邮件表EMAILS:

|     ID |      PROFILE_ID |        EMAIL | LAST_UPDATED_TIMESTAMP|
___________________________________________________________________
|       1|                1|       a@a.com|    2017-02-02 15:13:46|
|       2|                1|       b@b.com|    2017-02-01 15:13:46|
|       3|                2|       c@c.com|    2017-02-03 15:13:46|

其中PROFILE_ID指的是用户配置文件表PROFILES。我想为每个用户个人资料获得2个(一般为N个)最新的不同电子邮件作为一行:

|     PROFILE_ID |      EMAIL_1    |      EMAIL_2 |
__________________________________________________
|               1|          b@b.com|       a@a.com|
|               2|          c@c.com|          NULL|

我尝试了很多查询,我停下来的最后一个查询如下:

SELECT
  EMAIL1.PROFILE_ID,
  EMAIL1.EMAIL AS EMAIL_1,
  EMAIL2.EMAIL AS EMAIL_2
FROM EMAILS EMAIL1
  LEFT JOIN
  EMAILS EMAIL2
    ON EMAIL1.PROFILE_ID = EMAIL2.PROFILE_ID AND EMAIL2.LAST_UPD_TMST <= EMAIL1.LAST_UPD_TMST AND EMAIL1.ID <> EMAIL2.ID

但它为我提供了包含多封电子邮件的个人资料的额外记录:

|     PROFILE_ID |      EMAIL_1    |      EMAIL_2 |
__________________________________________________
|               1|          b@b.com|       a@a.com|
|               1|          a@a.com|          NULL|
|               2|          c@c.com|          NULL|

我使用Oracle 11g,但如果可能的话,请记住MySql兼容性。有人可以说上面提到的查询有什么问题吗?

2 个答案:

答案 0 :(得分:2)

在Oracle 11中,您可以使用PIVOT运算符。我不了解MySQL,但我相信它没有PIVOT运营商。

with
     test_data ( ID, PROFILE_ID, EMAIL, LAST_UPDATED_TIMESTAMP ) as (
       select 1, 1, 'a@a.com', to_timestamp('2017-02-02 15:13:46', 'yyyy-mm-dd hh24:mi:ss')
       from dual union all
       select 2, 1, 'b@b.com', to_timestamp('2017-02-01 15:13:46', 'yyyy-mm-dd hh24:mi:ss')
       from dual union all
       select 3, 2, 'c@c.com', to_timestamp('2017-02-03 15:13:46', 'yyyy-mm-dd hh24:mi:ss')
       from dual
     )
--  end of test data (not part of the solution); SQL query begins BELOW THIS LINE
select profile_id, email_1, email_2
from   ( select profile_id, email, 
                row_number() over (partition by profile_id 
                                   order by last_updated_timestamp desc) as rn
         from   test_data
       )
pivot (min(email) for rn in (1 as email_1, 2 as email_2))
;

PROFILE_ID EMAIL_1 EMAIL_2
---------- ------- -------
         1 a@a.com b@b.com
         2 c@c.com

2 rows selected.

答案 1 :(得分:0)

一种方法是条件聚合:

select profile_id,
       max(case when seqnum = 1 then email end) as email1,
       max(case when seqnum = 2 then email end) as email2
from (select e.*,
             row_number() over (partition by profile_id order by last_updated_timestamp) as seqnum
      from emails e
     ) e
group by profile_id;

如果您确实想删除重复项,则可以在row_number()之前聚合数据。要获取上次重复发送的电子邮件:

select profile_id,
       max(case when seqnum = 1 then email end) as email1,
       max(case when seqnum = 2 then email end) as email2
from (select e.*,
             row_number() over (partition by profile_id order by last_updated_timestamp) as seqnum
      from (select profile_id, email, max(last_updated_timestamp) as last_updated_timestamp
            from emails e
            group by profile_id, email
           ) e
     ) e
group by profile_id;