为每个用户选择一个电子邮件地

时间:2017-08-31 14:55:33

标签: sql oracle greatest-n-per-group

我想根据以下规则为每个用户选择一个电子邮件地址。 如果preferred_email为Y,请选择该电子邮件地址(email2@gmail.com)。 如果preferred_email不是Y,请使用该电子邮件地址。 某些用户可能只有preferred_email值为N. 我尝试过使用case语句,但它没有返回正确的结果。

这是该表的一个例子。

user_id email_address       preferred_email
25      email1@gmail.com    N
25      email2@gmail.com    Y
26      email3@gmail.com    N
27      email4@gmail.com    N

2 个答案:

答案 0 :(得分:2)

SELECT user_id,
       MAX( email_address ) KEEP ( DENSE_RANK FIRST ORDER BY preferred_email DESC, ROWNUM )
         AS email_address
FROM   your_table
GROUP BY user_id

SELECT user_id,
       email_address
FROM   (
  SELECT t.*,
         ROW_NUMBER() OVER ( PARTITION BY user_id
                             ORDER BY preferred_email DESC, ROWNUM )
           AS rn
  FROM   your_table t
)
WHERE  rn = 1;

答案 1 :(得分:0)

在Oracle 12.1及更高版本中,可以使用match_recognize子句轻松完成此操作,如下所示:

select user_id, email_address
from   inputs
match_recognize (
  partition by user_id
  order by preferred_email desc nulls last
  all rows per match
  pattern ( ^x )
  define x as 0 = 0
)
;

但是,此解决方案(以及此处提出的其他一些解决方案)存在潜在的弱点:它依赖于'Y' vs 'N'的显式排序,并假设这些是唯一的值可能在preferred_email列中(以及该列不可为空)。

如果列preferred_email不被限制为不可为空且只有可能的值'Y''N',那么最好有一个像

这样的订单子句
 order by case preferred_email when 'Y' then 0 end [...]

不幸的是,match_recognize子句只能按列排序,而不能按表达式排序。 (希望将来能够解决这个问题!)在这种情况下,使用FIRST / LAST聚合函数的聚合解决方案(如MT0的答案)是最佳选择 - 但ORDER BY子句也相应更改。

select   user_id, 
         max(email_address) keep (dense_rank first 
                 order by case preferred_email when 'Y' then 0 end) as email_address
from     inputs
group by user_id
;