在Oracle DB上查找排名靠前的行的有效方法

时间:2018-06-22 15:00:54

标签: sql oracle

我有一个大表(数百万条记录),我需要编写一个有效的select语句。

该表如下所示:

create table tab1 (
pt_key number
, cp_key number
, ext_info varchar2(10)
, resp_nm varchar2(20)
, resp_dttm date
, rank number
);

样本记录:

insert into tab1 values (1,1,'info1','OK', to_date('01.03.18 17:00:00','DD.MM.RR HH24:MI'),1);
insert into tab1 values (1,1,'info2','FAILED', to_date('01.03.18 17:00:00','DD.MM.RR HH24:MI'),2);
insert into tab1 values (1,1,'info3','SENT', to_date('01.03.18 17:00:00','DD.MM.RR HH24:MI'),3);
insert into tab1 values (1,1,'info4','SENT', to_date('02.03.18 17:00:00','DD.MM.RR HH24:MI'),3);
insert into tab1 values (1,2,'info5','OK', to_date('05.03.18 17:00:00','DD.MM.RR HH24:MI'),1);
insert into tab1 values (1,2,'info6','OK', to_date('06.03.18 17:00:00','DD.MM.RR HH24:MI'),1);
insert into tab1 values (1,2,'info7','FAILED', to_date('01.03.18 17:00:00','DD.MM.RR HH24:MI'),2);

我希望查询返回具有最高排名的pt_key和cp_key(复合主键的一部分,其他列未编制索引)的每种组合。如果存在(对于pt_key和cp_key的每个组合)几条记录具有相同的最高排名,则选择resp_dttm最大的记录。

select语句应仅返回前四列。

对于上面发布的样本数据,期望的结果将是:

1   1   info4   SENT
1   2   info7   FAILED 

感谢帮助。

2 个答案:

答案 0 :(得分:3)

这是使用row_number()的一种方法:

select *
from (
    select *, row_number() over (partition by pt_key, cp_key 
                                 order by rank desc, resp_dttm desc) rn
    from tab1
) t
where rn = 1

答案 1 :(得分:2)

这是使用FIRST聚合函数的另一种方法:

select pt_key,
       cp_key,
       max(ext_info) keep (dense_rank first order by t.rank desc, t.resp_dttm desc) as ext_info,
       max(resp_nm) keep (dense_rank first order by t.rank desc, t.resp_dttm desc) as resp_nm
from tab1 t
group by pt_key, cp_key

这是它在Oracle Live SQL上的工作方式

编辑2:

结果:

 PT_KEY | CP_KEY | EXT_INFO | RESP_NM
--------+--------+----------+---------
      1 |      1 | info4    | SENT
      1 |      2 | info7    | FAILED

编辑1:

此解决方案有一个重要的缺点,如果对于pt_keycp_key的特定组合,有多个行具有相同的rankresp_dttm值。在这种情况下,它将“合并”这些行,并计算ext_inforesp_nm的聚合(在我的示例中,它将取max的值)。

您可以通过添加第三级排序条件来改善该行为,以使排名与众不同(例如,从主键添加所有其他列)。

answer开始的@sgeddes在这个意义上要好一些,它将使用同等排名的行中的一个(随机)行,而无需合并数据,也不必添加排序条件。维护/更新也更容易,因为它在一个地方具有排名标准,而我的则在两个地方。

您可能应该在特定情况下(例如,特定索引,特定数据配置文件/统计信息)测试两者的性能。