选择按日期排序的唯一值

时间:2012-09-03 09:34:31

标签: postgresql unique plpgsql

我正在努力解决一个有趣的问题。我有一个表,除了其他数据之外,还有这些列(此示例中的日期以欧洲格式显示 - dd / mm / yyyy):

n_place_id   dt_visit_date
(integer)    (date)
==========   =============
   1           10/02/2012
   3           11/03/2012
   4           11/05/2012
  13           14/06/2012
   3           04/10/2012
   3           03/11/2012
   5           05/09/2012
  13           18/08/2012

基本上,每个地方可能会多次访问 - 日期可能是过去(完成访问)或未来(计划访问)。为简单起见,今天的访问是未来计划访问的一部分。

现在,我需要在此表上运行一个select,它会从此表中提取唯一地点ID(没有日期),按以下顺序排序:

  1. 未来访问在过去访问之前进行
  2. 未来访问优先于对同一地点的过去访问进行排序
  3. 对于将来的访问,最早的日期必须优先排序同一个地方
  4. 对于过去的访问,最新日期必须优先排序同一个地方。
  5. 例如,对于上面显示的示例数据,我需要的结果是:

     5     (earliest future visit)
     3     (next future visit into the future)
    13     (latest past visit)
     4     (previous past visit)
     1     (earlier visit in the past)
    

    现在,我可以使用case when子句中的order by来实现所需的排序,如下所示:

    select
        n_place_id
    from
        place_visit
    order by
        (case when dt_visit_date >= now()::date then 1 else 2 end),
        (case when dt_visit_date >= now():: date then 1 else -1 end) * extract(epoch from dt_visit_date)
    

    这个做我需要的东西,但它确实包含重复的ID,而我需要唯一的地方ID。如果我尝试将distinct添加到select语句中,postgres会抱怨我必须在select子句中使用order by - 但是那时唯一的不再合理,因为我在那里有日期

    不知怎的,我觉得应该有一种方法可以在一个select语句中得到我需要的结果,但是我无法理解如何做到这一点。

    如果无法做到这一点,那么,当然,我将不得不在代码中完成所有工作,但我更愿意在一个SQL语句中使用它。

    P.S。我并不担心性能,因为我要排序的数据集并不大。在应用where子句后,它很少包含超过10条记录。

2 个答案:

答案 0 :(得分:2)

使用DISTINCT ON,您可以轻松显示该行的其他列,并生成n_place_id

SELECT n_place_id, dt_visit_date
FROM  (
   SELECT DISTINCT ON (n_place_id) *
         ,dt_visit_date < now()::date    AS prio  -- future first
         ,@(now()::date - dt_visit_date) AS diff  -- closest first
   FROM   place_visit
   ORDER  BY n_place_id, prio, diff
   ) x
ORDER  BY prio, diff;

有效地,我会根据n_place_id - 或过去的最新日期选择具有最早未来日期(包括“今天”)的行,否则会失败。
然后,生成的唯一行按相同的标准排序。

结果:

 n_place_id | dt_visit_date
------------+--------------
 5          | 2012-09-05
 3          | 2012-10-04
 13         | 2012-08-18
 4          | 2012-05-11
 1          | 2012-02-10

答案 1 :(得分:1)

试试这个

 select n_place_id
 from 
 (
    select *, 
    extract(epoch from (dt_visit_date - now())) as seconds,  
    1 - SIGN(extract(epoch from (dt_visit_date - now())) ) as futurepast
    from #t
 ) v
 group by n_place_id
 order by max(futurepast) desc, min(abs(seconds))