Oracle分组在日期列中的分钟数

时间:2018-01-17 12:17:35

标签: sql database oracle group-by

如果我有下表:

ID                       d_date                        d_value
----------------------------------------------------------------------
1                 1/16/2018 03:41:01 PM                 10   
2                 1/16/2018 03:41:02 PM                 20 
3                 1/16/2018 03:41:03 PM                 30 
4                 1/16/2018 03:44:04 PM                 40 
5                 1/16/2018 03:44:05 PM                 50 
6                 1/16/2018 03:46:06 PM                 60 
7                 1/17/2018 03:41:01 PM                 70 
8                 1/17/2018 03:41:02 PM                 80 
9                 1/17/2018 03:44:03 PM                 90 
10                1/17/2018 03:45:04 PM                 100 

需要获取每分钟最后一秒的值。因此,对于上表,结果如下:

ID                       d_date                        value
----------------------------------------------------------------------
3                 1/16/2018 03:41:03 PM                 30 
5                 1/16/2018 03:44:05 PM                 50 
6                 1/16/2018 03:46:06 PM                 60 
8                 1/17/2018 03:41:02 PM                 80 
9                 1/17/2018 03:44:03 PM                 90 
10                1/17/2018 03:45:04 PM                 100 

我不得不使用内连接来实现它:

select dt, mx, d_tb.d_value from (
    select to_char(d_date,'YYYY-MM-DD HH24:MI') dt, max(d_date) mx from d_table
    group by to_char(d_date,'YYYY-MM-DD HH24:MI')
) d_sub
left outer join d_table d_tb on d_tb.d_date = d_sub.mx
order by dt

表中有数千条记录的问题,有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

您可以使用row_number()

select d.*
from (select d.*,
             row_number() over (partition by to_char(d_date, 'YYYY-MM-DD HH24:MI')
                                order by d_date desc
                               ) as seqnum
      from d_table d
     ) d
where seqnum = 1;

对于性能,您可以从(to_char(d_date, 'YYYY-MM-DD HH24:MI'), d_date)上的索引开始。您也可以尝试以下方法(使用相同的索引:

select d.*
from d_table d
where d.d_date = (select max(d2.d_date)
                  from d_table d2
                  where to_char(d2.d_date, 'YYYY-MM-DD HH24:MI') = to_char(d.d_date, 'YYYY-MM-DD HH24:MI')
                 );

在任何一种情况下,性能的关键是基于函数的索引来提取分钟。

正如Boneist所说,trunc(d_date, 'MI')可能比转换为约会更好。

答案 1 :(得分:2)

另一种方法是使用keep .. dense_rank with first or last,它不需要额外的索引:

select max(id) keep (dense_rank last order by d_date) as id,
  max(d_date) as d_date,
  max(d_value) keep (dense_rank last order by d_value) as d_value
from d_table
group by trunc(d_date, 'MI');

        ID D_DATE                 D_VALUE
---------- ------------------- ----------
         3 2018-01-16 15:41:03         30
         5 2018-01-16 15:44:05         50
         6 2018-01-16 15:46:06         60
         8 2018-01-17 15:41:02         80
         9 2018-01-17 15:44:03         90
        10 2018-01-17 15:45:04        100

如果有可能在同一秒内有两个值,那么你需要决定如何打破平局 - 即决定使用哪个 - 并适当修改order by

trunc(d_date, 'MI')从日期值中剥离第二个,以便按分钟值分组;并且您需要一个正常的聚合max(d_date)来获得每个组中最高的实际值。