我有一个大表(数百万条记录),我需要编写一个有效的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
感谢帮助。
答案 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_key
和cp_key
的特定组合,有多个行具有相同的rank
和resp_dttm
值。在这种情况下,它将“合并”这些行,并计算ext_info
和resp_nm
的聚合(在我的示例中,它将取max
的值)。
您可以通过添加第三级排序条件来改善该行为,以使排名与众不同(例如,从主键添加所有其他列)。
从answer开始的@sgeddes在这个意义上要好一些,它将使用同等排名的行中的一个(随机)行,而无需合并数据,也不必添加排序条件。维护/更新也更容易,因为它在一个地方具有排名标准,而我的则在两个地方。
您可能应该在特定情况下(例如,特定索引,特定数据配置文件/统计信息)测试两者的性能。