在Oracle

时间:2018-07-16 06:57:38

标签: oracle oracle11g top-n

我需要对具有两列transID和travel_date的表进行排名

这是我的数据

transID    travel_date
2341       2018-04-04 10:00:00
2341       2018-04-04 11:30:00
2891       2018-04-04 12:30:00
2891       2018-04-04 18:30:00
2341       2018-04-05 11:30:00
2891       2018-04-05 22:30:00

这是我尝试过的查询

 select transID,travel_date,rn,
    dense_rank () over (partition by transID order by EarliestDate,transID) as rn2
    from
    (SELECT transID,travel_date,
             ROW_NUMBER() OVER (PARTITION BY transID ORDER BY travel_date) AS rn, 
             max(travel_date) OVER (partition by travel_date) as EarliestDate
      FROM travel_log_info
     ) t
     order by transID;

上述查询的当前输出

transID    travel_date            rn2
2341       2018-04-04 10:00:00    1
2341       2018-04-04 11:30:00    2
2341       2018-04-05 11:30:00    3
2891       2018-04-04 12:30:00    1
2891       2018-04-04 18:30:00    2
2891       2018-04-05 22:30:00    3

预期输出

transID    travel_date            rn2
2341       2018-04-04 10:00:00    1
2341       2018-04-04 11:30:00    2
2341       2018-04-05 11:30:00    1
2891       2018-04-04 12:30:00    1
2891       2018-04-04 18:30:00    2
2891       2018-04-05 22:30:00    1

使用此输出,我可以通过条件rn2 = 1来获得所需的输出,以基于旅行日期和transId获得输出。

如上所述,我没有得到所需的输出。请提供建议以实现正确的输出。 谢谢您的时间

1 个答案:

答案 0 :(得分:3)

您现在拥有的主要问题是:

max(travel_date) OVER (partition by travel_date)

,其中包括分区中每个日期的时间部分-因此,您实际上获得的是每个单独日期/时间(即该日期/时间)的最大值。您似乎想要每天最多的日期/时间,因此可以通过在partition-by子句中使用trunc()按每个进行分区:

max(travel_date) OVER (partition by trunc(travel_date))

只要有了变化,您就可以:

   TRANSID TRAVEL_DATE                 RN        RN2
---------- ------------------- ---------- ----------
      2341 2018-04-04 10:00:00          1          1
      2341 2018-04-04 11:30:00          2          1
      2341 2018-04-05 11:30:00          3          2
      2891 2018-04-04 12:30:00          1          1
      2891 2018-04-04 18:30:00          2          1
      2891 2018-04-05 22:30:00          3          2

尽管外部查询中的分区也是错误的,但您需要按“最早”日期(实际上是最新的,但与此无关)进行分区:

select transID,travel_date,rn,
    dense_rank () over (partition by transID,EarliestDate order by travel_date) as rn2
    from
    (SELECT transID,travel_date,
             ROW_NUMBER() OVER (PARTITION BY transID ORDER BY travel_date) AS rn, 
             max(travel_date) OVER (partition by trunc(travel_date)) as EarliestDate
      FROM travel_log_info
     ) t
     order by transID;

   TRANSID TRAVEL_DATE                 RN        RN2
---------- ------------------- ---------- ----------
      2341 2018-04-04 10:00:00          1          1
      2341 2018-04-04 11:30:00          2          2
      2341 2018-04-05 11:30:00          3          1
      2891 2018-04-04 12:30:00          1          1
      2891 2018-04-04 18:30:00          2          2
      2891 2018-04-05 22:30:00          3          1

但是您实际上并不需要那个max或您当前拥有的外部查询;如果您在row_number()分区(您当前尚未真正使用)中包含那截短的一天,则会得到:

SELECT transID,travel_date,
  ROW_NUMBER() OVER (PARTITION BY transID, trunc(travel_date) ORDER BY travel_date) AS rn
FROM travel_log_info;

   TRANSID TRAVEL_DATE                 RN
---------- ------------------- ----------
      2341 2018-04-04 10:00:00          1
      2341 2018-04-04 11:30:00          2
      2341 2018-04-05 11:30:00          1
      2891 2018-04-04 12:30:00          1
      2891 2018-04-04 18:30:00          2
      2891 2018-04-05 22:30:00          1

,然后可以将其包装在外部查询中以对rn进行过滤:

SELECT transID,travel_date
FROM (
  SELECT transID,travel_date,
    ROW_NUMBER() OVER (PARTITION BY transID, trunc(travel_date) ORDER BY travel_date) AS rn
  FROM travel_log_info
)
WHERE rn = 1
ORDER BY transID,travel_date;

   TRANSID TRAVEL_DATE        
---------- -------------------
      2341 2018-04-04 10:00:00
      2341 2018-04-05 11:30:00
      2891 2018-04-04 12:30:00
      2891 2018-04-05 22:30:00

您也可以不使用子查询来执行此操作;这将得到相同的结果using first

SELECT transID,
  min(travel_date) keep (dense_rank first order by travel_date) as travel_date
FROM travel_log_info
GROUP BY transID, trunc(travel_date)
ORDER BY transID, travel_date;